Bug 1339711 - Part 3: stylo: Support mathsize, width, and scriptminsize presentation attributes in MathML; r?emilio draft
authorManish Goregaokar <manishearth@gmail.com>
Mon, 10 Apr 2017 15:28:48 +0800
changeset 561224 545ae1970c22132e5ebf7260cb28997c2bd318c3
parent 561223 9ab135c9da1843387fe976d616747d1cd42af379
child 561225 39a5c950a11624f5c131408817fe6709171dab81
push id53654
push userbmo:manishearth@gmail.com
push dateWed, 12 Apr 2017 09:22:24 +0000
reviewersemilio
bugs1339711
milestone55.0a1
Bug 1339711 - Part 3: stylo: Support mathsize, width, and scriptminsize presentation attributes in MathML; r?emilio MozReview-Commit-ID: GvHslYdBfXU
dom/mathml/nsMathMLElement.cpp
layout/style/GenericSpecifiedValues.h
layout/style/GenericSpecifiedValuesInlines.h
layout/style/ServoBindingList.h
layout/style/ServoSpecifiedValues.cpp
layout/style/ServoSpecifiedValues.h
layout/style/nsRuleData.h
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/values/specified/length.rs
servo/ports/geckolib/glue.rs
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -483,25 +483,18 @@ nsMathMLElement::ParseNumericValue(const
   }
 
   aCSSValue.SetFloatValue(floatValue, cssUnit);
   return true;
 }
 
 void
 nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
-                                         GenericSpecifiedValues* aGenericData)
+                                         GenericSpecifiedValues* aData)
 {
-  if (aGenericData->IsServo()) {
-    // FIXME (bug 1339711) handle MathML properties in Stylo
-    NS_WARNING("stylo: cannot handle MathML presentation attributes");
-    return;
-  }
-
-  nsRuleData* aData = aGenericData->AsGecko();
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
     // scriptsizemultiplier
     //
     // "Specifies the multiplier to be used to adjust font size due to changes
     // in scriptlevel.
     //
     // values: number
     // default: 0.71
@@ -534,27 +527,30 @@ nsMathMLElement::MapMathMLAttributesInto
     //
     // values: length
     // default: 8pt
     //
     // We don't allow negative values.
     // Unitless and percent values give a multiple of the default value.
     //
     value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
-    nsCSSValue* scriptMinSize = aData->ValueForScriptMinSize();
     if (value && value->Type() == nsAttrValue::eString &&
-        scriptMinSize->GetUnit() == eCSSUnit_Null) {
-      ParseNumericValue(value->GetStringValue(), *scriptMinSize,
+        !aData->PropertyIsSet(eCSSProperty__moz_script_min_size)) {
+      nsCSSValue scriptMinSize;
+      ParseNumericValue(value->GetStringValue(), scriptMinSize,
                         PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
                         aData->mPresContext->Document());
 
-      if (scriptMinSize->GetUnit() == eCSSUnit_Percent) {
-        scriptMinSize->SetFloatValue(8.0 * scriptMinSize->GetPercentValue(),
+      if (scriptMinSize.GetUnit() == eCSSUnit_Percent) {
+        scriptMinSize.SetFloatValue(8.0 * scriptMinSize.GetPercentValue(),
                                      eCSSUnit_Point);
       }
+      if (scriptMinSize.GetUnit() != eCSSUnit_Null) {
+        aData->SetLengthValue(eCSSProperty__moz_script_min_size, scriptMinSize);
+      }
     }
 
     // scriptlevel
     // 
     // "Changes the scriptlevel in effect for the children. When the value is
     // given without a sign, it sets scriptlevel to the specified value; when a
     // sign is given, it increments ("+") or decrements ("-") the current
     // value. (Note that large decrements can result in negative values of
@@ -617,36 +613,41 @@ nsMathMLElement::MapMathMLAttributesInto
       parseSizeKeywords = false;
       value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
       if (value) {
         WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(),
                        nsGkAtoms::mathsize_->GetUTF16String(),
                        aData->mPresContext->Document());
       }
     }
-    nsCSSValue* fontSize = aData->ValueForFontSize();
     if (value && value->Type() == nsAttrValue::eString &&
-        fontSize->GetUnit() == eCSSUnit_Null) {
+        !aData->PropertyIsSet(eCSSProperty_font_size)) {
       nsAutoString str(value->GetStringValue());
-      if (!ParseNumericValue(str, *fontSize, PARSE_SUPPRESS_WARNINGS |
+      nsCSSValue fontSize;
+      if (!ParseNumericValue(str, fontSize, PARSE_SUPPRESS_WARNINGS |
                              PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
                              nullptr)
           && parseSizeKeywords) {
         static const char sizes[3][7] = { "small", "normal", "big" };
         static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
           NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM,
           NS_STYLE_FONT_SIZE_LARGE
         };
         str.CompressWhitespace();
         for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
           if (str.EqualsASCII(sizes[i])) {
-            fontSize->SetIntValue(values[i], eCSSUnit_Enumerated);
+            aData->SetKeywordValue(eCSSProperty_font_size, values[i]);
             break;
           }
         }
+      } else if (fontSize.GetUnit() == eCSSUnit_Percent) {
+        aData->SetPercentValue(eCSSProperty_font_size,
+                               fontSize.GetPercentValue());
+      } else if (fontSize.GetUnit() != eCSSUnit_Null) {
+        aData->SetLengthValue(eCSSProperty_font_size, fontSize);
       }
     }
 
     // fontfamily
     //
     // "Should be the name of a font that may be available to a MathML renderer,
     // or a CSS font specification; See Section 6.5 Using CSS with MathML and
     // CSS for more information. Deprecated in favor of mathvariant."
@@ -844,23 +845,29 @@ nsMathMLElement::MapMathMLAttributesInto
     // relative to the horizontal space a MathML renderer has available for the
     // math element. When the value is "auto", the MathML renderer should
     // calculate the table width from its contents using whatever layout
     // algorithm it chooses. "
     //
     // values: "auto" | length
     // default: auto
     //
-    nsCSSValue* width = aData->ValueForWidth();
-    if (width->GetUnit() == eCSSUnit_Null) {
+    if (!aData->PropertyIsSet(eCSSProperty_width)) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
+      nsCSSValue width;
       // This does not handle auto and unitless values
       if (value && value->Type() == nsAttrValue::eString) {
-        ParseNumericValue(value->GetStringValue(), *width, 0,
+        ParseNumericValue(value->GetStringValue(), width, 0,
                           aData->mPresContext->Document());
+        if (width.GetUnit() == eCSSUnit_Percent) {
+          aData->SetPercentValue(eCSSProperty_width,
+                                 width.GetPercentValue());
+        } else if (width.GetUnit() != eCSSUnit_Null) {
+          aData->SetLengthValue(eCSSProperty_width, width);
+        }
       }
     }
   }
 
   // dir
   //
   // Overall Directionality of Mathematics Formulas:
   // "The overall directionality for a formula, basically the direction of the
--- a/layout/style/GenericSpecifiedValues.h
+++ b/layout/style/GenericSpecifiedValues.h
@@ -73,16 +73,18 @@ public:
     }
 
     // Set a property to an integer value
     inline void SetIntValue(nsCSSPropertyID aId, int32_t aValue);
     // Set a property to a pixel value
     inline void SetPixelValue(nsCSSPropertyID aId, float aValue);
     inline void SetPixelValueIfUnset(nsCSSPropertyID aId, float aValue);
 
+    inline void SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue);
+
     // Set a property to a number value
     inline void SetNumberValue(nsCSSPropertyID aId, float aValue);
 
     // Set a property to a percent value
     inline void SetPercentValue(nsCSSPropertyID aId, float aValue);
     inline void SetPercentValueIfUnset(nsCSSPropertyID aId, float aValue);
 
     // Set a property to `auto`
--- a/layout/style/GenericSpecifiedValuesInlines.h
+++ b/layout/style/GenericSpecifiedValuesInlines.h
@@ -94,16 +94,22 @@ GenericSpecifiedValues::SetPixelValue(ns
 
 void
 GenericSpecifiedValues::SetPixelValueIfUnset(nsCSSPropertyID aId, float aValue)
 {
   MOZ_STYLO_FORWARD(SetPixelValueIfUnset, (aId, aValue))
 }
 
 void
+GenericSpecifiedValues::SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue)
+{
+  MOZ_STYLO_FORWARD(SetLengthValue, (aId, aValue))
+}
+
+void
 GenericSpecifiedValues::SetNumberValue(nsCSSPropertyID aId, float aValue)
 {
   MOZ_STYLO_FORWARD(SetNumberValue, (aId, aValue))
 }
 
 void
 GenericSpecifiedValues::SetPercentValue(nsCSSPropertyID aId, float aValue)
 {
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -235,16 +235,21 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetIntValue, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
                    int32_t value)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetPixelValue, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
                    float value)
+SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetLengthValue, void,
+                   RawServoDeclarationBlockBorrowed declarations,
+                   nsCSSPropertyID property,
+                   float value,
+                   nsCSSUnit unit)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetNumberValue, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
                    float value)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetPercentValue, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
                    float value)
--- a/layout/style/ServoSpecifiedValues.cpp
+++ b/layout/style/ServoSpecifiedValues.cpp
@@ -64,16 +64,23 @@ ServoSpecifiedValues::SetIntValue(nsCSSP
 
 void
 ServoSpecifiedValues::SetPixelValue(nsCSSPropertyID aId, float aValue)
 {
   Servo_DeclarationBlock_SetPixelValue(mDecl, aId, aValue);
 }
 
 void
+ServoSpecifiedValues::SetLengthValue(nsCSSPropertyID aId, nsCSSValue aValue)
+{
+  MOZ_ASSERT(aValue.IsLengthUnit());
+  Servo_DeclarationBlock_SetLengthValue(mDecl, aId, aValue.GetFloatValue(), aValue.GetUnit());
+}
+
+void
 ServoSpecifiedValues::SetNumberValue(nsCSSPropertyID aId, float aValue)
 {
   Servo_DeclarationBlock_SetNumberValue(mDecl, aId, aValue);
 }
 
 void
 ServoSpecifiedValues::SetPercentValue(nsCSSPropertyID aId, float aValue)
 {
--- a/layout/style/ServoSpecifiedValues.h
+++ b/layout/style/ServoSpecifiedValues.h
@@ -55,16 +55,19 @@ public:
 
   void SetPixelValueIfUnset(nsCSSPropertyID aId,
                             float aValue) {
     if (!PropertyIsSet(aId)) {
       SetPixelValue(aId, aValue);
     }
   }
 
+  void SetLengthValue(nsCSSPropertyID aId,
+                      nsCSSValue aValue);
+
   void SetNumberValue(nsCSSPropertyID aId,
                      float aValue);
 
   void SetPercentValue(nsCSSPropertyID aId,
                        float aValue);
 
   void SetAutoValue(nsCSSPropertyID aId);
 
--- a/layout/style/nsRuleData.h
+++ b/layout/style/nsRuleData.h
@@ -161,16 +161,22 @@ struct nsRuleData final: mozilla::Generi
 
   void SetPixelValueIfUnset(nsCSSPropertyID aId,
                             float aValue) {
     if (!PropertyIsSet(aId)) {
       SetPixelValue(aId, aValue);
     }
   }
 
+  void SetLengthValue(nsCSSPropertyID aId,
+                      nsCSSValue aValue) {
+    nsCSSValue* val = ValueFor(aId);
+    *val = aValue;
+  }
+
   void SetNumberValue(nsCSSPropertyID aId,
                      float aValue) {
     ValueFor(aId)->SetFloatValue(aValue, eCSSUnit_Number);
   }
 
   void SetPercentValue(nsCSSPropertyID aId,
                        float aValue) {
     ValueFor(aId)->SetPercentValue(aValue);
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -1846,16 +1846,22 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_SetPixelValue(declarations:
                                                     RawServoDeclarationBlockBorrowed,
                                                 property: nsCSSPropertyID,
                                                 value: f32);
 }
 extern "C" {
+    pub fn Servo_DeclarationBlock_SetLengthValue(declarations:
+                                                     RawServoDeclarationBlockBorrowed,
+                                                 property: nsCSSPropertyID,
+                                                 value: f32, unit: nsCSSUnit);
+}
+extern "C" {
     pub fn Servo_DeclarationBlock_SetNumberValue(declarations:
                                                      RawServoDeclarationBlockBorrowed,
                                                  property: nsCSSPropertyID,
                                                  value: f32);
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_SetPercentValue(declarations:
                                                       RawServoDeclarationBlockBorrowed,
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -447,16 +447,22 @@
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Length(specified::LengthOrPercentage),
         Keyword(KeywordSize),
         Smaller,
         Larger,
     }
 
+    impl From<specified::LengthOrPercentage> for SpecifiedValue {
+        fn from(other: specified::LengthOrPercentage) -> Self {
+            SpecifiedValue::Length(other)
+        }
+    }
+
     pub mod computed_value {
         use app_units::Au;
         pub type T = Au;
     }
 
     /// CSS font keywords
     #[derive(Debug, Copy, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -1256,8 +1262,36 @@
                             bold-sans-serif sans-serif-italic sans-serif-bold-italic
                             monospace initial tailed looped stretched""",
                          gecko_constant_prefix="NS_MATHML_MATHVARIANT",
                          gecko_ffi_name="mMathVariant",
                          products="gecko",
                          spec="Internal (not web-exposed)",
                          animation_type="none",
                          needs_conversion=True)}
+
+<%helpers:longhand name="-moz-script-min-size" products="gecko" animation_type="none"
+                   predefined_type="Length" gecko_ffi_name="mScriptMinSize"
+                   spec="Internal (not web-exposed)"
+                   internal="True" disable_when_testing="True">
+    use app_units::Au;
+    use gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT;
+    use values::HasViewportPercentage;
+    use values::computed::ComputedValueAsSpecified;
+    use values::specified::length::{AU_PER_PT, Length};
+
+    pub type SpecifiedValue = Length;
+
+    pub mod computed_value {
+        pub type T = super::Au;
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        Au((NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * AU_PER_PT) as i32)
+    }
+
+    pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        debug_assert!(false, "Should be set directly by presentation attributes only.");
+        Err(())
+    }
+</%helpers:longhand>
+
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -20,23 +20,30 @@ use super::{Angle, Number, SimplifiedVal
 use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
 use values::ExtremumLength;
 use values::computed::{ComputedValueAsSpecified, Context};
 
 pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use super::image::{SizeKeyword, VerticalDirection};
 
-const AU_PER_PX: CSSFloat = 60.;
-const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
-const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
-const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4;
-const AU_PER_Q: CSSFloat = AU_PER_MM / 4.;
-const AU_PER_PT: CSSFloat = AU_PER_IN / 72.;
-const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
+/// 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
 }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1362,16 +1362,51 @@ pub extern "C" fn Servo_DeclarationBlock
             }
         ),
     };
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(prop, Importance::Normal);
     })
 }
 
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(declarations:
+                                                        RawServoDeclarationBlockBorrowed,
+                                                        property: nsCSSPropertyID,
+                                                        value: f32,
+                                                        unit: structs::nsCSSUnit) {
+    use style::properties::{PropertyDeclaration, LonghandId};
+    use style::values::specified::length::{AbsoluteLength, FontRelativeLength};
+    use style::values::specified::length::{LengthOrPercentage, NoCalcLength};
+
+    let long = get_longhand_from_id!(property);
+    let nocalc = match unit {
+        structs::nsCSSUnit::eCSSUnit_EM => NoCalcLength::FontRelative(FontRelativeLength::Em(value)),
+        structs::nsCSSUnit::eCSSUnit_XHeight => NoCalcLength::FontRelative(FontRelativeLength::Ex(value)),
+        structs::nsCSSUnit::eCSSUnit_Pixel => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
+        structs::nsCSSUnit::eCSSUnit_Inch => NoCalcLength::Absolute(AbsoluteLength::In(value)),
+        structs::nsCSSUnit::eCSSUnit_Centimeter => NoCalcLength::Absolute(AbsoluteLength::Cm(value)),
+        structs::nsCSSUnit::eCSSUnit_Millimeter => NoCalcLength::Absolute(AbsoluteLength::Mm(value)),
+        structs::nsCSSUnit::eCSSUnit_Point => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
+        structs::nsCSSUnit::eCSSUnit_Pica => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
+        structs::nsCSSUnit::eCSSUnit_Quarter => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
+        _ => unreachable!("Unknown unit {:?} passed to SetLengthValue", unit)
+    };
+
+    let prop = match_wrap_declared! { long,
+        Width => nocalc.into(),
+        FontSize => LengthOrPercentage::from(nocalc).into(),
+        MozScriptMinSize => nocalc.into(),
+    };
+    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+        decls.push(prop, Importance::Normal);
+    })
+}
+
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetNumberValue(declarations:
                                                        RawServoDeclarationBlockBorrowed,
                                                        property: nsCSSPropertyID,
                                                        value: f32) {
     use style::properties::{PropertyDeclaration, LonghandId};
     use style::properties::longhands::_moz_script_level::SpecifiedValue as MozScriptLevel;
 
@@ -1388,28 +1423,29 @@ pub extern "C" fn Servo_DeclarationBlock
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(declarations:
                                                          RawServoDeclarationBlockBorrowed,
                                                          property: nsCSSPropertyID,
                                                          value: f32) {
     use style::properties::{PropertyDeclaration, LonghandId};
-    use style::values::specified::length::Percentage;
+    use style::values::specified::length::{LengthOrPercentage, Percentage};
 
     let long = get_longhand_from_id!(property);
     let pc = Percentage(value);
 
     let prop = match_wrap_declared! { long,
         Height => pc.into(),
         Width => pc.into(),
         MarginTop => pc.into(),
         MarginRight => pc.into(),
         MarginBottom => pc.into(),
         MarginLeft => pc.into(),
+        FontSize => LengthOrPercentage::from(pc).into(),
     };
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(prop, Importance::Normal);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(declarations: