mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Prefixed intrinsic size value <!-- Please describe your changes on the following line: --> This is a PR for https://bugzilla.mozilla.org/show_bug.cgi?id=1355402 - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #16788 - [X] These changes do not require tests because it's for stylo <!-- 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/16962) <!-- Reviewable:end -->
1259 lines
46 KiB
Rust
1259 lines
46 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! [Length values][length].
|
|
//!
|
|
//! [length]: https://drafts.csswg.org/css-values/#lengths
|
|
|
|
use app_units::Au;
|
|
use cssparser::{Parser, Token};
|
|
use euclid::size::Size2D;
|
|
use font_metrics::FontMetricsQueryResult;
|
|
use parser::{Parse, ParserContext};
|
|
use std::{cmp, fmt, mem};
|
|
use std::ascii::AsciiExt;
|
|
use std::ops::Mul;
|
|
use style_traits::{HasViewportPercentage, ToCss};
|
|
use style_traits::values::specified::{AllowedLengthType, AllowedNumericType};
|
|
use stylesheets::CssRuleType;
|
|
use super::{AllowQuirks, Number, ToComputedValue};
|
|
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal};
|
|
use values::ExtremumLength;
|
|
use values::computed::{ComputedValueAsSpecified, Context};
|
|
use values::specified::calc::CalcNode;
|
|
|
|
pub use values::specified::calc::CalcLengthOrPercentage;
|
|
pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
|
|
pub use super::image::{GradientKind, Image};
|
|
|
|
/// Number of app units per pixel
|
|
pub const AU_PER_PX: CSSFloat = 60.;
|
|
/// Number of app units per inch
|
|
pub const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
|
|
/// Number of app units per centimeter
|
|
pub const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
|
|
/// Number of app units per millimeter
|
|
pub const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4;
|
|
/// Number of app units per quarter
|
|
pub const AU_PER_Q: CSSFloat = AU_PER_MM / 4.;
|
|
/// Number of app units per point
|
|
pub const AU_PER_PT: CSSFloat = AU_PER_IN / 72.;
|
|
/// Number of app units per pica
|
|
pub const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
|
|
|
|
/// Same as Gecko's AppUnitsToIntCSSPixels
|
|
///
|
|
/// Converts app units to integer pixel values,
|
|
/// rounding during the conversion
|
|
pub fn au_to_int_px(au: f32) -> i32 {
|
|
(au / AU_PER_PX).round() as i32
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
/// A font relative length.
|
|
pub enum FontRelativeLength {
|
|
/// A "em" value: https://drafts.csswg.org/css-values/#em
|
|
Em(CSSFloat),
|
|
/// A "ex" value: https://drafts.csswg.org/css-values/#ex
|
|
Ex(CSSFloat),
|
|
/// A "ch" value: https://drafts.csswg.org/css-values/#ch
|
|
Ch(CSSFloat),
|
|
/// A "rem" value: https://drafts.csswg.org/css-values/#rem
|
|
Rem(CSSFloat)
|
|
}
|
|
|
|
impl ToCss for FontRelativeLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
where W: fmt::Write
|
|
{
|
|
match *self {
|
|
FontRelativeLength::Em(length) => write!(dest, "{}em", length),
|
|
FontRelativeLength::Ex(length) => write!(dest, "{}ex", length),
|
|
FontRelativeLength::Ch(length) => write!(dest, "{}ch", length),
|
|
FontRelativeLength::Rem(length) => write!(dest, "{}rem", length)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A source to resolve font-relative units against
|
|
pub enum FontBaseSize {
|
|
/// Use the font-size of the current element
|
|
CurrentStyle,
|
|
/// Use the inherited font-size
|
|
InheritedStyle,
|
|
/// Use a custom base size
|
|
Custom(Au),
|
|
}
|
|
|
|
impl FontBaseSize {
|
|
/// Calculate the actual size for a given context
|
|
pub fn resolve(&self, context: &Context) -> Au {
|
|
match *self {
|
|
FontBaseSize::Custom(size) => size,
|
|
FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size(),
|
|
FontBaseSize::InheritedStyle => context.inherited_style().get_font().clone_font_size(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FontRelativeLength {
|
|
/// Computes the font-relative length. We use the base_size
|
|
/// flag to pass a different size for computing font-size and unconstrained font-size
|
|
pub fn to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> Au {
|
|
fn query_font_metrics(context: &Context, reference_font_size: Au) -> FontMetricsQueryResult {
|
|
context.font_metrics_provider.query(context.style().get_font(),
|
|
reference_font_size,
|
|
context.style().writing_mode,
|
|
context.in_media_query,
|
|
context.device)
|
|
}
|
|
|
|
let reference_font_size = base_size.resolve(context);
|
|
|
|
let root_font_size = context.style().root_font_size;
|
|
match *self {
|
|
FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
|
|
FontRelativeLength::Ex(length) => {
|
|
match query_font_metrics(context, reference_font_size) {
|
|
FontMetricsQueryResult::Available(metrics) => metrics.x_height.scale_by(length),
|
|
// https://drafts.csswg.org/css-values/#ex
|
|
//
|
|
// In the cases where it is impossible or impractical to
|
|
// determine the x-height, a value of 0.5em must be
|
|
// assumed.
|
|
//
|
|
FontMetricsQueryResult::NotAvailable => reference_font_size.scale_by(0.5 * length),
|
|
}
|
|
},
|
|
FontRelativeLength::Ch(length) => {
|
|
match query_font_metrics(context, reference_font_size) {
|
|
FontMetricsQueryResult::Available(metrics) => metrics.zero_advance_measure.scale_by(length),
|
|
// https://drafts.csswg.org/css-values/#ch
|
|
//
|
|
// In the cases where it is impossible or impractical to
|
|
// determine the measure of the “0” glyph, it must be
|
|
// assumed to be 0.5em wide by 1em tall. Thus, the ch
|
|
// unit falls back to 0.5em in the general case, and to
|
|
// 1em when it would be typeset upright (i.e.
|
|
// writing-mode is vertical-rl or vertical-lr and
|
|
// text-orientation is upright).
|
|
//
|
|
FontMetricsQueryResult::NotAvailable => {
|
|
if context.style().writing_mode.is_vertical() {
|
|
reference_font_size.scale_by(length)
|
|
} else {
|
|
reference_font_size.scale_by(0.5 * length)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FontRelativeLength::Rem(length) => root_font_size.scale_by(length)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
/// A viewport-relative length.
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#viewport-relative-lengths
|
|
pub enum ViewportPercentageLength {
|
|
/// A vw unit: https://drafts.csswg.org/css-values/#vw
|
|
Vw(CSSFloat),
|
|
/// A vh unit: https://drafts.csswg.org/css-values/#vh
|
|
Vh(CSSFloat),
|
|
/// https://drafts.csswg.org/css-values/#vmin
|
|
Vmin(CSSFloat),
|
|
/// https://drafts.csswg.org/css-values/#vmax
|
|
Vmax(CSSFloat)
|
|
}
|
|
|
|
impl HasViewportPercentage for ViewportPercentageLength {
|
|
fn has_viewport_percentage(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl ToCss for ViewportPercentageLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
ViewportPercentageLength::Vw(length) => write!(dest, "{}vw", length),
|
|
ViewportPercentageLength::Vh(length) => write!(dest, "{}vh", length),
|
|
ViewportPercentageLength::Vmin(length) => write!(dest, "{}vmin", length),
|
|
ViewportPercentageLength::Vmax(length) => write!(dest, "{}vmax", length)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ViewportPercentageLength {
|
|
/// Computes the given viewport-relative length for the given viewport size.
|
|
pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> Au {
|
|
macro_rules! to_unit {
|
|
($viewport_dimension:expr) => {
|
|
$viewport_dimension.to_f32_px() / 100.0
|
|
}
|
|
}
|
|
|
|
let value = match *self {
|
|
ViewportPercentageLength::Vw(length) =>
|
|
length * to_unit!(viewport_size.width),
|
|
ViewportPercentageLength::Vh(length) =>
|
|
length * to_unit!(viewport_size.height),
|
|
ViewportPercentageLength::Vmin(length) =>
|
|
length * to_unit!(cmp::min(viewport_size.width, viewport_size.height)),
|
|
ViewportPercentageLength::Vmax(length) =>
|
|
length * to_unit!(cmp::max(viewport_size.width, viewport_size.height)),
|
|
};
|
|
Au::from_f32_px(value)
|
|
}
|
|
}
|
|
|
|
/// HTML5 "character width", as defined in HTML5 § 14.5.4.
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct CharacterWidth(pub i32);
|
|
|
|
impl CharacterWidth {
|
|
/// Computes the given character width.
|
|
pub fn to_computed_value(&self, reference_font_size: Au) -> Au {
|
|
// This applies the *converting a character width to pixels* algorithm as specified
|
|
// in HTML5 § 14.5.4.
|
|
//
|
|
// TODO(pcwalton): Find these from the font.
|
|
let average_advance = reference_font_size.scale_by(0.5);
|
|
let max_advance = reference_font_size;
|
|
average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance
|
|
}
|
|
}
|
|
|
|
/// Same as Gecko
|
|
const ABSOLUTE_LENGTH_MAX: i32 = (1 << 30);
|
|
const ABSOLUTE_LENGTH_MIN: i32 = - (1 << 30);
|
|
|
|
/// Helper to convert a floating point length to application units
|
|
fn to_au_round(length: CSSFloat, au_per_unit: CSSFloat) -> Au {
|
|
Au(
|
|
(length * au_per_unit)
|
|
.min(ABSOLUTE_LENGTH_MAX as f32)
|
|
.max(ABSOLUTE_LENGTH_MIN as f32)
|
|
.round() as i32
|
|
)
|
|
}
|
|
|
|
/// Represents an absolute length with its unit
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub enum AbsoluteLength {
|
|
/// An absolute length in pixels (px)
|
|
Px(CSSFloat),
|
|
/// An absolute length in inches (in)
|
|
In(CSSFloat),
|
|
/// An absolute length in centimeters (cm)
|
|
Cm(CSSFloat),
|
|
/// An absolute length in millimeters (mm)
|
|
Mm(CSSFloat),
|
|
/// An absolute length in quarter-millimeters (q)
|
|
Q(CSSFloat),
|
|
/// An absolute length in points (pt)
|
|
Pt(CSSFloat),
|
|
/// An absolute length in pica (pc)
|
|
Pc(CSSFloat),
|
|
}
|
|
|
|
impl AbsoluteLength {
|
|
fn is_zero(&self) -> bool {
|
|
match *self {
|
|
AbsoluteLength::Px(v)
|
|
| AbsoluteLength::In(v)
|
|
| AbsoluteLength::Cm(v)
|
|
| AbsoluteLength::Mm(v)
|
|
| AbsoluteLength::Q(v)
|
|
| AbsoluteLength::Pt(v)
|
|
| AbsoluteLength::Pc(v) => v == 0.,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToComputedValue for AbsoluteLength {
|
|
type ComputedValue = Au;
|
|
|
|
fn to_computed_value(&self, _: &Context) -> Au {
|
|
Au::from(*self)
|
|
}
|
|
|
|
fn from_computed_value(computed: &Au) -> AbsoluteLength {
|
|
AbsoluteLength::Px(computed.to_f32_px())
|
|
}
|
|
}
|
|
|
|
impl From<AbsoluteLength> for Au {
|
|
fn from(length: AbsoluteLength) -> Au {
|
|
match length {
|
|
AbsoluteLength::Px(value) => to_au_round(value, AU_PER_PX),
|
|
AbsoluteLength::In(value) => to_au_round(value, AU_PER_IN),
|
|
AbsoluteLength::Cm(value) => to_au_round(value, AU_PER_CM),
|
|
AbsoluteLength::Mm(value) => to_au_round(value, AU_PER_MM),
|
|
AbsoluteLength::Q(value) => to_au_round(value, AU_PER_Q),
|
|
AbsoluteLength::Pt(value) => to_au_round(value, AU_PER_PT),
|
|
AbsoluteLength::Pc(value) => to_au_round(value, AU_PER_PC),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToCss for AbsoluteLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
AbsoluteLength::Px(length) => write!(dest, "{}px", length),
|
|
AbsoluteLength::In(length) => write!(dest, "{}in", length),
|
|
AbsoluteLength::Cm(length) => write!(dest, "{}cm", length),
|
|
AbsoluteLength::Mm(length) => write!(dest, "{}mm", length),
|
|
AbsoluteLength::Q(length) => write!(dest, "{}q", length),
|
|
AbsoluteLength::Pt(length) => write!(dest, "{}pt", length),
|
|
AbsoluteLength::Pc(length) => write!(dest, "{}pc", length),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<CSSFloat> for AbsoluteLength {
|
|
type Output = AbsoluteLength;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> AbsoluteLength {
|
|
match self {
|
|
AbsoluteLength::Px(v) => AbsoluteLength::Px(v * scalar),
|
|
AbsoluteLength::In(v) => AbsoluteLength::In(v * scalar),
|
|
AbsoluteLength::Cm(v) => AbsoluteLength::Cm(v * scalar),
|
|
AbsoluteLength::Mm(v) => AbsoluteLength::Mm(v * scalar),
|
|
AbsoluteLength::Q(v) => AbsoluteLength::Q(v * scalar),
|
|
AbsoluteLength::Pt(v) => AbsoluteLength::Pt(v * scalar),
|
|
AbsoluteLength::Pc(v) => AbsoluteLength::Pc(v * scalar),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Represents a physical length (mozmm) based on DPI
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg(feature = "gecko")]
|
|
pub struct PhysicalLength(pub CSSFloat);
|
|
|
|
#[cfg(feature = "gecko")]
|
|
impl PhysicalLength {
|
|
fn is_zero(&self) -> bool {
|
|
self.0 == 0.
|
|
}
|
|
|
|
/// Computes the given character width.
|
|
pub fn to_computed_value(&self, context: &Context) -> Au {
|
|
use gecko_bindings::bindings;
|
|
// Same as Gecko
|
|
const MM_PER_INCH: f32 = 25.4;
|
|
|
|
let physical_inch = unsafe {
|
|
let pres_context = &*context.device.pres_context;
|
|
bindings::Gecko_GetAppUnitsPerPhysicalInch(&pres_context)
|
|
};
|
|
|
|
let inch = self.0 / MM_PER_INCH;
|
|
|
|
to_au_round(inch, physical_inch as f32)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "gecko")]
|
|
impl ToCss for PhysicalLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
write!(dest, "{}mozmm", self.0)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "gecko")]
|
|
impl Mul<CSSFloat> for PhysicalLength {
|
|
type Output = PhysicalLength;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> PhysicalLength {
|
|
PhysicalLength(self.0 * scalar)
|
|
}
|
|
}
|
|
|
|
/// A `<length>` without taking `calc` expressions into account
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#lengths
|
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub enum NoCalcLength {
|
|
/// An absolute length
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#absolute-length
|
|
Absolute(AbsoluteLength),
|
|
|
|
/// A font-relative length:
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#font-relative-lengths
|
|
FontRelative(FontRelativeLength),
|
|
|
|
/// A viewport-relative length.
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#viewport-relative-lengths
|
|
ViewportPercentage(ViewportPercentageLength),
|
|
|
|
/// HTML5 "character width", as defined in HTML5 § 14.5.4.
|
|
///
|
|
/// This cannot be specified by the user directly and is only generated by
|
|
/// `Stylist::synthesize_rules_for_legacy_attributes()`.
|
|
ServoCharacterWidth(CharacterWidth),
|
|
|
|
/// A physical length (mozmm) based on DPI
|
|
#[cfg(feature = "gecko")]
|
|
Physical(PhysicalLength),
|
|
}
|
|
|
|
impl HasViewportPercentage for NoCalcLength {
|
|
fn has_viewport_percentage(&self) -> bool {
|
|
match *self {
|
|
NoCalcLength::ViewportPercentage(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToCss for NoCalcLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
NoCalcLength::Absolute(length) => length.to_css(dest),
|
|
NoCalcLength::FontRelative(length) => length.to_css(dest),
|
|
NoCalcLength::ViewportPercentage(length) => length.to_css(dest),
|
|
/* This should only be reached from style dumping code */
|
|
NoCalcLength::ServoCharacterWidth(CharacterWidth(i)) => write!(dest, "CharWidth({})", i),
|
|
#[cfg(feature = "gecko")]
|
|
NoCalcLength::Physical(length) => length.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<CSSFloat> for NoCalcLength {
|
|
type Output = NoCalcLength;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> NoCalcLength {
|
|
match self {
|
|
NoCalcLength::Absolute(v) => NoCalcLength::Absolute(v * scalar),
|
|
NoCalcLength::FontRelative(v) => NoCalcLength::FontRelative(v * scalar),
|
|
NoCalcLength::ViewportPercentage(v) => NoCalcLength::ViewportPercentage(v * scalar),
|
|
NoCalcLength::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
|
|
#[cfg(feature = "gecko")]
|
|
NoCalcLength::Physical(v) => NoCalcLength::Physical(v * scalar),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl NoCalcLength {
|
|
/// Parse a given absolute or relative dimension.
|
|
pub fn parse_dimension(context: &ParserContext, value: CSSFloat, unit: &str) -> Result<NoCalcLength, ()> {
|
|
let in_page_rule = context.rule_type.map_or(false, |rule_type| rule_type == CssRuleType::Page);
|
|
match_ignore_ascii_case! { unit,
|
|
"px" => Ok(NoCalcLength::Absolute(AbsoluteLength::Px(value))),
|
|
"in" => Ok(NoCalcLength::Absolute(AbsoluteLength::In(value))),
|
|
"cm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Cm(value))),
|
|
"mm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Mm(value))),
|
|
"q" => Ok(NoCalcLength::Absolute(AbsoluteLength::Q(value))),
|
|
"pt" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pt(value))),
|
|
"pc" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pc(value))),
|
|
// font-relative
|
|
"em" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Em(value))),
|
|
"ex" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ex(value))),
|
|
"ch" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ch(value))),
|
|
"rem" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Rem(value))),
|
|
// viewport percentages
|
|
"vw" => {
|
|
if in_page_rule {
|
|
return Err(())
|
|
}
|
|
Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value)))
|
|
},
|
|
"vh" => {
|
|
if in_page_rule {
|
|
return Err(())
|
|
}
|
|
Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value)))
|
|
},
|
|
"vmin" => {
|
|
if in_page_rule {
|
|
return Err(())
|
|
}
|
|
Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value)))
|
|
},
|
|
"vmax" => {
|
|
if in_page_rule {
|
|
return Err(())
|
|
}
|
|
Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value)))
|
|
},
|
|
#[cfg(feature = "gecko")]
|
|
"mozmm" => Ok(NoCalcLength::Physical(PhysicalLength(value))),
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
/// Returns a `zero` length.
|
|
pub fn zero() -> NoCalcLength {
|
|
NoCalcLength::Absolute(AbsoluteLength::Px(0.))
|
|
}
|
|
|
|
#[inline]
|
|
/// Checks whether the length value is zero.
|
|
pub fn is_zero(&self) -> bool {
|
|
match *self {
|
|
NoCalcLength::Absolute(length) => length.is_zero(),
|
|
#[cfg(feature = "gecko")]
|
|
NoCalcLength::Physical(length) => length.is_zero(),
|
|
_ => false
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
/// Returns a `medium` length.
|
|
pub fn medium() -> NoCalcLength {
|
|
NoCalcLength::Absolute(AbsoluteLength::Px(FONT_MEDIUM_PX as f32))
|
|
}
|
|
|
|
/// Get an absolute length from a px value.
|
|
#[inline]
|
|
pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
|
|
NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
|
|
}
|
|
}
|
|
|
|
/// An extension to `NoCalcLength` to parse `calc` expressions.
|
|
/// This is commonly used for the `<length>` values.
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#lengths
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub enum Length {
|
|
/// The internal length type that cannot parse `calc`
|
|
NoCalc(NoCalcLength),
|
|
/// A calc expression.
|
|
///
|
|
/// https://drafts.csswg.org/css-values/#calc-notation
|
|
Calc(Box<CalcLengthOrPercentage>),
|
|
}
|
|
|
|
impl From<NoCalcLength> for Length {
|
|
#[inline]
|
|
fn from(len: NoCalcLength) -> Self {
|
|
Length::NoCalc(len)
|
|
}
|
|
}
|
|
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<CSSFloat> for Length {
|
|
type Output = Length;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> Length {
|
|
match self {
|
|
Length::NoCalc(inner) => Length::NoCalc(inner * scalar),
|
|
Length::Calc(..) => panic!("Can't multiply Calc!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<CSSFloat> for FontRelativeLength {
|
|
type Output = FontRelativeLength;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> FontRelativeLength {
|
|
match self {
|
|
FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
|
|
FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
|
|
FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar),
|
|
FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<CSSFloat> for ViewportPercentageLength {
|
|
type Output = ViewportPercentageLength;
|
|
|
|
#[inline]
|
|
fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength {
|
|
match self {
|
|
ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar),
|
|
ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar),
|
|
ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
|
|
ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Length {
|
|
#[inline]
|
|
/// Returns a `zero` length.
|
|
pub fn zero() -> Length {
|
|
Length::NoCalc(NoCalcLength::zero())
|
|
}
|
|
|
|
/// Parse a given absolute or relative dimension.
|
|
pub fn parse_dimension(context: &ParserContext, value: CSSFloat, unit: &str) -> Result<Length, ()> {
|
|
NoCalcLength::parse_dimension(context, value, unit).map(Length::NoCalc)
|
|
}
|
|
|
|
#[inline]
|
|
fn parse_internal(context: &ParserContext,
|
|
input: &mut Parser,
|
|
num_context: AllowedLengthType,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Length, ()> {
|
|
match try!(input.next()) {
|
|
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
|
Length::parse_dimension(context, value.value, unit),
|
|
Token::Number(ref value) if num_context.is_ok(value.value) => {
|
|
if value.value != 0. &&
|
|
!context.parsing_mode.allows_unitless_lengths() &&
|
|
!allow_quirks.allowed(context.quirks_mode) {
|
|
return Err(())
|
|
}
|
|
Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(value.value))))
|
|
},
|
|
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
|
|
input.parse_nested_block(|input| {
|
|
CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
|
|
}),
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Parse a non-negative length
|
|
#[inline]
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Length, ()> {
|
|
Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
|
|
/// Parse a non-negative length, allowing quirks.
|
|
#[inline]
|
|
pub fn parse_non_negative_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Length, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::NonNegative, allow_quirks)
|
|
}
|
|
|
|
/// Get an absolute length from a px value.
|
|
#[inline]
|
|
pub fn from_px(px_value: CSSFloat) -> Length {
|
|
Length::NoCalc(NoCalcLength::from_px(px_value))
|
|
}
|
|
|
|
/// Extract inner length without a clone, replacing it with a 0 Au
|
|
///
|
|
/// Use when you need to move out of a length array without cloning
|
|
#[inline]
|
|
pub fn take(&mut self) -> Self {
|
|
mem::replace(self, Length::zero())
|
|
}
|
|
}
|
|
|
|
impl Parse for Length {
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
impl Length {
|
|
/// Parses a length, with quirks.
|
|
pub fn parse_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::All, allow_quirks)
|
|
}
|
|
}
|
|
|
|
impl<T: Parse> Either<Length, T> {
|
|
/// Parse a non-negative length
|
|
#[inline]
|
|
pub fn parse_non_negative_length(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
if let Ok(v) = input.try(|input| T::parse(context, input)) {
|
|
return Ok(Either::Second(v));
|
|
}
|
|
Length::parse_internal(context, input, AllowedLengthType::NonNegative, AllowQuirks::No).map(Either::First)
|
|
}
|
|
}
|
|
|
|
/// A percentage value.
|
|
///
|
|
/// [0 .. 100%] maps to [0.0 .. 1.0]
|
|
///
|
|
/// FIXME(emilio): There's no standard property that requires a `<percentage>`
|
|
/// without requiring also a `<length>`. If such a property existed, we'd need
|
|
/// to add special handling for `calc()` and percentages in here in the same way
|
|
/// as for `Angle` and `Time`, but the lack of this this is otherwise
|
|
/// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
|
|
///
|
|
/// As of today, only `-moz-image-rect` supports percentages without length.
|
|
/// This is not a regression, and that's a non-standard extension anyway, so I'm
|
|
/// not implementing it for now.
|
|
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct Percentage(pub CSSFloat);
|
|
|
|
impl ToCss for Percentage {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
where W: fmt::Write,
|
|
{
|
|
write!(dest, "{}%", self.0 * 100.)
|
|
}
|
|
}
|
|
|
|
impl Percentage {
|
|
/// Parse a specific kind of percentage.
|
|
pub fn parse_with_clamping_mode(input: &mut Parser,
|
|
context: AllowedNumericType)
|
|
-> Result<Self, ()> {
|
|
match try!(input.next()) {
|
|
Token::Percentage(ref value) if context.is_ok(value.unit_value) => {
|
|
Ok(Percentage(value.unit_value))
|
|
}
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Parses a percentage token, but rejects it if it's negative.
|
|
pub fn parse_non_negative(input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_with_clamping_mode(input, AllowedNumericType::NonNegative)
|
|
}
|
|
}
|
|
|
|
impl Parse for Percentage {
|
|
#[inline]
|
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_with_clamping_mode(input, AllowedNumericType::All)
|
|
}
|
|
}
|
|
|
|
impl ComputedValueAsSpecified for Percentage {}
|
|
|
|
/// A length or a percentage value.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
#[allow(missing_docs)]
|
|
pub enum LengthOrPercentage {
|
|
Length(NoCalcLength),
|
|
Percentage(Percentage),
|
|
Calc(Box<CalcLengthOrPercentage>),
|
|
}
|
|
|
|
impl From<Length> for LengthOrPercentage {
|
|
fn from(len: Length) -> LengthOrPercentage {
|
|
match len {
|
|
Length::NoCalc(l) => LengthOrPercentage::Length(l),
|
|
Length::Calc(l) => LengthOrPercentage::Calc(l),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<NoCalcLength> for LengthOrPercentage {
|
|
#[inline]
|
|
fn from(len: NoCalcLength) -> Self {
|
|
LengthOrPercentage::Length(len)
|
|
}
|
|
}
|
|
|
|
impl From<Percentage> for LengthOrPercentage {
|
|
#[inline]
|
|
fn from(pc: Percentage) -> Self {
|
|
LengthOrPercentage::Percentage(pc)
|
|
}
|
|
}
|
|
|
|
impl ToCss for LengthOrPercentage {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
LengthOrPercentage::Length(ref length) => length.to_css(dest),
|
|
LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest),
|
|
LengthOrPercentage::Calc(ref calc) => calc.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LengthOrPercentage {
|
|
#[inline]
|
|
/// Returns a `zero` length.
|
|
pub fn zero() -> LengthOrPercentage {
|
|
LengthOrPercentage::Length(NoCalcLength::zero())
|
|
}
|
|
|
|
fn parse_internal(context: &ParserContext,
|
|
input: &mut Parser,
|
|
num_context: AllowedLengthType,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<LengthOrPercentage, ()>
|
|
{
|
|
match try!(input.next()) {
|
|
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
|
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentage::Length),
|
|
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
|
Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))),
|
|
Token::Number(value) if num_context.is_ok(value.value) => {
|
|
if value.value != 0. &&
|
|
!context.parsing_mode.allows_unitless_lengths() &&
|
|
!allow_quirks.allowed(context.quirks_mode) {
|
|
return Err(())
|
|
}
|
|
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, num_context)
|
|
}));
|
|
Ok(LengthOrPercentage::Calc(Box::new(calc)))
|
|
},
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Parse a non-negative length.
|
|
#[inline]
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentage, ()> {
|
|
Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
|
|
/// Parse a non-negative length, with quirks.
|
|
#[inline]
|
|
pub fn parse_non_negative_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<LengthOrPercentage, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::NonNegative, allow_quirks)
|
|
}
|
|
|
|
/// Parse a length, treating dimensionless numbers as pixels
|
|
///
|
|
/// https://www.w3.org/TR/SVG2/types.html#presentation-attribute-css-value
|
|
pub fn parse_numbers_are_pixels(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentage, ()> {
|
|
if let Ok(lop) = input.try(|i| Self::parse(context, i)) {
|
|
return Ok(lop)
|
|
}
|
|
|
|
// TODO(emilio): Probably should use Number::parse_non_negative to
|
|
// handle calc()?
|
|
let num = input.expect_number()?;
|
|
Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(num))))
|
|
}
|
|
|
|
/// Parse a non-negative length, treating dimensionless numbers as pixels
|
|
///
|
|
/// This is nonstandard behavior used by Firefox for SVG
|
|
pub fn parse_numbers_are_pixels_non_negative(context: &ParserContext,
|
|
input: &mut Parser)
|
|
-> Result<LengthOrPercentage, ()> {
|
|
if let Ok(lop) = input.try(|i| Self::parse_non_negative(context, i)) {
|
|
return Ok(lop)
|
|
}
|
|
|
|
// TODO(emilio): Probably should use Number::parse_non_negative to
|
|
// handle calc()?
|
|
let num = input.expect_number()?;
|
|
if num >= 0. {
|
|
Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(num))))
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
|
|
/// Extract value from ref without a clone, replacing it with a 0 Au
|
|
///
|
|
/// Use when you need to move out of a length array without cloning
|
|
#[inline]
|
|
pub fn take(&mut self) -> Self {
|
|
mem::replace(self, LengthOrPercentage::zero())
|
|
}
|
|
}
|
|
|
|
impl Parse for LengthOrPercentage {
|
|
#[inline]
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
impl LengthOrPercentage {
|
|
/// Parses a length or a percentage, allowing the unitless length quirk.
|
|
/// https://quirks.spec.whatwg.org/#the-unitless-length-quirk
|
|
#[inline]
|
|
pub fn parse_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks) -> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::All, allow_quirks)
|
|
}
|
|
}
|
|
|
|
/// Either a `<length>`, a `<percentage>`, or the `auto` keyword.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
#[allow(missing_docs)]
|
|
pub enum LengthOrPercentageOrAuto {
|
|
Length(NoCalcLength),
|
|
Percentage(Percentage),
|
|
Auto,
|
|
Calc(Box<CalcLengthOrPercentage>),
|
|
}
|
|
|
|
impl From<NoCalcLength> for LengthOrPercentageOrAuto {
|
|
#[inline]
|
|
fn from(len: NoCalcLength) -> Self {
|
|
LengthOrPercentageOrAuto::Length(len)
|
|
}
|
|
}
|
|
|
|
impl From<Percentage> for LengthOrPercentageOrAuto {
|
|
#[inline]
|
|
fn from(pc: Percentage) -> Self {
|
|
LengthOrPercentageOrAuto::Percentage(pc)
|
|
}
|
|
}
|
|
|
|
impl ToCss for LengthOrPercentageOrAuto {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
LengthOrPercentageOrAuto::Length(ref length) => length.to_css(dest),
|
|
LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest),
|
|
LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
|
|
LengthOrPercentageOrAuto::Calc(ref calc) => calc.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LengthOrPercentageOrAuto {
|
|
fn parse_internal(context: &ParserContext,
|
|
input: &mut Parser,
|
|
num_context: AllowedLengthType,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Self, ()> {
|
|
match try!(input.next()) {
|
|
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
|
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrAuto::Length),
|
|
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
|
Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))),
|
|
Token::Number(ref value) if num_context.is_ok(value.value) => {
|
|
if value.value != 0. &&
|
|
!context.parsing_mode.allows_unitless_lengths() &&
|
|
!allow_quirks.allowed(context.quirks_mode) {
|
|
return Err(())
|
|
}
|
|
Ok(LengthOrPercentageOrAuto::Length(
|
|
NoCalcLength::Absolute(AbsoluteLength::Px(value.value))
|
|
))
|
|
}
|
|
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
|
|
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, num_context)
|
|
}));
|
|
Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
|
|
},
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Parse a non-negative length, percentage, or auto.
|
|
#[inline]
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
|
|
Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
|
|
/// Parse a non-negative length, percentage, or auto.
|
|
#[inline]
|
|
pub fn parse_non_negative_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::NonNegative, allow_quirks)
|
|
}
|
|
|
|
/// Returns the `auto` value.
|
|
pub fn auto() -> Self {
|
|
LengthOrPercentageOrAuto::Auto
|
|
}
|
|
|
|
/// Returns a value representing a `0` length.
|
|
pub fn zero() -> Self {
|
|
LengthOrPercentageOrAuto::Length(NoCalcLength::zero())
|
|
}
|
|
}
|
|
|
|
impl Parse for LengthOrPercentageOrAuto {
|
|
#[inline]
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
impl LengthOrPercentageOrAuto {
|
|
/// Parses, with quirks.
|
|
#[inline]
|
|
pub fn parse_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::All, allow_quirks)
|
|
}
|
|
}
|
|
|
|
/// Either a `<length>`, a `<percentage>`, or the `none` keyword.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
#[allow(missing_docs)]
|
|
pub enum LengthOrPercentageOrNone {
|
|
Length(NoCalcLength),
|
|
Percentage(Percentage),
|
|
Calc(Box<CalcLengthOrPercentage>),
|
|
None,
|
|
}
|
|
|
|
impl ToCss for LengthOrPercentageOrNone {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest),
|
|
LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest),
|
|
LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest),
|
|
LengthOrPercentageOrNone::None => dest.write_str("none"),
|
|
}
|
|
}
|
|
}
|
|
impl LengthOrPercentageOrNone {
|
|
fn parse_internal(context: &ParserContext,
|
|
input: &mut Parser,
|
|
num_context: AllowedLengthType,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<LengthOrPercentageOrNone, ()>
|
|
{
|
|
match try!(input.next()) {
|
|
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
|
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrNone::Length),
|
|
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
|
Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))),
|
|
Token::Number(value) if num_context.is_ok(value.value) => {
|
|
if value.value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
|
|
!allow_quirks.allowed(context.quirks_mode) {
|
|
return Err(())
|
|
}
|
|
Ok(LengthOrPercentageOrNone::Length(
|
|
NoCalcLength::Absolute(AbsoluteLength::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, num_context)
|
|
}));
|
|
Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
|
|
},
|
|
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
|
|
Ok(LengthOrPercentageOrNone::None),
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Parse a non-negative LengthOrPercentageOrNone.
|
|
#[inline]
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
|
|
/// Parse a non-negative LengthOrPercentageOrNone, with quirks.
|
|
#[inline]
|
|
pub fn parse_non_negative_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks)
|
|
-> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::NonNegative, allow_quirks)
|
|
}
|
|
}
|
|
|
|
impl Parse for LengthOrPercentageOrNone {
|
|
#[inline]
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
Self::parse_internal(context, input, AllowedLengthType::All, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
/// Either a `<length>` or the `none` keyword.
|
|
pub type LengthOrNone = Either<Length, None_>;
|
|
|
|
/// Either a `<length>` or the `normal` keyword.
|
|
pub type LengthOrNormal = Either<Length, Normal>;
|
|
|
|
/// Either a `<length>` or the `auto` keyword.
|
|
pub type LengthOrAuto = Either<Length, Auto>;
|
|
|
|
/// Either a `<length>` or a `<percentage>` or the `auto` keyword or the
|
|
/// `content` keyword.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub enum LengthOrPercentageOrAutoOrContent {
|
|
/// A `<length>`.
|
|
Length(NoCalcLength),
|
|
/// A percentage.
|
|
Percentage(Percentage),
|
|
/// A `calc` node.
|
|
Calc(Box<CalcLengthOrPercentage>),
|
|
/// The `auto` keyword.
|
|
Auto,
|
|
/// The `content` keyword.
|
|
Content
|
|
}
|
|
|
|
impl LengthOrPercentageOrAutoOrContent {
|
|
/// Parse a non-negative LengthOrPercentageOrAutoOrContent.
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
let num_context = AllowedLengthType::NonNegative;
|
|
match try!(input.next()) {
|
|
Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
|
|
NoCalcLength::parse_dimension(context, value.value, unit)
|
|
.map(LengthOrPercentageOrAutoOrContent::Length),
|
|
Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
|
|
Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))),
|
|
Token::Number(ref value) if value.value == 0. =>
|
|
Ok(Self::zero()),
|
|
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
|
|
Ok(LengthOrPercentageOrAutoOrContent::Auto),
|
|
Token::Ident(ref value) if value.eq_ignore_ascii_case("content") =>
|
|
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, num_context)
|
|
}));
|
|
Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc)))
|
|
},
|
|
_ => Err(())
|
|
}
|
|
}
|
|
|
|
/// Returns the `auto` value.
|
|
pub fn auto() -> Self {
|
|
LengthOrPercentageOrAutoOrContent::Auto
|
|
}
|
|
|
|
/// Returns a value representing a `0` length.
|
|
pub fn zero() -> Self {
|
|
LengthOrPercentageOrAutoOrContent::Length(NoCalcLength::zero())
|
|
}
|
|
}
|
|
|
|
impl ToCss for LengthOrPercentageOrAutoOrContent {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
LengthOrPercentageOrAutoOrContent::Length(ref len) => len.to_css(dest),
|
|
LengthOrPercentageOrAutoOrContent::Percentage(perc) => perc.to_css(dest),
|
|
LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"),
|
|
LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content"),
|
|
LengthOrPercentageOrAutoOrContent::Calc(ref calc) => calc.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Either a `<length>` or a `<number>`.
|
|
pub type LengthOrNumber = Either<Length, Number>;
|
|
|
|
impl LengthOrNumber {
|
|
/// Parse a non-negative LengthOrNumber.
|
|
pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
// We try to parse as a Number first because, for cases like
|
|
// LengthOrNumber, we want "0" to be parsed as a plain Number rather
|
|
// than a Length (0px); this matches the behaviour of all major browsers
|
|
if let Ok(v) = input.try(|i| Number::parse_non_negative(context, i)) {
|
|
return Ok(Either::Second(v))
|
|
}
|
|
|
|
Length::parse_non_negative(context, input).map(Either::First)
|
|
}
|
|
}
|
|
|
|
/// A value suitable for a `min-width` or `min-height` property.
|
|
/// Unlike `max-width` or `max-height` properties, a MozLength can be
|
|
/// `auto`, and cannot be `none`.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
#[allow(missing_docs)]
|
|
pub enum MozLength {
|
|
LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
|
|
ExtremumLength(ExtremumLength),
|
|
}
|
|
|
|
impl ToCss for MozLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
MozLength::LengthOrPercentageOrAuto(ref lopoa) =>
|
|
lopoa.to_css(dest),
|
|
MozLength::ExtremumLength(ref ext) =>
|
|
ext.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for MozLength {
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
MozLength::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
impl MozLength {
|
|
/// Parses, with quirks.
|
|
pub fn parse_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks) -> Result<Self, ()> {
|
|
input.try(ExtremumLength::parse).map(MozLength::ExtremumLength)
|
|
.or_else(|()| input.try(|i| LengthOrPercentageOrAuto::parse_non_negative_quirky(context, i, allow_quirks))
|
|
.map(MozLength::LengthOrPercentageOrAuto))
|
|
}
|
|
}
|
|
|
|
/// A value suitable for a `max-width` or `max-height` property.
|
|
#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
#[allow(missing_docs)]
|
|
pub enum MaxLength {
|
|
LengthOrPercentageOrNone(LengthOrPercentageOrNone),
|
|
ExtremumLength(ExtremumLength),
|
|
}
|
|
|
|
impl ToCss for MaxLength {
|
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
match *self {
|
|
MaxLength::LengthOrPercentageOrNone(ref lopon) =>
|
|
lopon.to_css(dest),
|
|
MaxLength::ExtremumLength(ref ext) =>
|
|
ext.to_css(dest),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for MaxLength {
|
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
|
MaxLength::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
impl MaxLength {
|
|
/// Parses, with quirks.
|
|
pub fn parse_quirky(context: &ParserContext,
|
|
input: &mut Parser,
|
|
allow_quirks: AllowQuirks) -> Result<Self, ()> {
|
|
input.try(ExtremumLength::parse).map(MaxLength::ExtremumLength)
|
|
.or_else(|()| input.try(|i| LengthOrPercentageOrNone::parse_non_negative_quirky(context, i, allow_quirks))
|
|
.map(MaxLength::LengthOrPercentageOrNone))
|
|
}
|
|
}
|