From 854c4801776a6366d13e485d8c82d481725d4179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 26 Oct 2019 14:17:28 +0000 Subject: [PATCH] style: Experiment with implementing zoom as a transform + transform-origin shorthand. This is a gross hack, of course, but has the advantage of not breaking sites that use both zoom and -moz-transform / -moz-transform-origin. There should be no behavior change when the pref is off, of course, and the webcompat team wanted to experiment with this. Differential Revision: https://phabricator.services.mozilla.com/D49792 --- .../properties/counted_unknown_properties.py | 1 - .../style/properties/properties.mako.rs | 11 +++- .../style/properties/shorthands/box.mako.rs | 55 +++++++++++++++++++ components/style/values/specified/image.rs | 5 +- components/style/values/specified/mod.rs | 46 +++++++++++----- .../style/values/specified/percentage.rs | 19 ++++++- .../style/values/specified/transform.rs | 23 +++++++- 7 files changed, 136 insertions(+), 24 deletions(-) diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py index 826bbf740a3..de36199e77a 100644 --- a/components/style/properties/counted_unknown_properties.py +++ b/components/style/properties/counted_unknown_properties.py @@ -13,7 +13,6 @@ # "offset" COUNTED_UNKNOWN_PROPERTIES = [ "-webkit-font-smoothing", - "zoom", "-webkit-tap-highlight-color", "speak", "text-size-adjust", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 24dfe1c57b6..a7af2844cae 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1876,7 +1876,16 @@ impl PropertyId { if let Some(id) = static_id(property_name) { return Ok(match *id { StaticId::Longhand(id) => PropertyId::Longhand(id), - StaticId::Shorthand(id) => PropertyId::Shorthand(id), + StaticId::Shorthand(id) => { + // We want to count `zoom` even if disabled. + if matches!(id, ShorthandId::Zoom) { + if let Some(counters) = use_counters { + counters.non_custom_properties.record(id.into()); + } + } + + PropertyId::Shorthand(id) + }, StaticId::LonghandAlias(id, alias) => PropertyId::LonghandAlias(id, alias), StaticId::ShorthandAlias(id, alias) => PropertyId::ShorthandAlias(id, alias), StaticId::CountedUnknown(unknown_prop) => { diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs index 84d2cb220bd..7b4ce131922 100644 --- a/components/style/properties/shorthands/box.mako.rs +++ b/components/style/properties/shorthands/box.mako.rs @@ -441,3 +441,58 @@ ${helpers.two_properties_shorthand( } } + +<%helpers:shorthand name="zoom" engines="gecko" + sub_properties="transform transform-origin" + gecko_pref="layout.css.zoom-transform-hack.enabled" + flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND" + spec="Not a standard, only a compat hack"> + use crate::parser::Parse; + use crate::values::specified::{Number, NumberOrPercentage, TransformOrigin}; + use crate::values::generics::transform::{Transform, TransformOperation}; + + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let zoom = match input.try(|input| NumberOrPercentage::parse(context, input)) { + Ok(number_or_percent) => number_or_percent.to_number(), + Err(..) => { + input.expect_ident_matching("normal")?; + Number::new(1.0) + }, + }; + + // Make sure that the initial value matches the values for the + // longhands, just for general sanity. + // + // FIXME: Should we just do this for the "normal" case? Seems weird + // either way, so maybe not? + Ok(if zoom.get() == 1.0 { + expanded! { + transform: Transform::none(), + transform_origin: TransformOrigin::initial_value(), + } + } else { + expanded! { + transform: Transform(vec![TransformOperation::Scale(zoom, zoom)].into()), + transform_origin: TransformOrigin::zero_zero(), + } + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + if self.transform.0.is_empty() && *self.transform_origin == TransformOrigin::initial_value() { + return 1.0f32.to_css(dest); + } + if *self.transform_origin != TransformOrigin::zero_zero() { + return Ok(()) + } + match &*self.transform.0 { + [TransformOperation::Scale(x, y)] if x == y => x.to_css(dest), + _ => Ok(()), + } + } + } + diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index c7a6ac1870c..7488e631e54 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -435,10 +435,7 @@ impl Gradient { let (color, mut p) = i.parse_nested_block(|i| { let p = match_ignore_ascii_case! { &function, "color-stop" => { - let p = match NumberOrPercentage::parse(context, i)? { - NumberOrPercentage::Number(number) => Percentage::new(number.value), - NumberOrPercentage::Percentage(p) => p, - }; + let p = NumberOrPercentage::parse(context, i)?.to_percentage(); i.expect_comma()?; p }, diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 160295c8f55..389464cd0c5 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -178,12 +178,22 @@ impl Parse for Number { } impl Number { + /// Returns a new number with the value `val`. + fn new_with_clamping_mode( + value: CSSFloat, + calc_clamping_mode: Option, + ) -> Self { + Self { value, calc_clamping_mode } + } + + /// Returns this percentage as a number. + pub fn to_percentage(&self) -> Percentage { + Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode) + } + /// Returns a new number with the value `val`. pub fn new(val: CSSFloat) -> Self { - Number { - value: val, - calc_clamping_mode: None, - } + Self::new_with_clamping_mode(val, None) } /// Returns whether this number came from a `calc()` expression. @@ -370,6 +380,22 @@ impl NumberOrPercentage { ) -> Result> { Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative) } + + /// Convert the number or the percentage to a number. + pub fn to_percentage(self) -> Percentage { + match self { + Self::Percentage(p) => p, + Self::Number(n) => n.to_percentage(), + } + } + + /// Convert the number or the percentage to a number. + pub fn to_number(self) -> Number { + match self { + Self::Percentage(p) => p.to_number(), + Self::Number(n) => n, + } + } } impl Parse for NumberOrPercentage { @@ -419,17 +445,7 @@ impl Parse for Opacity { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - let number = match NumberOrPercentage::parse(context, input)? { - NumberOrPercentage::Percentage(p) => Number { - value: p.get(), - calc_clamping_mode: if p.is_calc() { - Some(AllowedNumericType::All) - } else { - None - }, - }, - NumberOrPercentage::Number(n) => n, - }; + let number = NumberOrPercentage::parse(context, input)?.to_number(); Ok(Opacity(number)) } } diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs index acfa6db64af..64cb02bb424 100644 --- a/components/style/values/specified/percentage.rs +++ b/components/style/values/specified/percentage.rs @@ -8,6 +8,7 @@ use crate::parser::{Parse, ParserContext}; use crate::values::computed::percentage::Percentage as ComputedPercentage; use crate::values::computed::{Context, ToComputedValue}; use crate::values::specified::calc::CalcNode; +use crate::values::specified::Number; use crate::values::{serialize_percentage, CSSFloat}; use cssparser::{Parser, Token}; use std::fmt::{self, Write}; @@ -46,13 +47,21 @@ impl ToCss for Percentage { impl Percentage { /// Creates a percentage from a numeric value. - pub fn new(value: CSSFloat) -> Self { + pub(super) fn new_with_clamping_mode( + value: CSSFloat, + calc_clamping_mode: Option, + ) -> Self { Self { value, - calc_clamping_mode: None, + calc_clamping_mode, } } + /// Creates a percentage from a numeric value. + pub fn new(value: CSSFloat) -> Self { + Self::new_with_clamping_mode(value, None) + } + /// `0%` #[inline] pub fn zero() -> Self { @@ -70,12 +79,18 @@ impl Percentage { calc_clamping_mode: None, } } + /// Gets the underlying value for this float. pub fn get(&self) -> CSSFloat { self.calc_clamping_mode .map_or(self.value, |mode| mode.clamp(self.value)) } + /// Returns this percentage as a number. + pub fn to_number(&self) -> Number { + Number::new_with_clamping_mode(self.value, self.calc_clamping_mode) + } + /// Returns whether this percentage is a `calc()` value. pub fn is_calc(&self) -> bool { self.calc_clamping_mode.is_some() diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index 9d33fd4188f..157c6504856 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -33,6 +33,27 @@ pub type TransformOrigin = generic::TransformOrigin< Length, >; +impl TransformOrigin { + /// Returns the initial specified value for `transform-origin`. + #[inline] + pub fn initial_value() -> Self { + Self::new( + OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))), + OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))), + Length::zero(), + ) + } + + /// Returns the `0 0` value. + pub fn zero_zero() -> Self { + Self::new( + OriginComponent::Length(LengthPercentage::zero()), + OriginComponent::Length(LengthPercentage::zero()), + Length::zero(), + ) + } +} + impl Transform { /// Internal parse function for deciding if we wish to accept prefixed values or not /// @@ -260,7 +281,7 @@ impl Parse for TransformOrigin { let parse_depth = |input: &mut Parser| { input .try(|i| Length::parse(context, i)) - .unwrap_or(Length::from_px(0.)) + .unwrap_or(Length::zero()) }; match input.try(|i| OriginComponent::parse(context, i)) { Ok(x_origin @ OriginComponent::Center) => {