Bug 1448763 part 1 - Make DOMIntersectionObserver use nsStyleSides for mRootMargin, and use Servo code to serialize it. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Thu, 05 Apr 2018 13:23:27 +1000
changeset 777682 19ac679c06e453e22c7bd124a72768ba28ac6b94
parent 777681 8efa4efd9b7ca559cf356376e1c1e375a3102eec
child 777683 f36ec0a41b2c67834f87292eea0b9a05022c10c8
push id105263
push userxquan@mozilla.com
push dateThu, 05 Apr 2018 05:22:06 +0000
reviewersemilio
bugs1448763
milestone61.0a1
Bug 1448763 part 1 - Make DOMIntersectionObserver use nsStyleSides for mRootMargin, and use Servo code to serialize it. r?emilio MozReview-Commit-ID: 8RsoLflRtzE
dom/base/DOMIntersectionObserver.cpp
dom/base/DOMIntersectionObserver.h
layout/style/ServoBindingList.h
layout/style/ServoBindings.toml
layout/style/ServoCSSParser.cpp
layout/style/ServoCSSParser.h
servo/components/style/values/specified/gecko.rs
servo/ports/geckolib/glue.rs
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMIntersectionObserver.h"
 #include "nsCSSParser.h"
 #include "nsCSSPropertyID.h"
 #include "nsIFrame.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
-#include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoBindings.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
@@ -111,24 +111,24 @@ DOMIntersectionObserver::Constructor(con
   }
 
   return observer.forget();
 }
 
 bool
 DOMIntersectionObserver::SetRootMargin(const nsAString& aString)
 {
-  return ServoCSSParser::ParseIntersectionObserverRootMargin(aString,
-                                                             &mRootMargin);
+  return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin);
 }
 
 void
 DOMIntersectionObserver::GetRootMargin(mozilla::dom::DOMString& aRetVal)
 {
-  mRootMargin.AppendToString(eCSSProperty_DOM, aRetVal);
+  nsString& retVal = aRetVal;
+  Servo_IntersectionObserverRootMargin_ToString(&mRootMargin, &retVal);
 }
 
 void
 DOMIntersectionObserver::GetThresholds(nsTArray<double>& aRetVal)
 {
   aRetVal = mThresholds;
 }
 
@@ -301,25 +301,17 @@ DOMIntersectionObserver::Update(nsIDocum
       }
     }
   }
 
   nsMargin rootMargin;
   NS_FOR_CSS_SIDES(side) {
     nscoord basis = side == eSideTop || side == eSideBottom ?
       rootRect.Height() : rootRect.Width();
-    nsCSSValue value = mRootMargin.*nsCSSRect::sides[side];
-    nsStyleCoord coord;
-    if (value.IsPixelLengthUnit()) {
-      coord.SetCoordValue(value.GetPixelLength());
-    } else if (value.IsPercentLengthUnit()) {
-      coord.SetPercentValue(value.GetPercentValue());
-    } else {
-      MOZ_ASSERT_UNREACHABLE("invalid length unit");
-    }
+    nsStyleCoord coord = mRootMargin.Get(side);
     rootMargin.Side(side) = nsLayoutUtils::ComputeCBDependentValue(basis, coord);
   }
 
   for (size_t i = 0; i < mObservationTargets.Length(); ++i) {
     Element* target = mObservationTargets.ElementAt(i);
     nsIFrame* targetFrame = target->GetPrimaryFrame();
     nsRect targetRect;
     Maybe<nsRect> intersectionRect;
--- a/dom/base/DOMIntersectionObserver.h
+++ b/dom/base/DOMIntersectionObserver.h
@@ -3,17 +3,17 @@
 /* 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/. */
 
 #ifndef DOMIntersectionObserver_h
 #define DOMIntersectionObserver_h
 
 #include "mozilla/dom/IntersectionObserverBinding.h"
-#include "nsCSSValue.h"
+#include "nsStyleCoord.h"
 #include "nsTArray.h"
 
 using mozilla::dom::DOMRect;
 using mozilla::dom::Element;
 
 namespace mozilla {
 namespace dom {
 
@@ -172,17 +172,17 @@ protected:
                                       const nsRect& aTargetRect,
                                       const Maybe<nsRect>& aIntersectionRect,
                                       double aIntersectionRatio);
 
   nsCOMPtr<nsPIDOMWindowInner>                    mOwner;
   RefPtr<nsIDocument>                             mDocument;
   RefPtr<mozilla::dom::IntersectionCallback>      mCallback;
   RefPtr<Element>                                 mRoot;
-  nsCSSRect                                       mRootMargin;
+  nsStyleSides                                    mRootMargin;
   nsTArray<double>                                mThresholds;
 
   // Holds raw pointers which are explicitly cleared by UnlinkTarget().
   nsTArray<Element*>                              mObservationTargets;
 
   nsTArray<RefPtr<DOMIntersectionObserverEntry>>  mQueuedEntries;
   bool                                            mConnected;
 };
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -831,19 +831,20 @@ SERVO_BINDING_FUNC(Servo_CloneArcStringD
 SERVO_BINDING_FUNC(Servo_IsValidCSSColor, bool, const nsAString* value);
 SERVO_BINDING_FUNC(Servo_ComputeColor, bool,
                    RawServoStyleSetBorrowedOrNull set,
                    nscolor current_color,
                    const nsAString* value,
                    nscolor* result_color,
                    bool* was_current_color,
                    mozilla::css::Loader* loader)
-SERVO_BINDING_FUNC(Servo_ParseIntersectionObserverRootMargin, bool,
-                   const nsAString* value,
-                   nsCSSRect* result);
+SERVO_BINDING_FUNC(Servo_IntersectionObserverRootMargin_Parse, bool,
+                   const nsAString* value, nsStyleSides* result)
+SERVO_BINDING_FUNC(Servo_IntersectionObserverRootMargin_ToString, void,
+                   const nsStyleSides* rect, nsAString* result)
 // Returning false means the parsed transform contains relative lengths or
 // percentage value, so we cannot compute the matrix. In this case, we keep
 // |result| and |contains_3d_transform| as-is.
 SERVO_BINDING_FUNC(Servo_ParseTransformIntoMatrix, bool,
                    const nsAString* value,
                    bool* contains_3d_transform,
                    RawGeckoGfxMatrix4x4* result);
 SERVO_BINDING_FUNC(Servo_ParseFontShorthandForMatching, bool,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -277,18 +277,16 @@ whitelist-types = [
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
     "nsCSSFontDesc",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSProps",
-    "nsCSSRect",
-    "nsCSSRect_heap",
     "nsCSSShadowArray",
     "nsCSSValue",
     "nsCSSValueFloatColor",
     "nsCSSValueGradient",
     "nsCSSValueGradientStop",
     "nsCSSValueList",
     "nsCSSValueList_heap",
     "nsCSSValuePair_heap",
@@ -329,16 +327,17 @@ whitelist-types = [
     "nsStyleGridTemplate",
     "nsStyleImage",
     "nsStyleImageLayers",
     "nsStyleList",
     "nsStyleMargin",
     "nsStyleOutline",
     "nsStylePadding",
     "nsStylePosition",
+    "nsStyleSides",
     "nsStyleSVG",
     "nsStyleSVGOpacitySource",
     "nsStyleSVGPaint",
     "nsStyleSVGReset",
     "nsStyleTable",
     "nsStyleTableBorder",
     "nsStyleText",
     "nsStyleTextReset",
@@ -529,17 +528,16 @@ structs-types = [
     "StyleShapeSource",
     "StyleTransition",
     "gfxFontFeatureValueSet",
     "nsCSSCounterDesc",
     "nsCSSFontDesc",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
-    "nsCSSRect",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
     "nsCSSValueSharedList",
     "nsChangeHint",
     "nsCursorImage",
     "nsFont",
     "nsAtom",
@@ -575,16 +573,17 @@ structs-types = [
     "nsStyleOutline",
     "nsStylePadding",
     "nsStylePosition",
     "nsStyleQuoteValues",
     "nsStyleSVG",
     "nsStyleSVGOpacitySource",
     "nsStyleSVGPaint",
     "nsStyleSVGReset",
+    "nsStyleSides",
     "nsStyleTable",
     "nsStyleTableBorder",
     "nsStyleText",
     "nsStyleTextReset",
     "nsStyleUIReset",
     "nsStyleUnion",
     "nsStyleUnit",
     "nsStyleUserInterface",
--- a/layout/style/ServoCSSParser.cpp
+++ b/layout/style/ServoCSSParser.cpp
@@ -28,23 +28,16 @@ ServoCSSParser::ComputeColor(ServoStyleS
                              bool* aWasCurrentColor,
                              css::Loader* aLoader)
 {
   return Servo_ComputeColor(aStyleSet ? aStyleSet->RawSet() : nullptr,
                             aCurrentColor, &aValue, aResultColor,
                             aWasCurrentColor, aLoader);
 }
 
-/* static */ bool
-ServoCSSParser::ParseIntersectionObserverRootMargin(const nsAString& aValue,
-                                                    nsCSSRect* aResult)
-{
-  return Servo_ParseIntersectionObserverRootMargin(&aValue, aResult);
-}
-
 /* static */ already_AddRefed<RawServoDeclarationBlock>
 ServoCSSParser::ParseProperty(nsCSSPropertyID aProperty,
                               const nsAString& aValue,
                               const ParsingEnvironment& aParsingEnvironment,
                               ParsingMode aParsingMode)
 {
   NS_ConvertUTF16toUTF8 value(aValue);
   return Servo_ParseProperty(aProperty,
--- a/layout/style/ServoCSSParser.h
+++ b/layout/style/ServoCSSParser.h
@@ -68,27 +68,16 @@ public:
   static bool ComputeColor(ServoStyleSet* aStyleSet,
                            nscolor aCurrentColor,
                            const nsAString& aValue,
                            nscolor* aResultColor,
                            bool* aWasCurrentColor = nullptr,
                            css::Loader* aLoader = nullptr);
 
   /**
-   * Parses a IntersectionObserver's initialization dictionary's rootMargin
-   * property.
-   *
-   * @param aValue The rootMargin value.
-   * @param aResult The nsCSSRect object to write the result into.
-   * @return Whether the value was successfully parsed.
-   */
-  static bool ParseIntersectionObserverRootMargin(const nsAString& aValue,
-                                                  nsCSSRect* aResult);
-
-  /**
    * Parse a string representing a CSS property value into a
    * RawServoDeclarationBlock.
    *
    * @param aProperty The property to be parsed.
    * @param aValue The specified value.
    * @param aParsingEnvironment All the parsing environment data we need.
    * @param aParsingMode The paring mode we apply.
    * @return The parsed value as a RawServoDeclarationBlock. We put the value
--- a/servo/components/style/values/specified/gecko.rs
+++ b/servo/components/style/values/specified/gecko.rs
@@ -1,24 +1,26 @@
 /* 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/. */
 
 //! Specified types for legacy Gecko-only properties.
 
 use cssparser::{Parser, Token};
-use gecko_bindings::structs;
-use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
+use gecko::values::GeckoStyleCoordConvertible;
+use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut};
 use parser::{Parse, ParserContext};
-use style_traits::{ParseError, StyleParseErrorKind};
-use values::CSSFloat;
+use std::fmt;
+use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::values::SequenceWriter;
 use values::computed;
 use values::generics::gecko::ScrollSnapPoint as GenericScrollSnapPoint;
 use values::generics::rect::Rect;
 use values::specified::length::LengthOrPercentage;
+use values::computed::length::CSSPixelLength;
 
 /// A specified type for scroll snap points.
 pub type ScrollSnapPoint = GenericScrollSnapPoint<LengthOrPercentage>;
 
 impl Parse for ScrollSnapPoint {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if input.try(|i| i.expect_ident_matching("none")).is_ok() {
             return Ok(GenericScrollSnapPoint::None);
@@ -27,62 +29,67 @@ impl Parse for ScrollSnapPoint {
         let length = input.parse_nested_block(|i| {
             LengthOrPercentage::parse_non_negative(context, i)
         })?;
         Ok(GenericScrollSnapPoint::Repeat(length))
     }
 }
 
 /// A component of an IntersectionObserverRootMargin.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum PixelOrPercentage {
     /// An absolute length in pixels (px)
-    Px(CSSFloat),
+    Pixel(CSSPixelLength),
     /// A percentage (%)
     Percentage(computed::Percentage),
 }
 
 impl Parse for PixelOrPercentage {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Self, ParseError<'i>> {
         let location = input.current_source_location();
         let token = input.next()?;
         let value = match *token {
             Token::Dimension { value, ref unit, .. } => {
                 match_ignore_ascii_case! { unit,
-                    "px" => Ok(PixelOrPercentage::Px(value)),
+                    "px" => Ok(PixelOrPercentage::Pixel(CSSPixelLength::new(value))),
                     _ => Err(()),
                 }
             }
             Token::Percentage { unit_value, .. } => {
                 Ok(PixelOrPercentage::Percentage(
                     computed::Percentage(unit_value)
                 ))
             }
             _ => Err(()),
         };
         value.map_err(|()| {
             location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
         })
     }
 }
 
-impl ToNsCssValue for PixelOrPercentage {
-    fn convert(self, nscssvalue: &mut structs::nsCSSValue) {
-        match self {
-            PixelOrPercentage::Px(px) => {
-                unsafe { nscssvalue.set_px(px); }
-            }
-            PixelOrPercentage::Percentage(pc) => {
-                unsafe { nscssvalue.set_percentage(pc.0); }
-            }
+impl GeckoStyleCoordConvertible for PixelOrPercentage {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        match *self {
+            PixelOrPercentage::Pixel(ref l) => l.to_gecko_style_coord(coord),
+            PixelOrPercentage::Percentage(ref pc) => pc.to_gecko_style_coord(coord),
         }
     }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        CSSPixelLength::from_gecko_style_coord(coord)
+            .map(PixelOrPercentage::Pixel)
+            .or_else(|| {
+                computed::Percentage::from_gecko_style_coord(coord)
+                    .map(PixelOrPercentage::Percentage)
+            })
+    }
 }
 
 /// The value of an IntersectionObserver's rootMargin property.
 ///
 /// Only bare px or percentage values are allowed. Other length units and
 /// calc() values are not allowed.
 ///
 /// <https://w3c.github.io/IntersectionObserver/#parse-a-root-margin>
@@ -92,8 +99,25 @@ impl Parse for IntersectionObserverRootM
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let rect = Rect::parse_with(context, input, PixelOrPercentage::parse)?;
         Ok(IntersectionObserverRootMargin(rect))
     }
 }
+
+// Strictly speaking this is not ToCss. It's serializing for DOM. But
+// we can just reuse the infrastructure of this.
+//
+// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>
+impl ToCss for IntersectionObserverRootMargin {
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+        // We cannot use the ToCss impl of Rect, because that would
+        // merge items when they are equal. We want to list them all.
+        let mut writer = SequenceWriter::new(dest, " ");
+        let rect = &self.0;
+        writer.item(&rect.0)?;
+        writer.item(&rect.1)?;
+        writer.item(&rect.2)?;
+        writer.item(&rect.3)
+    }
+}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -152,18 +152,19 @@ use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
 use style::traversal::resolve_style;
 use style::traversal_flags::{self, TraversalFlags};
 use style::values::{CustomIdent, KeyframesName};
 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
 use style::values::computed::{Context, ToComputedValue};
 use style::values::distance::ComputeSquaredDistance;
+use style::values::generics::rect::Rect;
 use style::values::specified;
-use style::values::specified::gecko::IntersectionObserverRootMargin;
+use style::values::specified::gecko::{IntersectionObserverRootMargin, PixelOrPercentage};
 use style::values::specified::source_size_list::SourceSizeList;
 use style_traits::{CssWriter, ParsingMode, StyleParseErrorKind, ToCss};
 use super::error_reporter::ErrorReporter;
 use super::stylesheet_loader::StylesheetLoader;
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
@@ -5199,52 +5200,60 @@ pub extern "C" fn Servo_ComputeColor(
                 None => false,
             }
         }
         Err(_) => false,
     }
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_ParseIntersectionObserverRootMargin(
+pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_Parse(
     value: *const nsAString,
-    result: *mut structs::nsCSSRect,
+    result: *mut structs::nsStyleSides,
 ) -> bool {
-    let value = unsafe { value.as_ref().unwrap().to_string() };
-    let result = unsafe { result.as_mut().unwrap() };
+    let value = value.as_ref().unwrap().to_string();
+    let result = result.as_mut().unwrap();
 
     let mut input = ParserInput::new(&value);
     let mut parser = Parser::new(&mut input);
 
-    let url_data = unsafe { dummy_url_data() };
+    let url_data = dummy_url_data();
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
     );
 
     let margin = parser.parse_entirely(|p| {
         IntersectionObserverRootMargin::parse(&context, p)
     });
     match margin {
         Ok(margin) => {
-            let rect = margin.0;
-            result.mTop.set_from(rect.0);
-            result.mRight.set_from(rect.1);
-            result.mBottom.set_from(rect.2);
-            result.mLeft.set_from(rect.3);
+            margin.0.to_gecko_rect(result);
             true
         }
         Err(..) => false,
     }
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_ToString(
+    rect: *mut structs::nsStyleSides,
+    result: *mut nsAString,
+) {
+    let rect = Rect::<PixelOrPercentage>::
+        from_gecko_rect(rect.as_ref().unwrap()).unwrap();
+    let root_margin = IntersectionObserverRootMargin(rect);
+    let mut writer = CssWriter::new(result.as_mut().unwrap());
+    root_margin.to_css(&mut writer).unwrap();
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_ParseTransformIntoMatrix(
     value: *const nsAString,
     contain_3d: *mut bool,
     result: *mut RawGeckoGfxMatrix4x4
 ) -> bool {
     use style::properties::longhands::transform;
 
     let string = unsafe { (*value).to_string() };