Bug 1425700 - Add a test for the use counters. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 20 Aug 2018 22:07:19 +0200
changeset 491367 bc3f9356906bd462f3941e2ec6510a145aa65b2d
parent 491366 5449572186472bfc8ed0a48e6b08e6bc4e2424a6
child 491368 42822c922dcfd2664b3a5d357a6b8d1f792c5d85
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1425700
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1425700 - Add a test for the use counters. r=heycam Mostly testing that they work, and that they record what we expect them to record, that is, the actual property that was parsed, and not the properties that it'd resolve or expand to. That may be another tricky part for CSSOM, I think style setters would fail an alias test if implemented with the current setup (we do the property lookup in C++). Differential Revision: https://phabricator.services.mozilla.com/D3829
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
layout/style/ServoBindingList.h
layout/style/test/mochitest.ini
layout/style/test/test_use_counters.html
servo/components/style/properties/properties.mako.rs
servo/components/style/use_counters/mod.rs
servo/ports/geckolib/glue.rs
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4429,8 +4429,27 @@ nsDOMWindowUtils::GetSystemFont(nsACStri
   }
 
   nsAutoCString fontName;
   widget->GetSystemFont(fontName);
   aFontName.Assign(fontName);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::IsCssPropertyRecordedInUseCounter(const nsACString& aPropName,
+                                                    bool* aRecorded)
+{
+  *aRecorded = false;
+
+  nsIDocument* doc = GetDocument();
+  if (!doc || !doc->GetStyleUseCounters()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool knownProp = false;
+  *aRecorded =
+    Servo_IsCssPropertyRecordedInUseCounter(doc->GetStyleUseCounters(),
+                                            &aPropName,
+                                            &knownProp);
+  return knownProp ? NS_OK : NS_ERROR_FAILURE;
+}
+
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1923,16 +1923,24 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Capture the contents of the current WebRender frame and
    * save them to a folder relative to the current working directory.
    */
   void wrCapture();
 
   /**
+   * Returns whether the document we're associated to has recorded a given CSS
+   * property via the use counter mechanism.
+   *
+   * Throws if there's no document or the property is invalid.
+   */
+  bool isCssPropertyRecordedInUseCounter(in ACString aProperty);
+
+  /**
    * NOTE: Currently works only on GTK+.
    */
   attribute ACString systemFont;
 
   // These consts are only for testing purposes.
   const long DEFAULT_MOUSE_POINTER_ID = 0;
   const long DEFAULT_PEN_POINTER_ID   = 1;
   const long DEFAULT_TOUCH_POINTER_ID = 2;
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -903,15 +903,19 @@ SERVO_BINDING_FUNC(Servo_Property_IsShor
 SERVO_BINDING_FUNC(Servo_Property_IsInherited, bool,
                    const nsACString* name);
 SERVO_BINDING_FUNC(Servo_Property_SupportsType, bool,
                    const nsACString* name, uint32_t ty, bool* found);
 SERVO_BINDING_FUNC(Servo_Property_GetCSSValuesForProperty, void,
                    const nsACString* name, bool* found, nsTArray<nsString>* result)
 SERVO_BINDING_FUNC(Servo_PseudoClass_GetStates, uint64_t,
                    const nsACString* name)
+SERVO_BINDING_FUNC(Servo_IsCssPropertyRecordedInUseCounter, bool,
+                   StyleUseCountersBorrowed,
+                   const nsACString* property,
+                   bool* out_known_prop)
 
 // AddRef / Release functions
 #define SERVO_ARC_TYPE(name_, type_)                                \
   SERVO_BINDING_FUNC(Servo_##name_##_AddRef, void, type_##Borrowed) \
   SERVO_BINDING_FUNC(Servo_##name_##_Release, void, type_##Borrowed)
 #include "mozilla/ServoArcTypeList.h"
 #undef SERVO_ARC_TYPE
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -367,8 +367,9 @@ skip-if = toolkit == 'android' # TIMED_O
 [test_webkit_appearance_basic.html]
 [test_webkit_device_pixel_ratio.html]
 [test_webkit_flex_display.html]
 [test_first_letter_restrictions.html]
 [test_first_line_restrictions.html]
 [test_placeholder_restrictions.html]
 [test_mql_event_listener_leaks.html]
 [test_non_matching_sheet_media.html]
+[test_use_counters.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_use_counters.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>Test for Bug 1425700: CSS properties use-counters</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<body>
+<script>
+const utils = SpecialPowers.getDOMWindowUtils(window);
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  { "set": [["layout.css.use-counters.enabled", true ]]},
+  run,
+);
+
+function assert_recorded(recorded, properties) {
+  for (const prop of properties) {
+    try {
+      is(utils.isCssPropertyRecordedInUseCounter(prop), recorded, prop)
+    } catch(ex) {
+      ok(false, "Threw: " + prop);
+    }
+  }
+}
+
+function run() {
+  const style = document.createElement('style');
+  style.textContent = `
+    * {
+      grid-gap: 1px; /* shorthand alias */
+      -webkit-background-size: 100px 100px; /* longhand alias */
+      transform-origin: top left; /* longhand */
+      background: green; /* shorthand */
+    }
+  `;
+
+  document.body.appendChild(style);
+
+  assert_recorded(true, [
+    "grid-gap",
+    "-webkit-background-size",
+    "transform-origin",
+    "background"
+  ]);
+
+  // Should only record the aliases, not the non-aliased property.
+  // Should only record shorthands, not the longhands it expands to.
+  assert_recorded(false, [
+    "gap",
+    "background-size",
+    "-moz-transform-origin",
+    "-webkit-transform-origin",
+    "background-color"
+  ]);
+
+  // TODO(emilio): Make work (and test) inline style and maybe even CSSOM and
+  // such?
+  //
+  // Make sure that something on the lines of the following passes:
+  //
+  //   element.style.webkitTransform = "rotate(1deg)"
+  //   assert_recorded(true, ["-webkit-transform"]);
+  //   assert_recorded(false, ["transform"]);
+  //
+  SimpleTest.finish();
+}
+</script>
+</body>
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1858,17 +1858,18 @@ impl PropertyId {
             PropertyId::ShorthandAlias(id, _) |
             PropertyId::Shorthand(id) => Ok(id),
             PropertyId::LonghandAlias(id, _) |
             PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)),
             PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
         }
     }
 
-    fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
+    /// Returns the `NonCustomPropertyId` corresponding to this property id.
+    pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
         Some(match *self {
             PropertyId::Custom(_) => return None,
             PropertyId::Shorthand(shorthand_id) => shorthand_id.into(),
             PropertyId::Longhand(longhand_id) => longhand_id.into(),
             PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(),
             PropertyId::LonghandAlias(_, alias_id) => alias_id.into(),
         })
     }
--- a/servo/components/style/use_counters/mod.rs
+++ b/servo/components/style/use_counters/mod.rs
@@ -17,23 +17,39 @@ const BITS_PER_ENTRY: usize = 32;
 
 /// One bit per each non-custom CSS property.
 #[derive(Default)]
 pub struct NonCustomPropertyUseCounters {
     storage: [AtomicUsize; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY],
 }
 
 impl NonCustomPropertyUseCounters {
+    /// Returns the bucket a given property belongs in, and the bitmask for that
+    /// property.
+    #[inline(always)]
+    fn bucket_and_pattern(id: NonCustomPropertyId) -> (usize, usize) {
+        let bit = id.bit();
+        let bucket = bit / BITS_PER_ENTRY;
+        let bit_in_bucket = bit % BITS_PER_ENTRY;
+        (bucket, 1 << bit_in_bucket)
+    }
+
     /// Record that a given non-custom property ID has been parsed.
     #[inline]
     pub fn record(&self, id: NonCustomPropertyId) {
-        let bit = id.bit();
-        let bucket = bit / BITS_PER_ENTRY;
-        let bit_in_bucket = bit % BITS_PER_ENTRY;
-        self.storage[bucket].fetch_or(1 << bit_in_bucket, Ordering::Relaxed);
+        let (bucket, pattern) = Self::bucket_and_pattern(id);
+        self.storage[bucket].fetch_or(pattern, Ordering::Relaxed);
+    }
+
+    /// Returns whether a given non-custom property ID has been recorded
+    /// earlier.
+    #[inline]
+    pub fn recorded(&self, id: NonCustomPropertyId) -> bool {
+        let (bucket, pattern) = Self::bucket_and_pattern(id);
+        self.storage[bucket].load(Ordering::Relaxed) & pattern != 0
     }
 }
 
 /// The use-counter data related to a given document we want to store.
 #[derive(Default)]
 pub struct UseCounters {
     /// The counters for non-custom properties that have been parsed in the
     /// document's stylesheets.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -5820,8 +5820,24 @@ pub unsafe extern "C" fn Servo_PseudoCla
         Some(pseudo_class) => pseudo_class.state_flag().bits(),
     }
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_UseCounters_Create() -> bindings::StyleUseCountersStrong {
     Arc::new(UseCounters::default()).into_strong()
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
+    use_counters: bindings::StyleUseCountersBorrowed,
+    property: *const nsACString,
+    known_prop: *mut bool,
+) -> bool {
+    let prop_id = parse_enabled_property_name!(property, known_prop, false);
+    let non_custom_id = match prop_id.non_custom_id() {
+        Some(id) => id,
+        None => return false,
+    };
+
+    let use_counters = UseCounters::as_arc(&use_counters);
+    use_counters.non_custom_properties.recorded(non_custom_id)
+}