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 layout_debug;
use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto};
use model::{specified, specified_or_none};
use sequential;
use serde::{Serialize, Serializer};
use servo_geometry::max_rect;
@ -326,7 +325,7 @@ impl CandidateBSizeIterator {
MaybeAuto::Specified(block_container_block_size.scale_by(percent))
}
(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::Auto, _) => MaybeAuto::Auto,
@ -337,7 +336,7 @@ impl CandidateBSizeIterator {
Some(block_container_block_size.scale_by(percent))
}
(LengthOrPercentageOrNone::Calc(calc), _) => {
calc.to_computed(block_container_block_size)
calc.to_used_value(block_container_block_size)
}
(LengthOrPercentageOrNone::Percentage(_), None) |
(LengthOrPercentageOrNone::None, _) => None,
@ -348,7 +347,7 @@ impl CandidateBSizeIterator {
block_container_block_size.scale_by(percent)
}
(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::Length(length), _) => length,
@ -1167,7 +1166,7 @@ impl BlockFlow {
match (content_block_size, containing_block_size) {
(LengthOrPercentageOrAuto::Calc(calc), _) => {
calc.to_computed(containing_block_size)
calc.to_used_value(containing_block_size)
}
(LengthOrPercentageOrAuto::Length(length), _) => Some(length),
(LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
@ -1417,8 +1416,8 @@ impl BlockFlow {
// we know.
if kid.is_inline_flow() {
kid.as_mut_inline().first_line_indentation =
specified(self.fragment.style().get_inheritedtext().text_indent,
containing_block_size);
self.fragment.style().get_inheritedtext().text_indent
.to_used_value(containing_block_size);
}
}
}
@ -1512,14 +1511,12 @@ impl BlockFlow {
} else {
content_box.size.inline
} - self.fragment.margin.inline_start_end();
let max_inline_size = specified_or_none(
self.fragment.style().max_inline_size(),
self.base.block_container_inline_size
).unwrap_or(MAX_AU);
let min_inline_size = specified(
self.fragment.style().min_inline_size(),
self.base.block_container_inline_size
);
let max_inline_size =
self.fragment.style().max_inline_size()
.to_used_value(self.base.block_container_inline_size)
.unwrap_or(MAX_AU);
let min_inline_size =
self.fragment.style().min_inline_size().to_used_value(self.base.block_container_inline_size);
let specified_inline_size = self.fragment.style().content_inline_size();
let container_size = self.base.block_container_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
// be recalculated, but this time using the computed value of 'max-inline-size' as the
// computed value for 'inline-size'.
match specified_or_none(block.fragment().style().max_inline_size(),
containing_block_inline_size) {
match block.fragment().style().max_inline_size().to_used_value(containing_block_inline_size) {
Some(max_inline_size) if max_inline_size < solution.inline_size => {
input.computed_inline_size = MaybeAuto::Specified(max_inline_size);
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
// recalculated, but this time using the value of 'min-inline-size' as the computed value
// for 'inline-size'.
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
containing_block_inline_size);
let computed_min_inline_size =
block.fragment().style().min_inline_size().to_used_value(containing_block_inline_size);
if computed_min_inline_size > solution.inline_size {
input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size);
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 ipc_channel::ipc;
use list_item::ListItemFlow;
use model::{self, MaybeAuto, specified};
use model::{self, MaybeAuto};
use msg::constellation_msg::BrowsingContextId;
use net_traits::image::base::PixelFormat;
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 vert_position = *get_cyclic(&background.background_position_y.0, index);
// Use `background-position` to get the offset.
let horizontal_position = model::specified(horiz_position,
bounds.size.width - image_size.width);
let vertical_position = model::specified(vert_position,
bounds.size.height - image_size.height);
let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width);
let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height);
// The anchor position for this background, based on both the background-attachment
// and background-position properties.
@ -1185,8 +1183,8 @@ impl FragmentDisplayListBuilding for Fragment {
repeating: bool,
style: &ServoComputedValues)
-> display_list::RadialGradient {
let center = Point2D::new(specified(center.horizontal, bounds.size.width),
specified(center.vertical, bounds.size.height));
let center = Point2D::new(center.horizontal.to_used_value(bounds.size.width),
center.vertical.to_used_value(bounds.size.height));
let radius = match *shape {
GenericEndingShape::Circle(Circle::Radius(length)) => {
Size2D::new(length, length)
@ -1195,7 +1193,7 @@ impl FragmentDisplayListBuilding for Fragment {
convert_circle_size_keyword(extent, &bounds.size, &center)
},
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)) => {
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 layout_debug;
use model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
use model::{specified, specified_or_none};
use std::cmp::{max, min};
use std::ops::Range;
use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content};
@ -52,7 +51,7 @@ impl AxisSize {
}
}
LengthOrPercentageOrAuto::Calc(calc) => {
match calc.to_computed(content_size) {
match calc.to_used_value(content_size) {
Some(length) => AxisSize::Definite(length),
None => AxisSize::Infinite,
}
@ -79,7 +78,7 @@ fn from_flex_basis(flex_basis: LengthOrPercentageOrAutoOrContent,
(LengthOrPercentageOrAutoOrContent::Percentage(_), None) =>
MaybeAuto::Auto,
(LengthOrPercentageOrAutoOrContent::Calc(calc), _) =>
MaybeAuto::from_option(calc.to_computed(containing_length)),
MaybeAuto::from_option(calc.to_used_value(containing_length)),
(LengthOrPercentageOrAutoOrContent::Content, _) =>
MaybeAuto::Auto,
(LengthOrPercentageOrAutoOrContent::Auto, Some(size)) =>
@ -169,10 +168,11 @@ impl FlexItem {
- margin
+ block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(block.fragment.style.max_inline_size(),
containing_length).unwrap_or(MAX_AU);
self.min_size = specified(block.fragment.style.min_inline_size(),
containing_length);
self.max_size =
block.fragment.style.max_inline_size()
.to_used_value(containing_length)
.unwrap_or(MAX_AU);
self.min_size = block.fragment.style.min_inline_size().to_used_value(containing_length);
}
Direction::Block => {
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.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(block.fragment.style.max_block_size(),
containing_length).unwrap_or(MAX_AU);
self.min_size = specified(block.fragment.style.min_block_size(),
containing_length);
self.max_size =
block.fragment.style.max_block_size()
.to_used_value(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.
let padding = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_PADDING) {
let padding = style.logical_padding();
(model::specified(padding.inline_start, Au(0)) +
model::specified(padding.inline_end, Au(0)))
(padding.inline_start.to_used_value(Au(0)) +
padding.inline_end.to_used_value(Au(0)))
} else {
Au(0)
};
@ -935,8 +935,8 @@ impl Fragment {
if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
specified = MaybeAuto::from_style(style.content_inline_size(),
Au(0)).specified_or_zero();
specified = max(model::specified(style.min_inline_size(), Au(0)), specified);
if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) {
specified = max(style.min_inline_size().to_used_value(Au(0)), specified);
if let Some(max) = style.max_inline_size().to_used_value(Au(0)) {
specified = min(specified, max)
}
@ -1159,10 +1159,10 @@ impl Fragment {
let border_width = self.border_width();
SpeculatedInlineContentEdgeOffsets {
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,
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,
}
}
@ -1491,10 +1491,10 @@ impl Fragment {
// 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));
self.border_padding.inline_start = padding.inline_start.to_used_value(Au(0));
self.border_padding.inline_end = padding.inline_end.to_used_value(Au(0));
self.border_padding.block_start = padding.block_start.to_used_value(Au(0));
self.border_padding.block_end = padding.block_end.to_used_value(Au(0));
let border = self.border_width();
self.border_padding.inline_start += border.inline_start;
self.border_padding.inline_end += border.inline_end;
@ -2847,12 +2847,14 @@ impl Fragment {
let mut transform = Matrix4D::identity();
let transform_origin = &self.style.get_box().transform_origin;
let transform_origin_x = model::specified(transform_origin.horizontal,
stacking_relative_border_box.size
.width).to_f32_px();
let transform_origin_y = model::specified(transform_origin.vertical,
stacking_relative_border_box.size
.height).to_f32_px();
let transform_origin_x =
transform_origin.horizontal
.to_used_value(stacking_relative_border_box.size.width)
.to_f32_px();
let transform_origin_y =
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 pre_transform = Matrix4D::create_translation(transform_origin_x,
@ -2875,10 +2877,8 @@ impl Fragment {
Matrix4D::create_scale(sx, sy, sz)
}
transform::ComputedOperation::Translate(tx, ty, tz) => {
let tx =
model::specified(tx, stacking_relative_border_box.size.width).to_f32_px();
let ty =
model::specified(ty, stacking_relative_border_box.size.height).to_f32_px();
let tx = tx.to_used_value(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 tz = tz.to_f32_px();
Matrix4D::create_translation(tx, ty, tz)
}
@ -2907,10 +2907,13 @@ impl Fragment {
Either::First(length) => {
let perspective_origin = self.style().get_box().perspective_origin;
let perspective_origin =
Point2D::new(model::specified(perspective_origin.horizontal,
stacking_relative_border_box.size.width).to_f32_px(),
model::specified(perspective_origin.vertical,
stacking_relative_border_box.size.height).to_f32_px());
Point2D::new(
perspective_origin.horizontal
.to_used_value(stacking_relative_border_box.size.width)
.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,
perspective_origin.y,

View file

@ -412,7 +412,7 @@ impl MaybeAuto {
MaybeAuto::Specified(containing_length.scale_by(percent))
}
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)
}
@ -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.
///
/// Note that percentages in `border-radius` are resolved against the relevant
@ -495,8 +477,8 @@ pub fn specified_border_radius(
-> Size2D<Au>
{
let generics::BorderRadiusSize(size) = radius;
let w = specified(size.width, containing_size.width);
let h = specified(size.height, containing_size.height);
let w = size.width.to_used_value(containing_size.width);
let h = size.height.to_used_value(containing_size.height);
Size2D::new(w, h)
}
@ -507,10 +489,10 @@ pub fn padding_from_style(style: &ServoComputedValues,
-> LogicalMargin<Au> {
let padding_style = style.get_padding();
LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
specified(padding_style.padding_top, containing_block_inline_size),
specified(padding_style.padding_right, containing_block_inline_size),
specified(padding_style.padding_bottom, containing_block_inline_size),
specified(padding_style.padding_left, containing_block_inline_size)))
padding_style.padding_top.to_used_value(containing_block_inline_size),
padding_style.padding_right.to_used_value(containing_block_inline_size),
padding_style.padding_bottom.to_used_value(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
@ -559,7 +541,7 @@ impl SizeConstraint {
max_size: LengthOrPercentageOrNone,
border: Option<Au>) -> SizeConstraint {
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 {
length
} else {
@ -568,7 +550,7 @@ impl SizeConstraint {
};
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 {
Some(length)
} else {

View file

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

View file

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

View file

@ -8,6 +8,7 @@ use app_units::{Au, AU_PER_PX};
use ordered_float::NotNaN;
use std::fmt;
use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
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 {
match *self {
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))]
#[allow(missing_docs)]
pub struct CalcLengthOrPercentage {
pub length: Au,
pub clamping_mode: AllowedLengthType,
length: Au,
pub percentage: Option<CSSFloat>,
}
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]
#[allow(missing_docs)]
pub fn length(&self) -> Au {
self.length
self.clamping_mode.clamp(self.length)
}
#[inline]
@ -81,10 +102,12 @@ impl CalcLengthOrPercentage {
/// If there are special rules for computing percentages in a value (e.g. the height property),
/// 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) {
(Some(len), Some(percent)) => Some(self.length + len.scale_by(percent)),
(_, None) => Some(self.length),
(Some(len), Some(percent)) => {
Some(self.clamping_mode.clamp(self.length + len.scale_by(percent)))
},
(_, None) => Some(self.length()),
_ => None,
}
}
@ -94,16 +117,10 @@ impl From<LengthOrPercentage> for CalcLengthOrPercentage {
fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
match len {
LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage {
length: Au(0),
percentage: Some(this),
}
CalcLengthOrPercentage::new(Au(0), Some(this))
}
LengthOrPercentage::Length(this) => {
CalcLengthOrPercentage {
length: this,
percentage: None,
}
CalcLengthOrPercentage::new(this, None)
}
LengthOrPercentage::Calc(this) => {
this
@ -116,16 +133,10 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage {
length: Au(0),
percentage: Some(this),
})
Some(CalcLengthOrPercentage::new(Au(0), Some(this)))
}
LengthOrPercentageOrAuto::Length(this) => {
Some(CalcLengthOrPercentage {
length: this,
percentage: None,
})
Some(CalcLengthOrPercentage::new(this, None))
}
LengthOrPercentageOrAuto::Calc(this) => {
Some(this)
@ -176,6 +187,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
}
CalcLengthOrPercentage {
clamping_mode: self.clamping_mode,
length: length,
percentage: self.percentage,
}
@ -184,6 +196,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
#[inline]
fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
specified::CalcLengthOrPercentage {
clamping_mode: computed.clamping_mode,
absolute: Some(computed.length),
percentage: computed.percentage,
..Default::default()
@ -235,6 +248,17 @@ impl LengthOrPercentage {
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 {
@ -481,6 +505,18 @@ pub enum LengthOrPercentageOrNone {
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {

View file

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

View file

@ -539,7 +539,7 @@ pub enum Length {
/// A calc expression.
///
/// https://drafts.csswg.org/css-values/#calc-notation
Calc(AllowedLengthType, Box<CalcLengthOrPercentage>),
Calc(Box<CalcLengthOrPercentage>),
}
impl From<NoCalcLength> for Length {
@ -553,7 +553,7 @@ impl HasViewportPercentage for Length {
fn has_viewport_percentage(&self) -> bool {
match *self {
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 {
match *self {
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") =>
input.parse_nested_block(|input| {
CalcNode::parse_length(context, input)
.map(|calc| {
Length::Calc(num_context, Box::new(calc))
})
CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
}),
_ => Err(())
}
@ -770,7 +767,7 @@ impl From<Length> for LengthOrPercentage {
fn from(len: Length) -> LengthOrPercentage {
match len {
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))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
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)))
},
@ -986,7 +983,7 @@ impl LengthOrPercentageOrAuto {
Ok(LengthOrPercentageOrAuto::Auto),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
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)))
},
@ -1092,7 +1089,7 @@ impl LengthOrPercentageOrNone {
}
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
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)))
},
@ -1169,7 +1166,7 @@ impl LengthOrPercentageOrAutoOrContent {
Ok(LengthOrPercentageOrAutoOrContent::Content),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
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)))
},

View file

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

View file

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

View file

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