Merge mozilla-central to autoland
authorDorel Luca <dluca@mozilla.com>
Sun, 06 May 2018 00:59:40 +0300
changeset 473173 86d103a429eb2fbb450f05af51e2afe0c5d54bdc
parent 473172 8534948d69d4703af5e97e91022b15d4c30c17e8 (current diff)
parent 473170 9a2eac450781b026b42c44ca8f0f92bb0846b6e2 (diff)
child 473174 355a6e3f9bede2598878cf723a085b396846fe20
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone61.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
Merge mozilla-central to autoland
js/src/devtools/automation/tsan-sighandlers.txt
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -1056,18 +1056,17 @@ Toolbox.prototype = {
   },
 
   /**
    * Build the notification box as soon as needed.
    */
   get notificationBox() {
     if (!this._notificationBox) {
       let { NotificationBox, PriorityLevels } =
-        this.browserRequire(
-          "devtools/client/shared/components/NotificationBox");
+        this.browserRequire("devtools/client/shared/components/NotificationBox");
 
       NotificationBox = this.React.createFactory(NotificationBox);
 
       // Render NotificationBox and assign priority levels to it.
       let box = this.doc.getElementById("toolbox-notificationbox");
       this._notificationBox = Object.assign(
         this.ReactDOM.render(NotificationBox({}), box),
         PriorityLevels);
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -120,22 +120,23 @@ function Inspector(toolbox) {
   this.previousURL = this.target.url;
 
   this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
   this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF);
   this.show3PaneTooltip = Services.prefs.getBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF);
 
   this.nodeMenuTriggerInfo = null;
 
+  this._clearSearchResultsLabel = this._clearSearchResultsLabel.bind(this);
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
+  this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
-  this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
-  this._clearSearchResultsLabel = this._clearSearchResultsLabel.bind(this);
+  this._updateDebuggerPausedWarning = this._updateDebuggerPausedWarning.bind(this);
 
   this.onDetached = this.onDetached.bind(this);
   this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onNewRoot = this.onNewRoot.bind(this);
   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
   this.onShowBoxModelHighlighterForNode =
     this.onShowBoxModelHighlighterForNode.bind(this);
@@ -168,41 +169,49 @@ Inspector.prototype = {
     return this._deferredOpen(defaultSelection);
   },
 
   get toolbox() {
     return this._toolbox;
   },
 
   get inspector() {
-    return this._toolbox.inspector;
+    return this.toolbox.inspector;
   },
 
   get walker() {
-    return this._toolbox.walker;
+    return this.toolbox.walker;
   },
 
   get selection() {
-    return this._toolbox.selection;
+    return this.toolbox.selection;
   },
 
   get highlighter() {
-    return this._toolbox.highlighter;
+    return this.toolbox.highlighter;
   },
 
   // Added in 53.
   get canGetCssPath() {
     return this._target.client.traits.getCssPath;
   },
 
   // Added in 56.
   get canGetXPath() {
     return this._target.client.traits.getXPath;
   },
 
+  get notificationBox() {
+    if (!this._notificationBox) {
+      this._notificationBox = this.toolbox.getNotificationBox();
+    }
+
+    return this._notificationBox;
+  },
+
   /**
    * Handle promise rejections for various asynchronous actions, and only log errors if
    * the inspector panel still exists.
    * This is useful to silence useless errors that happen when the inspector is closed
    * while still initializing (and making protocol requests).
    */
   _handleRejectionIfNotDestroyed: function(e) {
     if (!this._panelDestroyer) {
@@ -214,42 +223,20 @@ Inspector.prototype = {
     this.breadcrumbs = new HTMLBreadcrumbs(this);
 
     this.walker.on("new-root", this.onNewRoot);
 
     this.selection.on("new-node-front", this.onNewSelection);
     this.selection.on("detached-front", this.onDetached);
 
     if (this.target.isLocalTab) {
-      // Show a warning when the debugger is paused.
-      // We show the warning only when the inspector
-      // is selected.
-      this.updateDebuggerPausedWarning = () => {
-        let notificationBox = this._toolbox.getNotificationBox();
-        let notification =
-          notificationBox.getNotificationWithValue("inspector-script-paused");
-        if (!notification && this._toolbox.currentToolId == "inspector" &&
-            this._toolbox.threadClient.paused) {
-          let message = INSPECTOR_L10N.getStr("debuggerPausedWarning.message");
-          notificationBox.appendNotification(message,
-            "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
-        }
-
-        if (notification && this._toolbox.currentToolId != "inspector") {
-          notificationBox.removeNotification(notification);
-        }
-
-        if (notification && !this._toolbox.threadClient.paused) {
-          notificationBox.removeNotification(notification);
-        }
-      };
-      this.target.on("thread-paused", this.updateDebuggerPausedWarning);
-      this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
-      this._toolbox.on("select", this.updateDebuggerPausedWarning);
-      this.updateDebuggerPausedWarning();
+      this.target.on("thread-paused", this._updateDebuggerPausedWarning);
+      this.target.on("thread-resumed", this._updateDebuggerPausedWarning);
+      this.toolbox.on("select", this._updateDebuggerPausedWarning);
+      this._updateDebuggerPausedWarning();
     }
 
     this._initMarkup();
     this.isReady = false;
 
     this.setupSearchBox();
 
     // Setup the splitter before the sidebar is displayed so,
@@ -418,16 +405,45 @@ Inspector.prototype = {
       } else {
         str = INSPECTOR_L10N.getStr("inspector.searchResultsNone");
       }
     }
 
     this.searchResultsLabel.textContent = str;
   },
 
+  /**
+   * Show a warning notification box when the debugger is paused. We show the warning only
+   * when the inspector is selected.
+   */
+  _updateDebuggerPausedWarning: function() {
+    if (!this.toolbox.threadClient.paused && !this._notificationBox) {
+      return;
+    }
+
+    let notificationBox = this.notificationBox;
+    let notification = this.notificationBox.getNotificationWithValue(
+      "inspector-script-paused");
+
+    if (!notification && this.toolbox.currentToolId == "inspector" &&
+        this.toolbox.threadClient.paused) {
+      let message = INSPECTOR_L10N.getStr("debuggerPausedWarning.message");
+      notificationBox.appendNotification(message,
+        "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
+    }
+
+    if (notification && this.toolbox.currentToolId != "inspector") {
+      notificationBox.removeNotification(notification);
+    }
+
+    if (notification && !this.toolbox.threadClient.paused) {
+      notificationBox.removeNotification(notification);
+    }
+  },
+
   get React() {
     return this._toolbox.React;
   },
 
   get ReactDOM() {
     return this._toolbox.ReactDOM;
   },
 
@@ -1273,19 +1289,19 @@ Inspector.prototype = {
     }
 
     this.cancelUpdate();
 
     this.selection.off("new-node-front", this.onNewSelection);
     this.selection.off("detached-front", this.onDetached);
     this.sidebar.off("select", this.onSidebarSelect);
     this.target.off("will-navigate", this._onBeforeNavigate);
-    this.target.off("thread-paused", this.updateDebuggerPausedWarning);
-    this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
-    this._toolbox.off("select", this.updateDebuggerPausedWarning);
+    this.target.off("thread-paused", this._updateDebuggerPausedWarning);
+    this.target.off("thread-resumed", this._updateDebuggerPausedWarning);
+    this._toolbox.off("select", this._updateDebuggerPausedWarning);
 
     for (let [, panel] of this._panels) {
       panel.destroy();
     }
     this._panels.clear();
 
     if (this.layoutview) {
       this.layoutview.destroy();
@@ -1313,32 +1329,33 @@ Inspector.prototype = {
     this.teardownSplitter();
     this.teardownToolbar();
 
     this.breadcrumbs.destroy();
     this.reflowTracker.destroy();
     this.styleChangeTracker.destroy();
     this.search.destroy();
 
-    this.telemetry = null;
+    this._notificationBox = null;
     this._target = null;
     this._toolbox = null;
     this.breadcrumbs = null;
     this.highlighters = null;
     this.is3PaneModeEnabled = null;
     this.panelDoc = null;
     this.panelWin.inspector = null;
     this.panelWin = null;
     this.resultsLength = null;
     this.search = null;
     this.searchBox = null;
     this.show3PaneToggle = null;
     this.show3PaneTooltip = null;
     this.sidebar = null;
     this.store = null;
+    this.telemetry = null;
     this.threePaneTooltip = null;
 
     this._panelDestroyer = promise.all([
       highlighterDestroyer,
       cssPropertiesDestroyer,
       markupDestroyer,
       sidebarDestroyer,
       ruleViewSideBarDestroyer
--- a/dom/smil/nsSMILCSSValueType.cpp
+++ b/dom/smil/nsSMILCSSValueType.cpp
@@ -11,17 +11,19 @@
 #include "nsComputedDOMStyle.h"
 #include "nsString.h"
 #include "nsSMILParserUtils.h"
 #include "nsSMILValue.h"
 #include "nsCSSProps.h"
 #include "nsCSSValue.h"
 #include "nsColor.h"
 #include "nsPresContext.h"
+#include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/ServoDeclarationBlock.h"
 #include "mozilla/StyleAnimationValue.h" // For AnimationValue
 #include "mozilla/ServoCSSParser.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/dom/BaseKeyframeTypesBinding.h" // For CompositeOperation
 #include "mozilla/dom/Element.h"
 #include "nsDebug.h"
 #include "nsStyleUtil.h"
 #include "nsIDocument.h"
@@ -33,49 +35,43 @@ using mozilla::StyleAnimationValue;
 typedef AutoTArray<RefPtr<RawServoAnimationValue>, 1> ServoAnimationValues;
 
 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
 
 struct ValueWrapper {
   ValueWrapper(nsCSSPropertyID aPropID, const AnimationValue& aValue)
     : mPropID(aPropID)
   {
-    if (aValue.mServo) {
-      mServoValues.AppendElement(aValue.mServo);
-      return;
-    }
-    MOZ_CRASH("old style system disabled");
+    MOZ_ASSERT(!aValue.IsNull());
+    mServoValues.AppendElement(aValue.mServo);
   }
   ValueWrapper(nsCSSPropertyID aPropID,
                const RefPtr<RawServoAnimationValue>& aValue)
     : mPropID(aPropID), mServoValues{(aValue)} {}
   ValueWrapper(nsCSSPropertyID aPropID, ServoAnimationValues&& aValues)
     : mPropID(aPropID), mServoValues{aValues} {}
 
   bool operator==(const ValueWrapper& aOther) const
   {
     if (mPropID != aOther.mPropID) {
       return false;
     }
 
-    if (!mServoValues.IsEmpty()) {
-      size_t len = mServoValues.Length();
-      if (len != aOther.mServoValues.Length()) {
+    MOZ_ASSERT(!mServoValues.IsEmpty());
+    size_t len = mServoValues.Length();
+    if (len != aOther.mServoValues.Length()) {
+      return false;
+    }
+    for (size_t i = 0; i < len; i++) {
+      if (!Servo_AnimationValue_DeepEqual(mServoValues[i],
+                                          aOther.mServoValues[i])) {
         return false;
       }
-      for (size_t i = 0; i < len; i++) {
-        if (!Servo_AnimationValue_DeepEqual(mServoValues[i],
-                                            aOther.mServoValues[i])) {
-          return false;
-        }
-      }
-      return true;
     }
-
-    MOZ_CRASH("old style system disabled");
+    return true;
   }
 
   bool operator!=(const ValueWrapper& aOther) const
   {
     return !(*this == aOther);
   }
 
   nsCSSPropertyID mPropID;
@@ -299,28 +295,21 @@ AddOrAccumulate(nsSMILValue& aDest, cons
       property == eCSSProperty_stroke_dasharray) {
     return false;
   }
   // Skip font shorthand since it includes font-size-adjust.
   if (property == eCSSProperty_font) {
     return false;
   }
 
-  bool isServo = valueToAddWrapper
-                 ? !valueToAddWrapper->mServoValues.IsEmpty()
-                 : !destWrapper->mServoValues.IsEmpty();
-  if (isServo) {
-    return AddOrAccumulateForServo(aDest,
-                                   valueToAddWrapper,
-                                   destWrapper,
-                                   aCompositeOp,
-                                   aCount);
-  }
-
-  MOZ_CRASH("old style system disabled");
+  return AddOrAccumulateForServo(aDest,
+                                 valueToAddWrapper,
+                                 destWrapper,
+                                 aCompositeOp,
+                                 aCount);
 }
 
 nsresult
 nsSMILCSSValueType::SandwichAdd(nsSMILValue& aDest,
                                 const nsSMILValue& aValueToAdd) const
 {
   return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Add, 1)
          ? NS_OK
@@ -382,22 +371,17 @@ nsSMILCSSValueType::ComputeDistance(cons
 {
   MOZ_ASSERT(aFrom.mType == aTo.mType,
              "Trying to compare different types");
   MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
 
   const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
   const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
   MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
-
-  if (!toWrapper->mServoValues.IsEmpty()) {
-    return ComputeDistanceForServo(fromWrapper, *toWrapper, aDistance);
-  }
-
-  MOZ_CRASH("old style system disabled");
+  return ComputeDistanceForServo(fromWrapper, *toWrapper, aDistance);
 }
 
 
 static nsresult
 InterpolateForServo(const ValueWrapper* aStartWrapper,
                     const ValueWrapper& aEndWrapper,
                     double aUnitDistance,
                     nsSMILValue& aResult)
@@ -461,25 +445,20 @@ nsSMILCSSValueType::Interpolate(const ns
   MOZ_ASSERT(aResult.mType == this, "Unexpected result type");
   MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
              "unit distance value out of bounds");
   MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
 
   const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
   const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
   MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
-
-  if (!endWrapper->mServoValues.IsEmpty()) {
-    return InterpolateForServo(startWrapper,
-                               *endWrapper,
-                               aUnitDistance,
-                               aResult);
-  }
-
-  MOZ_CRASH("old style system disabled");
+  return InterpolateForServo(startWrapper,
+                             *endWrapper,
+                             aUnitDistance,
+                             aResult);
 }
 
 // Helper function to extract presContext
 static nsPresContext*
 GetPresContextForElement(Element* aElem)
 {
   nsIDocument* doc = aElem->GetUncomposedDoc();
   if (!doc) {
@@ -595,44 +574,35 @@ nsSMILCSSValueType::ValueFromAnimationVa
 
   sSingleton.Init(result);
   result.mU.mPtr = new ValueWrapper(aPropID, aValue);
 
   return result;
 }
 
 // static
-void
-nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
-                                  nsAString& aString)
+bool
+nsSMILCSSValueType::SetPropertyValues(const nsSMILValue& aValue,
+                                      DeclarationBlock& aDecl)
 {
   MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
              "Unexpected SMIL value type");
   const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
   if (!wrapper) {
-    return;
-  }
-
-  if (wrapper->mServoValues.IsEmpty()) {
-    MOZ_CRASH("old style system disabled");
+    return false;
   }
 
-  if (nsCSSProps::IsShorthand(wrapper->mPropID)) {
-    // In case of shorthand on servo, we iterate over all mServoValues array
-    // since we have multiple AnimationValues in the array for each longhand
-    // component.
-    Servo_Shorthand_AnimationValues_Serialize(wrapper->mPropID,
-                                              &wrapper->mServoValues,
-                                              &aString);
-    return;
+  bool changed = false;
+  for (const auto& value : wrapper->mServoValues) {
+    changed |=
+      Servo_DeclarationBlock_SetPropertyToAnimationValue(
+        aDecl.AsServo()->Raw(), value);
   }
 
-  Servo_AnimationValue_Serialize(wrapper->mServoValues[0],
-                                 wrapper->mPropID,
-                                 &aString);
+  return changed;
 }
 
 // static
 nsCSSPropertyID
 nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue)
 {
   if (aValue.mType != &nsSMILCSSValueType::sSingleton) {
     return eCSSProperty_UNKNOWN;
@@ -662,28 +632,22 @@ nsSMILCSSValueType::FinalizeValue(nsSMIL
   }
 
   const ValueWrapper* valueToMatchWrapper = ExtractValueWrapper(aValueToMatch);
   if (!valueToMatchWrapper) {
     MOZ_ASSERT_UNREACHABLE("Value to match is empty");
     return;
   }
 
-  bool isServo = !valueToMatchWrapper->mServoValues.IsEmpty();
-
-  if (isServo) {
-    ServoAnimationValues zeroValues;
-    zeroValues.SetCapacity(valueToMatchWrapper->mServoValues.Length());
+  ServoAnimationValues zeroValues;
+  zeroValues.SetCapacity(valueToMatchWrapper->mServoValues.Length());
 
-    for (auto& valueToMatch : valueToMatchWrapper->mServoValues) {
-      RefPtr<RawServoAnimationValue> zeroValue =
-        Servo_AnimationValues_GetZeroValue(valueToMatch).Consume();
-      if (!zeroValue) {
-        return;
-      }
-      zeroValues.AppendElement(Move(zeroValue));
+  for (auto& valueToMatch : valueToMatchWrapper->mServoValues) {
+    RefPtr<RawServoAnimationValue> zeroValue =
+      Servo_AnimationValues_GetZeroValue(valueToMatch).Consume();
+    if (!zeroValue) {
+      return;
     }
-    aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
-                                      Move(zeroValues));
-  } else {
-    MOZ_CRASH("old style system disabled");
+    zeroValues.AppendElement(Move(zeroValue));
   }
+  aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
+                                    Move(zeroValues));
 }
--- a/dom/smil/nsSMILCSSValueType.h
+++ b/dom/smil/nsSMILCSSValueType.h
@@ -11,16 +11,17 @@
 
 #include "nsISMILType.h"
 #include "nsCSSPropertyID.h"
 #include "nsStringFwd.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 struct AnimationValue;
+class DeclarationBlock;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 /*
  * nsSMILCSSValueType: Represents a SMIL-animated CSS value.
  */
@@ -98,26 +99,21 @@ public:
    *                        nsSMILValue with the null type (i.e. rv.IsNull()
    *                        returns true).
    */
   static nsSMILValue ValueFromAnimationValue(nsCSSPropertyID aPropID,
                                              Element* aTargetElement,
                                              const AnimationValue& aValue);
 
   /**
-   * Creates a string representation of the given nsSMILValue.
+   * Sets the relevant property values in the declaration block.
    *
-   * Note: aValue is expected to be of this type (that is, it's expected to
-   * have been initialized by nsSMILCSSValueType::sSingleton).  If aValue is a
-   * freshly-initialized value the resulting string will be empty.
-   *
-   * @param       aValue   The nsSMILValue to be converted into a string.
-   * @param [out] aString  The string to be populated with the given value.
+   * Returns whether the declaration changed.
    */
-  static void ValueToString(const nsSMILValue& aValue, nsAString& aString);
+  static bool SetPropertyValues(const nsSMILValue&, mozilla::DeclarationBlock&);
 
   /**
    * Return the CSS property animated by the specified value.
    *
    * @param   aValue   The nsSMILValue to examine.
    * @return           The nsCSSPropertyID enum value of the property animated
    *                   by |aValue|, or eCSSProperty_UNKNOWN if the type of
    *                   |aValue| is not nsSMILCSSValueType.
rename from js/src/devtools/automation/tsan-sighandlers.txt
rename to js/src/devtools/automation/tsan-slow.txt
--- a/js/src/devtools/automation/tsan-sighandlers.txt
+++ b/js/src/devtools/automation/tsan-slow.txt
@@ -1,14 +1,4 @@
-# Skip tests that rely on wasm signal handlers.
-asm.js/testTimeout1.js
-asm.js/testTimeout2.js
-asm.js/testTimeout3.js
-asm.js/testTimeout4.js
-asm.js/testTimeout5.js
-asm.js/testTimeout6.js
-ion/iloop.js
-wasm/timeout
-
 # Skip tests that run too slowly under tsan.
 basic/spread-call-maxarg.js
 basic/spread-call-near-maxarg.js
 arrays/too-long-array-splice.js
--- a/js/src/devtools/automation/variants/tsan
+++ b/js/src/devtools/automation/variants/tsan
@@ -1,12 +1,12 @@
 {
     "configure-args": "--enable-debug-symbols='-gline-tables-only' --disable-jemalloc --enable-thread-sanitizer",
     "optimize": true,
     "debug": false,
     "compiler": "clang",
     "env": {
         "LLVM_SYMBOLIZER": "{TOOLTOOL_CHECKOUT}/clang/bin/llvm-symbolizer",
-        "JITTEST_EXTRA_ARGS": "--jitflags=tsan --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt --unusable-error-status --exclude-from={DIR}/tsan-sighandlers.txt",
+        "JITTEST_EXTRA_ARGS": "--jitflags=tsan --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt --unusable-error-status --exclude-from={DIR}/tsan-slow.txt",
         "JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/cgc-jstests-slow.txt"
     },
     "use_minidump": false
 }
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2587,17 +2587,18 @@ RestyleManager::ProcessPostTraversal(
   Element* aElement,
   ComputedStyle* aParentContext,
   ServoRestyleState& aRestyleState,
   ServoPostTraversalFlags aFlags)
 {
   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
 
-  MOZ_ASSERT(aElement->HasServoData(), "How in the world?");
+  MOZ_DIAGNOSTIC_ASSERT(aElement->HasServoData(),
+                        "Element without Servo data on a post-traversal? How?");
 
   // NOTE(emilio): This is needed because for table frames the bit is set on the
   // table wrapper (which is the primary frame), not on the table itself.
   const bool isOutOfFlow =
     primaryFrame &&
     primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
 
   // Grab the change hint from Servo.
--- a/layout/reftests/svg/smil/style/reftest.list
+++ b/layout/reftests/svg/smil/style/reftest.list
@@ -111,28 +111,28 @@ fails == anim-css-strokedasharray-1.svg 
 == anim-css-strokewidth-1-from-to-no-no.svg   anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-to-no-no.svg        anim-css-strokewidth-1-ref.svg
 
 # 'stroke-width' property, from/by/to with percent values
 # XXXdholbert the mixed pct + px tests fail right now, because we need calc()
 # in order to interpolate between pct and non-pct values, and we don't yet
 # support calc() for stroke-width & other SVG-specific properties. (Bug 1258270
 # for gecko, Bug 1386967 for stylo)
-== anim-css-strokewidth-1-by-pct-pct.svg      anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-by-pct-px.svg       anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-by-px-pct.svg       anim-css-strokewidth-1-ref.svg
-== anim-css-strokewidth-1-from-by-pct-pct.svg anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-from-by-pct-px.svg  anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-from-by-px-pct.svg  anim-css-strokewidth-1-ref.svg
-== anim-css-strokewidth-1-from-to-pct-pct.svg anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-from-to-pct-px.svg  anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-from-to-px-pct.svg  anim-css-strokewidth-1-ref.svg
-== anim-css-strokewidth-1-to-pct-pct.svg      anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-to-pct-px.svg       anim-css-strokewidth-1-ref.svg
-fails == anim-css-strokewidth-1-to-px-pct.svg       anim-css-strokewidth-1-ref.svg
+== anim-css-strokewidth-1-by-pct-pct.svg                          anim-css-strokewidth-1-ref.svg
+fails == anim-css-strokewidth-1-by-pct-px.svg                     anim-css-strokewidth-1-ref.svg
+fails == anim-css-strokewidth-1-by-px-pct.svg                     anim-css-strokewidth-1-ref.svg
+fails-if(webrender) == anim-css-strokewidth-1-from-by-pct-pct.svg anim-css-strokewidth-1-ref.svg # bug 1459418
+fails == anim-css-strokewidth-1-from-by-pct-px.svg                anim-css-strokewidth-1-ref.svg
+fails == anim-css-strokewidth-1-from-by-px-pct.svg                anim-css-strokewidth-1-ref.svg
+fails-if(webrender) == anim-css-strokewidth-1-from-to-pct-pct.svg anim-css-strokewidth-1-ref.svg # bug 1459418
+fails == anim-css-strokewidth-1-from-to-pct-px.svg                anim-css-strokewidth-1-ref.svg
+fails == anim-css-strokewidth-1-from-to-px-pct.svg                anim-css-strokewidth-1-ref.svg
+fails-if(webrender) == anim-css-strokewidth-1-to-pct-pct.svg      anim-css-strokewidth-1-ref.svg # bug 1459418
+fails == anim-css-strokewidth-1-to-pct-px.svg                     anim-css-strokewidth-1-ref.svg
+fails == anim-css-strokewidth-1-to-px-pct.svg                     anim-css-strokewidth-1-ref.svg
 
 # 'stroke-width' property, from/by/to with em values
 == anim-css-strokewidth-1-by-px-em.svg        anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-by-em-em.svg        anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-by-em-px.svg        anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-from-by-px-em.svg   anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-from-by-em-em.svg   anim-css-strokewidth-1-ref.svg
 == anim-css-strokewidth-1-from-by-em-px.svg   anim-css-strokewidth-1-ref.svg
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -481,20 +481,16 @@ SERVO_BINDING_FUNC(Servo_AnimationValues
                    RawServoAnimationValueBorrowed value_to_match)
 SERVO_BINDING_FUNC(Servo_AnimationValues_ComputeDistance, double,
                    RawServoAnimationValueBorrowed from,
                    RawServoAnimationValueBorrowed to)
 SERVO_BINDING_FUNC(Servo_AnimationValue_Serialize, void,
                    RawServoAnimationValueBorrowed value,
                    nsCSSPropertyID property,
                    nsAString* buffer)
-SERVO_BINDING_FUNC(Servo_Shorthand_AnimationValues_Serialize, void,
-                   nsCSSPropertyID shorthand_property,
-                   RawGeckoServoAnimationValueListBorrowed values,
-                   nsAString* buffer)
 SERVO_BINDING_FUNC(Servo_AnimationValue_GetOpacity, float,
                    RawServoAnimationValueBorrowed value)
 SERVO_BINDING_FUNC(Servo_AnimationValue_Opacity,
                    RawServoAnimationValueStrong,
                    float)
 SERVO_BINDING_FUNC(Servo_AnimationValue_GetTransform, void,
                    RawServoAnimationValueBorrowed value,
                    RefPtr<nsCSSValueSharedList>* list)
@@ -552,16 +548,19 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetProperty, bool,
                    RawServoDeclarationBlockBorrowed declarations,
                    const nsACString* property,
                    const nsACString* value, bool is_important,
                    RawGeckoURLExtraData* data,
                    mozilla::ParsingMode parsing_mode,
                    nsCompatibility quirks_mode,
                    mozilla::css::Loader* loader)
+SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetPropertyToAnimationValue, bool,
+                   RawServoDeclarationBlockBorrowed declarations,
+                   RawServoAnimationValueBorrowed animation_value)
 SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetPropertyById, bool,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsCSSPropertyID property,
                    const nsACString* value, bool is_important,
                    RawGeckoURLExtraData* data,
                    mozilla::ParsingMode parsing_mode,
                    nsCompatibility quirks_mode,
                    mozilla::css::Loader* loader)
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -113,102 +113,76 @@ AnimationValue::operator!=(const Animati
 {
   return !operator==(aOther);
 }
 
 float
 AnimationValue::GetOpacity() const
 {
   MOZ_ASSERT(mServo);
-  if (mServo) {
-    return Servo_AnimationValue_GetOpacity(mServo);
-  }
-  MOZ_CRASH("old style system disabled");
+  return Servo_AnimationValue_GetOpacity(mServo);
 }
 
 already_AddRefed<const nsCSSValueSharedList>
 AnimationValue::GetTransformList() const
 {
   MOZ_ASSERT(mServo);
 
   RefPtr<nsCSSValueSharedList> transform;
-  if (mServo) {
-    Servo_AnimationValue_GetTransform(mServo, &transform);
-  } else {
-    MOZ_CRASH("old style system disabled");
-  }
+  Servo_AnimationValue_GetTransform(mServo, &transform);
   return transform.forget();
 }
 
 Size
 AnimationValue::GetScaleValue(const nsIFrame* aFrame) const
 {
   MOZ_ASSERT(mServo);
-
-  if (mServo) {
-    RefPtr<nsCSSValueSharedList> list;
-    Servo_AnimationValue_GetTransform(mServo, &list);
-    return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
-  }
-  MOZ_CRASH("old style system disabled");
+  RefPtr<nsCSSValueSharedList> list;
+  Servo_AnimationValue_GetTransform(mServo, &list);
+  return nsStyleTransformMatrix::GetScaleValue(list, aFrame);
 }
 
 void
 AnimationValue::SerializeSpecifiedValue(nsCSSPropertyID aProperty,
                                         nsAString& aString) const
 {
   MOZ_ASSERT(mServo);
-
-  if (mServo) {
-    Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
-    return;
-  }
-
-  MOZ_CRASH("old style system disabled");
+  Servo_AnimationValue_Serialize(mServo, aProperty, &aString);
 }
 
 bool
 AnimationValue::IsInterpolableWith(nsCSSPropertyID aProperty,
                                    const AnimationValue& aToValue) const
 {
   if (IsNull() || aToValue.IsNull()) {
     return false;
   }
 
   MOZ_ASSERT(mServo);
   MOZ_ASSERT(aToValue.mServo);
-
-  if (mServo) {
-    return Servo_AnimationValues_IsInterpolable(mServo, aToValue.mServo);
-  }
-
-  MOZ_CRASH("old style system disabled");
+  return Servo_AnimationValues_IsInterpolable(mServo, aToValue.mServo);
 }
 
 double
 AnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
                                 const AnimationValue& aOther,
                                 ComputedStyle* aComputedStyle) const
 {
   if (IsNull() || aOther.IsNull()) {
     return 0.0;
   }
 
   MOZ_ASSERT(mServo);
   MOZ_ASSERT(aOther.mServo);
 
-  double distance= 0.0;
-  if (mServo) {
-    distance = Servo_AnimationValues_ComputeDistance(mServo, aOther.mServo);
-    return distance < 0.0
-           ? 0.0
-           : distance;
-  }
-
-  MOZ_CRASH("old style system disabled");
+  double distance =
+    Servo_AnimationValues_ComputeDistance(mServo, aOther.mServo);
+  return distance < 0.0
+         ? 0.0
+         : distance;
 }
 
 /* static */ AnimationValue
 AnimationValue::FromString(nsCSSPropertyID aProperty,
                            const nsAString& aValue,
                            Element* aElement)
 {
   MOZ_ASSERT(aElement);
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -76,18 +76,17 @@ struct AnimationValue
     return !mServo;
   }
 
   float GetOpacity() const;
 
   // Return the transform list as a RefPtr.
   already_AddRefed<const nsCSSValueSharedList> GetTransformList() const;
 
-  // Return the scale for mGecko or mServo, which are calculated with
-  // reference to aFrame.
+  // Return the scale for mServo, which is calculated with reference to aFrame.
   mozilla::gfx::Size GetScaleValue(const nsIFrame* aFrame) const;
 
   // Uncompute this AnimationValue and then serialize it.
   void SerializeSpecifiedValue(nsCSSPropertyID aProperty,
                                nsAString& aString) const;
 
   // Check if |*this| and |aToValue| can be interpolated.
   bool IsInterpolableWith(nsCSSPropertyID aProperty,
@@ -111,26 +110,16 @@ struct AnimationValue
   static AnimationValue Opacity(float aOpacity);
   // Create an AnimationValue from a transform list.
   static AnimationValue Transform(nsCSSValueSharedList& aList);
 
   static already_AddRefed<nsCSSValue::Array>
   AppendTransformFunction(nsCSSKeyword aTransformFunction,
                           nsCSSValueList**& aListTail);
 
-  // mGecko and mServo are mutually exclusive: only one or the other should
-  // ever be set.
-  // FIXME: After obsoleting StyleAnimationValue, we should remove mGecko, and
-  // make AnimationValue a wrapper of RawServoAnimationValue to hide these
-  // FFIs.
-  // Ideally we would use conditional compilation based on MOZ_OLD_STYLE in the
-  // Servo code that wants to initialize mGecko, but that seems tricky.  So for
-  // now, just define a dummy member variable that its initialization code will
-  // work on, even when the old style system is compiled out.
-  uintptr_t mGecko;
   RefPtr<RawServoAnimationValue> mServo;
 };
 
 struct PropertyStyleAnimationValuePair
 {
   nsCSSPropertyID mProperty;
   AnimationValue mValue;
 };
--- a/layout/style/nsDOMCSSAttrDeclaration.cpp
+++ b/layout/style/nsDOMCSSAttrDeclaration.cpp
@@ -9,16 +9,17 @@
 #include "nsDOMCSSAttrDeclaration.h"
 
 #include "mozilla/DeclarationBlock.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/ServoDeclarationBlock.h"
+#include "mozAutoDocUpdate.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsIURI.h"
 #include "nsNodeUtils.h"
 #include "nsSMILCSSValueType.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIFrame.h"
 #include "ActiveLayerTracker.h"
@@ -167,29 +168,30 @@ nsDOMCSSAttributeDeclaration::GetServoCS
   };
 }
 
 nsresult
 nsDOMCSSAttributeDeclaration::SetSMILValue(const nsCSSPropertyID aPropID,
                                            const nsSMILValue& aValue)
 {
   MOZ_ASSERT(mIsSMILOverride);
-
-  // Convert nsSMILValue to string.
-  //
-  // FIXME(emilio): This roundtrip should go away.
-  nsAutoString valStr;
-  nsSMILCSSValueType::ValueToString(aValue, valStr);
-
-  nsAutoString oldValStr;
-  GetPropertyValue(aPropID, oldValStr);
-  if (valStr.Equals(oldValStr)) {
-    return NS_OK;
+  // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits,
+  // since we're in a SMIL animation anyway, no need to try to detect we're a
+  // scripted animation.
+  DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify);
+  if (!olddecl) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
-  return SetPropertyValue(aPropID, valStr, nullptr);
+  mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
+  RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
+  bool changed = nsSMILCSSValueType::SetPropertyValues(aValue, *decl);
+  if (changed) {
+    SetCSSDeclaration(decl);
+  }
+  return NS_OK;
 }
 
 nsresult
 nsDOMCSSAttributeDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID,
                                                const nsAString& aValue,
                                                nsIPrincipal* aSubjectPrincipal)
 {
   // Scripted modifications to style.opacity or style.transform
--- a/servo/components/layout/animation.rs
+++ b/servo/components/layout/animation.rs
@@ -34,24 +34,24 @@ pub fn update_animation_state<E>(
     timer: &Timer,
 )
 where
     E: TElement,
 {
     let mut new_running_animations = vec![];
     while let Ok(animation) = new_animations_receiver.try_recv() {
         let mut should_push = true;
-        if let Animation::Keyframes(ref node, ref name, ref state) = animation {
+        if let Animation::Keyframes(ref node, _, ref name, ref state) = animation {
             // If the animation was already present in the list for the
             // node, just update its state, else push the new animation to
             // run.
             if let Some(ref mut animations) = running_animations.get_mut(node) {
                 // TODO: This being linear is probably not optimal.
                 for anim in animations.iter_mut() {
-                    if let Animation::Keyframes(_, ref anim_name, ref mut anim_state) = *anim {
+                    if let Animation::Keyframes(_, _, ref anim_name, ref mut anim_state) = *anim {
                         if *name == *anim_name {
                             debug!("update_animation_state: Found other animation {}", name);
                             anim_state.update_from_other(&state, timer);
                             should_push = false;
                             break;
                         }
                     }
                 }
@@ -78,17 +78,17 @@ where
     let mut keys_to_remove = vec![];
     for (key, running_animations) in running_animations.iter_mut() {
         let mut animations_still_running = vec![];
         for mut running_animation in running_animations.drain(..) {
             let still_running = !running_animation.is_expired() && match running_animation {
                 Animation::Transition(_, started_at, ref frame, _expired) => {
                     now < started_at + frame.duration
                 }
-                Animation::Keyframes(_, _, ref mut state) => {
+                Animation::Keyframes(_, _, _, ref mut state) => {
                     // This animation is still running, or we need to keep
                     // iterating.
                     now < state.started_at + state.duration || state.tick()
                 }
             };
 
             if still_running {
                 animations_still_running.push(running_animation);
--- a/servo/components/layout/generated_content.rs
+++ b/servo/components/layout/generated_content.rs
@@ -267,37 +267,37 @@ impl<'a, 'b> ResolveGeneratedContentFrag
         }
 
         // Truncate down counters.
         for (_, counter) in &mut self.traversal.counters {
             counter.truncate_to_level(self.level);
         }
         self.traversal.list_item.truncate_to_level(self.level);
 
-        for &(ref counter_name, value) in &*fragment.style().get_counters().counter_reset {
-            let counter_name = &*counter_name.0;
+        for pair in &*fragment.style().get_counters().counter_reset {
+            let counter_name = &*pair.name.0;
             if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
-                 counter.reset(self.level, value);
+                 counter.reset(self.level, pair.value);
                  continue
             }
 
             let mut counter = Counter::new();
-            counter.reset(self.level, value);
+            counter.reset(self.level, pair.value);
             self.traversal.counters.insert(counter_name.to_owned(), counter);
         }
 
-        for &(ref counter_name, value) in &*fragment.style().get_counters().counter_increment {
-            let counter_name = &*counter_name.0;
+        for pair in &*fragment.style().get_counters().counter_increment {
+            let counter_name = &*pair.name.0;
             if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
-                counter.increment(self.level, value);
+                counter.increment(self.level, pair.value);
                 continue
             }
 
             let mut counter = Counter::new();
-            counter.increment(self.level, value);
+            counter.increment(self.level, pair.value);
             self.traversal.counters.insert(counter_name.to_owned(), counter);
         }
 
         self.incremented = true
     }
 
     fn quote(&self, style: &ComputedValues, close: bool) -> String {
         let quotes = &style.get_list().quotes;
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -1,28 +1,28 @@
 /* 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/. */
 
 //! CSS transitions and animations.
-#![deny(missing_docs)]
 
 use Atom;
 use bezier::Bezier;
 use context::SharedStyleContext;
 use dom::{OpaqueNode, TElement};
 use font_metrics::FontMetricsProvider;
 use properties::{self, CascadeFlags, ComputedValues, LonghandId};
 use properties::animated_properties::{AnimatedProperty, TransitionProperty};
 use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
 use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
 use rule_tree::CascadeLevel;
 use servo_arc::Arc;
+use std::fmt;
 use std::sync::mpsc::Sender;
-use stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
+use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
 use timer::Timer;
 use values::computed::Time;
 use values::computed::transform::TimingFunction;
 use values::generics::box_::AnimationIterationCount;
 use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
 
 /// This structure represents a keyframes animation current iteration state.
 ///
@@ -47,17 +47,17 @@ pub enum KeyframesRunningState {
     /// This animation is actually running.
     Running,
 }
 
 /// This structure represents the current keyframe animation state, i.e., the
 /// duration, the current and maximum iteration count, and the state (either
 /// playing or paused).
 // TODO: unify the use of f32/f64 in this file.
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 pub struct KeyframesAnimationState {
     /// The time this animation started at.
     pub started_at: f64,
     /// The duration of this animation.
     pub duration: f64,
     /// The delay of the animation.
     pub delay: f64,
     /// The current iteration state for the animation.
@@ -178,65 +178,83 @@ impl KeyframesAnimationState {
     fn is_paused(&self) -> bool {
         match self.running_state {
             KeyframesRunningState::Paused(..) => true,
             KeyframesRunningState::Running => false,
         }
     }
 }
 
+impl fmt::Debug for KeyframesAnimationState {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("KeyframesAnimationState")
+            .field("started_at", &self.started_at)
+            .field("duration", &self.duration)
+            .field("delay", &self.delay)
+            .field("iteration_state", &self.iteration_state)
+            .field("running_state", &self.running_state)
+            .field("direction", &self.direction)
+            .field("current_direction", &self.current_direction)
+            .field("expired", &self.expired)
+            .field("cascade_style", &())
+            .finish()
+    }
+}
+
 /// State relating to an animation.
 #[derive(Clone, Debug)]
 pub enum Animation {
     /// A transition is just a single frame triggered at a time, with a reflow.
     ///
     /// the f64 field is the start time as returned by `time::precise_time_s()`.
     ///
     /// The `bool` field is werther this animation should no longer run.
     Transition(OpaqueNode, f64, AnimationFrame, bool),
     /// A keyframes animation is identified by a name, and can have a
     /// node-dependent state (i.e. iteration count, etc.).
-    Keyframes(OpaqueNode, Atom, KeyframesAnimationState),
+    ///
+    /// TODO(emilio): The animation object could be refcounted.
+    Keyframes(OpaqueNode, KeyframesAnimation, Atom, KeyframesAnimationState),
 }
 
 impl Animation {
     /// Mark this animation as expired.
     #[inline]
     pub fn mark_as_expired(&mut self) {
         debug_assert!(!self.is_expired());
         match *self {
             Animation::Transition(_, _, _, ref mut expired) => *expired = true,
-            Animation::Keyframes(_, _, ref mut state) => state.expired = true,
+            Animation::Keyframes(_, _, _, ref mut state) => state.expired = true,
         }
     }
 
     /// Whether this animation is expired.
     #[inline]
     pub fn is_expired(&self) -> bool {
         match *self {
             Animation::Transition(_, _, _, expired) => expired,
-            Animation::Keyframes(_, _, ref state) => state.expired,
+            Animation::Keyframes(_, _, _, ref state) => state.expired,
         }
     }
 
     /// The opaque node that owns the animation.
     #[inline]
     pub fn node(&self) -> &OpaqueNode {
         match *self {
             Animation::Transition(ref node, _, _, _) => node,
-            Animation::Keyframes(ref node, _, _) => node,
+            Animation::Keyframes(ref node, _, _, _) => node,
         }
     }
 
     /// Whether this animation is paused. A transition can never be paused.
     #[inline]
     pub fn is_paused(&self) -> bool {
         match *self {
             Animation::Transition(..) => false,
-            Animation::Keyframes(_, _, ref state) => state.is_paused(),
+            Animation::Keyframes(_, _, _, ref state) => state.is_paused(),
         }
     }
 
     /// Whether this animation is a transition.
     #[inline]
     pub fn is_transition(&self) -> bool {
         match *self {
             Animation::Transition(..) => true,
@@ -385,17 +403,16 @@ impl PropertyAnimation {
     pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
         self.property.has_the_same_end_value_as(&other.property)
     }
 }
 
 /// Inserts transitions into the queue of running animations as applicable for
 /// the given style difference. This is called from the layout worker threads.
 /// Returns true if any animations were kicked off and false otherwise.
-#[cfg(feature = "servo")]
 pub fn start_transitions_if_applicable(
     new_animations_sender: &Sender<Animation>,
     opaque_node: OpaqueNode,
     old_style: &ComputedValues,
     new_style: &mut Arc<ComputedValues>,
     timer: &Timer,
     possibly_expired_animations: &[PropertyAnimation],
 ) -> bool {
@@ -495,39 +512,43 @@ where
             );
             computed
         },
     }
 }
 
 /// Triggers animations for a given node looking at the animation property
 /// values.
-pub fn maybe_start_animations(
+pub fn maybe_start_animations<E>(
+    element: E,
     context: &SharedStyleContext,
     new_animations_sender: &Sender<Animation>,
     node: OpaqueNode,
     new_style: &Arc<ComputedValues>,
-) -> bool {
+) -> bool
+where
+    E: TElement,
+{
     let mut had_animations = false;
 
     let box_style = new_style.get_box();
     for (i, name) in box_style.animation_name_iter().enumerate() {
         let name = if let Some(atom) = name.as_atom() {
             atom
         } else {
             continue;
         };
 
         debug!("maybe_start_animations: name={}", name);
         let total_duration = box_style.animation_duration_mod(i).seconds();
         if total_duration == 0. {
             continue;
         }
 
-        if let Some(ref anim) = context.stylist.get_animation(name) {
+        if let Some(anim) = context.stylist.get_animation(name, element) {
             debug!("maybe_start_animations: animation {} found", name);
 
             // If this animation doesn't have any keyframe, we can just continue
             // without submitting it to the compositor, since both the first and
             // the second keyframes would be synthetised from the computed
             // values.
             if anim.steps.is_empty() {
                 continue;
@@ -556,16 +577,17 @@ pub fn maybe_start_animations(
             let running_state = match box_style.animation_play_state_mod(i) {
                 AnimationPlayState::Paused => KeyframesRunningState::Paused(0.),
                 AnimationPlayState::Running => KeyframesRunningState::Running,
             };
 
             new_animations_sender
                 .send(Animation::Keyframes(
                     node,
+                    anim.clone(),
                     name.clone(),
                     KeyframesAnimationState {
                         started_at: animation_start,
                         duration: duration as f64,
                         delay: delay as f64,
                         iteration_state: iteration_state,
                         running_state: running_state,
                         direction: animation_direction,
@@ -600,16 +622,17 @@ pub fn update_style_for_animation_frame(
     }
 
     frame
         .property_animation
         .update(Arc::make_mut(&mut new_style), progress);
 
     true
 }
+
 /// Updates a single animation and associated style based on the current time.
 pub fn update_style_for_animation<E>(
     context: &SharedStyleContext,
     animation: &Animation,
     style: &mut Arc<ComputedValues>,
     font_metrics_provider: &FontMetricsProvider,
 ) where
     E: TElement,
@@ -623,37 +646,29 @@ pub fn update_style_for_animation<E>(
             let now = context.timer.seconds();
             let mut new_style = (*style).clone();
             let updated_style =
                 update_style_for_animation_frame(&mut new_style, now, start_time, frame);
             if updated_style {
                 *style = new_style
             }
         },
-        Animation::Keyframes(_, ref name, ref state) => {
+        Animation::Keyframes(_, ref animation, ref name, ref state) => {
             debug!(
                 "update_style_for_animation: animation found: \"{}\", {:?}",
                 name, state
             );
             let duration = state.duration;
             let started_at = state.started_at;
 
             let now = match state.running_state {
                 KeyframesRunningState::Running => context.timer.seconds(),
                 KeyframesRunningState::Paused(progress) => started_at + duration * progress,
             };
 
-            let animation = match context.stylist.get_animation(name) {
-                None => {
-                    warn!("update_style_for_animation: Animation {:?} not found", name);
-                    return;
-                },
-                Some(animation) => animation,
-            };
-
             debug_assert!(!animation.steps.is_empty());
 
             let maybe_index = style
                 .get_box()
                 .animation_name_iter()
                 .position(|animation_name| Some(name) == animation_name.as_atom());
 
             let index = match maybe_index {
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -98,17 +98,16 @@ extern crate uluru;
 extern crate unicode_bidi;
 #[allow(unused_extern_crates)]
 extern crate unicode_segmentation;
 extern crate void;
 
 #[macro_use]
 mod macros;
 
-#[cfg(feature = "servo")]
 pub mod animation;
 pub mod applicable_declarations;
 #[allow(missing_docs)] // TODO.
 #[cfg(feature = "servo")]
 pub mod attr;
 pub mod author_styles;
 pub mod bezier;
 pub mod bloom;
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -420,16 +420,17 @@ trait PrivateMatchMethods: TElement {
                 &context.thread_local.font_metrics_provider,
             );
         }
 
         let new_animations_sender = &context.thread_local.new_animations_sender;
         let this_opaque = self.as_node().opaque();
         // Trigger any present animations if necessary.
         animation::maybe_start_animations(
+            *self,
             &shared_context,
             new_animations_sender,
             this_opaque,
             &new_values,
         );
 
         // Trigger transitions if necessary. This will reset `new_values` back
         // to its old value if it did trigger a transition.
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -191,34 +191,32 @@ pub fn nscsspropertyid_is_transitionable
             % endif
         % endfor
         _ => false
     }
 }
 
 /// An animated property interpolation between two computed values for that
 /// property.
-#[cfg(feature = "servo")]
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 pub enum AnimatedProperty {
     % for prop in data.longhands:
         % if prop.animatable:
             <%
                 value_type = "longhands::{}::computed_value::T".format(prop.ident)
                 if not prop.is_animatable_with_computed_value:
                     value_type = "<{} as ToAnimatedValue>::AnimatedValue".format(value_type)
             %>
             /// ${prop.name}
             ${prop.camel_case}(${value_type}, ${value_type}),
         % endif
     % endfor
 }
 
-#[cfg(feature = "servo")]
 impl AnimatedProperty {
     /// Get the name of this property.
     pub fn name(&self) -> &'static str {
         match *self {
             % for prop in data.longhands:
                 % if prop.animatable:
                     AnimatedProperty::${prop.camel_case}(..) => "${prop.name}",
                 % endif
@@ -250,19 +248,22 @@ impl AnimatedProperty {
                 % endif
             % endfor
             _ => false,
         }
     }
 
     /// Update `style` with the proper computed style corresponding to this
     /// animation at `progress`.
+    #[cfg_attr(feature = "gecko", allow(unused))]
     pub fn update(&self, style: &mut ComputedValues, progress: f64) {
-        match *self {
-            % for prop in data.longhands:
+        #[cfg(feature = "servo")]
+        {
+            match *self {
+                % for prop in data.longhands:
                 % if prop.animatable:
                     AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
                         // https://drafts.csswg.org/web-animations/#discrete-animation-type
                         % if prop.animation_value_type == "discrete":
                             let value = if progress < 0.5 { from.clone() } else { to.clone() };
                         % else:
                             let value = match from.animate(to, Procedure::Interpolate { progress }) {
                                 Ok(value) => value,
@@ -271,17 +272,18 @@ impl AnimatedProperty {
                         % endif
                         % if not prop.is_animatable_with_computed_value:
                             let value: longhands::${prop.ident}::computed_value::T =
                                 ToAnimatedValue::from_animated_value(value);
                         % endif
                         style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
                     }
                 % endif
-            % endfor
+                % endfor
+            }
         }
     }
 
     /// Get an animatable value from a transition-property, an old style, and a
     /// new style.
     pub fn from_longhand(
         property: LonghandId,
         old_style: &ComputedValues,
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -254,33 +254,33 @@ impl DeepCloneWithLock for Keyframe {
     }
 }
 
 /// A keyframes step value. This can be a synthetised keyframes animation, that
 /// is, one autogenerated from the current computed values, or a list of
 /// declarations to apply.
 ///
 /// TODO: Find a better name for this?
-#[derive(Debug, MallocSizeOf)]
+#[derive(Clone, Debug, MallocSizeOf)]
 pub enum KeyframesStepValue {
     /// A step formed by a declaration block specified by the CSS.
     Declarations {
         /// The declaration block per se.
         #[cfg_attr(feature = "gecko",
                    ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile")]
         #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
         block: Arc<Locked<PropertyDeclarationBlock>>,
     },
     /// A synthetic step computed from the current computed values at the time
     /// of the animation.
     ComputedValues,
 }
 
 /// A single step from a keyframe animation.
-#[derive(Debug, MallocSizeOf)]
+#[derive(Clone, Debug, MallocSizeOf)]
 pub struct KeyframesStep {
     /// The percentage of the animation duration when this step starts.
     pub start_percentage: KeyframePercentage,
     /// Declarations that will determine the final style during the step, or
     /// `ComputedValues` if this is an autogenerated step.
     pub value: KeyframesStepValue,
     /// Wether a animation-timing-function declaration exists in the list of
     /// declarations.
@@ -347,17 +347,17 @@ impl KeyframesStep {
         }
     }
 }
 
 /// This structure represents a list of animation steps computed from the list
 /// of keyframes, in order.
 ///
 /// It only takes into account animable properties.
-#[derive(Debug, MallocSizeOf)]
+#[derive(Clone, Debug, MallocSizeOf)]
 pub struct KeyframesAnimation {
     /// The difference steps of the animation.
     pub steps: Vec<KeyframesStep>,
     /// The properties that change in this animation.
     pub properties_changed: LonghandIdSet,
     /// Vendor prefix type the @keyframes has.
     pub vendor_prefix: Option<VendorPrefix>,
 }
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1445,17 +1445,18 @@ impl Stylist {
         // behavior makes a bit more sense off-hand, but it's way more complex
         // to implement, and it makes value computation having to thread around
         // the cascade level, which is not great. Also, it breaks if you inherit
         // animation-name from an element in a different tree.
         //
         // See [3] for the bug to implement whatever gets resolved, and related
         // bugs for a bit more context.
         //
-        // [1]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/resolver/style_resolver.cc?l=1267&rcl=90f9f8680ebb4a87d177f3b0833372ae4e0c88d8
+        // [1]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/
+        //        core/css/resolver/style_resolver.cc?l=1267&rcl=90f9f8680ebb4a87d177f3b0833372ae4e0c88d8
         // [2]: https://github.com/w3c/csswg-drafts/issues/1995
         // [3]: https://bugzil.la/1458189
         if let Some(shadow) = element.shadow_root() {
             try_find_in!(shadow.style_data());
         }
 
         if let Some(shadow) = element.containing_shadow() {
             try_find_in!(shadow.style_data());
--- a/servo/components/style/values/specified/counters.rs
+++ b/servo/components/style/values/specified/counters.rs
@@ -7,18 +7,18 @@
 #[cfg(feature = "servo")]
 use computed_values::list_style_type::T as ListStyleType;
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use style_traits::{ParseError, StyleParseErrorKind};
 use values::CustomIdent;
 #[cfg(feature = "gecko")]
 use values::generics::CounterStyleOrNone;
+use values::generics::counters::CounterIncrement as GenericCounterIncrement;
 use values::generics::counters::CounterPair;
-use values::generics::counters::CounterIncrement as GenericCounterIncrement;
 use values::generics::counters::CounterReset as GenericCounterReset;
 #[cfg(feature = "gecko")]
 use values::specified::Attr;
 use values::specified::Integer;
 #[cfg(feature = "gecko")]
 use values::specified::url::SpecifiedImageUrl;
 
 /// A specified value for the `counter-increment` property.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -68,17 +68,16 @@ use style::gecko_bindings::bindings::Gec
 use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
 use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
 use style::gecko_bindings::bindings::Gecko_NewNoneTransform;
 use style::gecko_bindings::bindings::RawGeckoAnimationPropertySegmentBorrowed;
 use style::gecko_bindings::bindings::RawGeckoCSSPropertyIDListBorrowed;
 use style::gecko_bindings::bindings::RawGeckoComputedKeyframeValuesListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoComputedTimingBorrowed;
 use style::gecko_bindings::bindings::RawGeckoFontFaceRuleListBorrowedMut;
-use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowed;
 use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
 use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
 use style::gecko_bindings::bindings::RawServoAnimationValueBorrowedOrNull;
 use style::gecko_bindings::bindings::RawServoAnimationValueMapBorrowedMut;
 use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
 use style::gecko_bindings::bindings::RawServoAnimationValueTableBorrowed;
 use style::gecko_bindings::bindings::RawServoDeclarationBlockBorrowedOrNull;
@@ -87,18 +86,18 @@ use style::gecko_bindings::bindings::Raw
 use style::gecko_bindings::bindings::nsCSSValueBorrowedMut;
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{CallerType, CSSPseudoElementType, CompositeOperation};
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{RawServoStyleRule, ComputedStyleStrong, RustString};
+use style::gecko_bindings::structs::{SheetParsingMode, nsAtom, nsCSSPropertyID};
 use style::gecko_bindings::structs::{StyleSheet as DomStyleSheet, SheetLoadData, SheetLoadDataHolder};
-use style::gecko_bindings::structs::{SheetParsingMode, nsAtom, nsCSSPropertyID};
 use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterDesc};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::IterationCompositeOperation;
 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
 use style::gecko_bindings::structs::OriginFlags;
 use style::gecko_bindings::structs::OriginFlags_Author;
 use style::gecko_bindings::structs::OriginFlags_User;
@@ -124,17 +123,17 @@ use style::gecko_bindings::structs::nsre
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties;
 use style::invalidation::element::restyle_hints;
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parser::{Parse, ParserContext, self};
 use style::properties::{ComputedValues, DeclarationSource, Importance};
-use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
+use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
 use style::properties::{PropertyDeclarationId, ShorthandId};
 use style::properties::{SourcePropertyDeclaration, StyleBuilder};
 use style::properties::{parse_one_declaration_into, parse_style_attribute};
 use style::properties::animated_properties::AnimationValue;
 use style::properties::animated_properties::compare_property_priority;
 use style::rule_cache::RuleCacheConditions;
 use style::rule_tree::{CascadeLevel, StrongRuleNode};
 use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
@@ -704,41 +703,16 @@ pub extern "C" fn Servo_AnimationValue_S
     let buffer = unsafe { buffer.as_mut().unwrap() };
     let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
         .single_value_to_css(&get_property_id_from_nscsspropertyid!(property, ()), buffer,
                              None, None /* No extra custom properties */);
     debug_assert!(rv.is_ok());
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn Servo_Shorthand_AnimationValues_Serialize(
-    shorthand_property: nsCSSPropertyID,
-    values: RawGeckoServoAnimationValueListBorrowed,
-    buffer: *mut nsAString,
-) {
-    let property_id = get_property_id_from_nscsspropertyid!(shorthand_property, ());
-    let shorthand = match property_id.as_shorthand() {
-        Ok(shorthand) => shorthand,
-        _ => return,
-    };
-
-    // Convert RawServoAnimationValue(s) into a vector of PropertyDeclaration
-    // so that we can use reference of the PropertyDeclaration without worrying
-    // about its lifetime. (longhands_to_css() expects &PropertyDeclaration
-    // iterator.)
-    let declarations: Vec<PropertyDeclaration> =
-        values.iter().map(|v| AnimationValue::as_arc(&&*v.mRawPtr).uncompute()).collect();
-
-    let _ = shorthand.longhands_to_css(
-        declarations.iter(),
-        &mut CssWriter::new(&mut *buffer),
-    );
-}
-
-#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_GetOpacity(
     value: RawServoAnimationValueBorrowed,
 ) -> f32 {
     let value = AnimationValue::as_arc(&value);
     if let AnimationValue::Opacity(opacity) = **value {
         opacity
     } else {
         panic!("The AnimationValue should be Opacity");
@@ -3605,16 +3579,30 @@ pub unsafe extern "C" fn Servo_Declarati
         data,
         parsing_mode,
         quirks_mode.into(),
         loader,
     )
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
+    declarations: RawServoDeclarationBlockBorrowed,
+    animation_value: RawServoAnimationValueBorrowed,
+) -> bool {
+    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+        decls.push(
+            AnimationValue::as_arc(&animation_value).uncompute(),
+            Importance::Normal,
+            DeclarationSource::CssOm,
+        )
+    })
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyById(
     declarations: RawServoDeclarationBlockBorrowed,
     property: nsCSSPropertyID,
     value: *const nsACString,
     is_important: bool,
     data: *mut URLExtraData,
     parsing_mode: structs::ParsingMode,
     quirks_mode: nsCompatibility,
@@ -4501,17 +4489,16 @@ impl<'a> Iterator for PrioritizedPropert
 #[no_mangle]
 pub extern "C" fn Servo_GetComputedKeyframeValues(
     keyframes: RawGeckoKeyframeListBorrowed,
     element: RawGeckoElementBorrowed,
     style: ComputedStyleBorrowed,
     raw_data: RawServoStyleSetBorrowed,
     computed_keyframes: RawGeckoComputedKeyframeValuesListBorrowedMut
 ) {
-    use std::mem;
     use style::properties::LonghandIdSet;
 
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
     let metrics = get_metrics_provider_for_product();
 
     let element = GeckoElement(element);
     let parent_element = element.inheritance_parent();
     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
@@ -4562,20 +4549,16 @@ pub extern "C" fn Servo_GetComputedKeyfr
                 if seen.contains(property) {
                     return;
                 }
                 seen.insert(property);
 
                 // This is safe since we immediately write to the uninitialized values.
                 unsafe { animation_values.set_len((property_index + 1) as u32) };
                 animation_values[property_index].mProperty = property.to_nscsspropertyid();
-                // We only make sure we have enough space for this variable,
-                // but didn't construct a default value for StyleAnimationValue,
-                // so we should zero it to avoid getting undefined behaviors.
-                animation_values[property_index].mValue.mGecko = unsafe { mem::zeroed() };
                 match value {
                     Some(v) => {
                         animation_values[property_index].mValue.mServo.set_arc_leaky(Arc::new(v));
                     },
                     None => {
                         animation_values[property_index].mValue.mServo.mRawPtr = ptr::null_mut();
                     },
                 }
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -918,36 +918,9 @@ mod shorthand_serialization {
             let shadow_decl = BoxShadowList(vec![shadow_val]);
             properties.push(PropertyDeclaration::BoxShadow(shadow_decl));
             let shadow_css = "box-shadow: 1px 2px 3px 4px;";
             let shadow = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), shadow_css).unwrap();
 
             assert_eq!(shadow.to_css_string(), shadow_css);
         }
     }
-
-    mod counter_increment {
-        pub use super::*;
-        pub use style::properties::longhands::counter_increment::SpecifiedValue as CounterIncrement;
-        use style::values::specified::Integer;
-
-        #[test]
-        fn counter_increment_with_properties_should_serialize_correctly() {
-            let mut properties = Vec::new();
-
-            properties.push((CustomIdent("counter1".into()), Integer::new(1)));
-            properties.push((CustomIdent("counter2".into()), Integer::new(-4)));
-
-            let counter_increment = CounterIncrement::new(properties);
-            let counter_increment_css = "counter1 1 counter2 -4";
-
-            assert_eq!(counter_increment.to_css_string(), counter_increment_css);
-        }
-
-        #[test]
-        fn counter_increment_without_properties_should_serialize_correctly() {
-            let counter_increment = CounterIncrement::new(Vec::new());
-            let counter_increment_css = "none";
-
-            assert_eq!(counter_increment.to_css_string(), counter_increment_css);
-        }
-    }
 }
--- a/toolkit/components/telemetry/gen_event_data.py
+++ b/toolkit/components/telemetry/gen_event_data.py
@@ -133,17 +133,17 @@ def generate_JSON_definitions(output, *f
 
         if category not in event_definitions:
             event_definitions[category] = OrderedDict()
 
         event_definitions[category][event.name] = OrderedDict({
             'methods': event.methods,
             'objects': event.objects,
             'extra_keys': event.extra_keys,
-            'record_on_release': True if event.dataset == 'opt-out' else False,
+            'record_on_release': True if event.dataset_short == 'opt-out' else False,
             # We don't expire dynamic-builtin scalars: they're only meant for
             # use in local developer builds anyway. They will expire when rebuilding.
             'expired': False,
         })
 
     json.dump(event_definitions, output)
 
 
--- a/toolkit/components/telemetry/gen_scalar_data.py
+++ b/toolkit/components/telemetry/gen_scalar_data.py
@@ -105,17 +105,17 @@ def generate_JSON_definitions(output, *f
         category = scalar.category
 
         if category not in scalar_definitions:
             scalar_definitions[category] = OrderedDict()
 
         scalar_definitions[category][scalar.name] = OrderedDict({
             'kind': scalar.nsITelemetry_kind,
             'keyed': scalar.keyed,
-            'record_on_release': True if scalar.dataset == 'opt-out' else False,
+            'record_on_release': True if scalar.dataset_short == 'opt-out' else False,
             # We don't expire dynamic-builtin scalars: they're only meant for
             # use in local developer builds anyway. They will expire when rebuilding.
             'expired': False,
         })
 
     json.dump(scalar_definitions, output)
 
 
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -266,23 +266,31 @@ class EventData:
             return m + '_' + o
         combinations = itertools.product(self.methods, self.objects)
         return [enum(t[0], t[1]) for t in combinations]
 
     @property
     def dataset(self):
         """Get the nsITelemetry constant equivalent for release_channel_collection.
         """
-        rcc = self._definition.get('release_channel_collection', 'opt-in')
+        rcc = self.dataset_short
         if rcc == 'opt-out':
             return 'nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT'
         else:
             return 'nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN'
 
     @property
+    def dataset_short(self):
+        """Get the short name of the chosen release channel collection policy for the event.
+        """
+        # The collection policy is optional, but we still define a default
+        # behaviour for it.
+        return self._definition.get('release_channel_collection', 'opt-in')
+
+    @property
     def extra_keys(self):
         return self._definition.get('extra_keys', {}).keys()
 
 
 def load_events(filename, strict_type_checks):
     """Parses a YAML file containing the event definitions.
 
     :param filename: the YAML file containing the event definitions.
--- a/toolkit/components/telemetry/parse_scalars.py
+++ b/toolkit/components/telemetry/parse_scalars.py
@@ -271,29 +271,35 @@ class ScalarType:
 
     @property
     def record_in_processes_enum(self):
         """Get the non-empty list of flags representing the processes to record data in"""
         return [utils.process_name_to_enum(p) for p in self.record_in_processes]
 
     @property
     def dataset(self):
-        """Get the nsITelemetry constant equivalent to the chose release channel collection
+        """Get the nsITelemetry constant equivalent to the chosen release channel collection
         policy for the scalar.
         """
-        # The collection policy is optional, but we still define a default
-        # behaviour for it.
-        rcc = self._definition.get('release_channel_collection', 'opt-in')
+        rcc = self.dataset_short
         table = {
             'opt-in': 'OPTIN',
             'opt-out': 'OPTOUT',
         }
         return 'nsITelemetry::DATASET_RELEASE_CHANNEL_' + table[rcc]
 
     @property
+    def dataset_short(self):
+        """Get the short name of the chosen release channel collection policy for the scalar.
+        """
+        # The collection policy is optional, but we still define a default
+        # behaviour for it.
+        return self._definition.get('release_channel_collection', 'opt-in')
+
+    @property
     def cpp_guard(self):
         """Get the cpp guard for this scalar"""
         return self._definition.get('cpp_guard')
 
 
 def load_scalars(filename, strict_type_checks=True):
     """Parses a YAML file containing the scalar definition.
 
--- a/toolkit/components/telemetry/tests/python/python.ini
+++ b/toolkit/components/telemetry/tests/python/python.ini
@@ -1,2 +1,4 @@
+[test_gen_event_data_json.py]
+[test_gen_scalar_data_json.py]
 [test_histogramtools_non_strict.py]
-[test_histogramtools_strict.py]
\ No newline at end of file
+[test_histogramtools_strict.py]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/python/test_gen_event_data_json.py
@@ -0,0 +1,88 @@
+# 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/.
+
+import json
+import mozunit
+import os
+import sys
+import tempfile
+import unittest
+from StringIO import StringIO
+from os import path
+
+TELEMETRY_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
+sys.path.append(TELEMETRY_ROOT_PATH)
+import gen_event_data   # noqa: E402
+
+
+class TestEventDataJson(unittest.TestCase):
+
+    def test_JSON_definitions_generation(self):
+        EVENTS_YAML = """
+with.optout:
+  testme1:
+    objects: ["test1"]
+    bug_numbers: [1456415]
+    notification_emails: ["telemetry-client-dev@mozilla.org"]
+    record_in_processes: ["main"]
+    description: opt-out event
+    release_channel_collection: opt-out
+    expiry_version: never
+    extra_keys:
+      message: a message 1
+with.optin:
+  testme2:
+    objects: ["test2"]
+    bug_numbers: [1456415]
+    notification_emails: ["telemetry-client-dev@mozilla.org"]
+    record_in_processes: ["main"]
+    description: opt-in event
+    release_channel_collection: opt-in
+    expiry_version: never
+    extra_keys:
+      message: a message 2
+        """
+
+        EXPECTED_JSON = {
+            "with.optout": {
+                "testme1": {
+                    "objects": ["test1"],
+                    "expired": False,
+                    "methods": ["testme1"],
+                    "extra_keys": ["message"],
+                    "record_on_release": True
+                }
+            },
+            "with.optin": {
+                "testme2": {
+                    "objects": ["test2"],
+                    "expired": False,
+                    "methods": ["testme2"],
+                    "extra_keys": ["message"],
+                    "record_on_release": False
+                }
+            },
+        }
+
+        io = StringIO()
+        try:
+            tmpfile = tempfile.NamedTemporaryFile(suffix=".json", delete=False)
+            # Write the event definition to the temporary file
+            tmpfile.write(EVENTS_YAML)
+            tmpfile.close()
+
+            # Let the parser generate the artifact definitions
+            gen_event_data.generate_JSON_definitions(io, tmpfile.name)
+        finally:
+            if tmpfile:
+                os.unlink(tmpfile.name)
+
+        event_definitions = json.loads(io.getvalue())
+
+        # Check that it generated the correct data
+        self.assertEqual(EXPECTED_JSON, event_definitions)
+
+
+if __name__ == '__main__':
+    mozunit.main()
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/python/test_gen_scalar_data_json.py
@@ -0,0 +1,83 @@
+# 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/.
+
+import json
+import mozunit
+import os
+import sys
+import tempfile
+import unittest
+from StringIO import StringIO
+from os import path
+
+TELEMETRY_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir, path.pardir))
+sys.path.append(TELEMETRY_ROOT_PATH)
+import gen_scalar_data   # noqa: E402
+
+
+class TestScalarDataJson(unittest.TestCase):
+
+    def test_JSON_definitions_generation(self):
+        SCALARS_YAML = """
+newscalar:
+  withoptin:
+    bug_numbers:
+      - 1456415
+    description: opt-in scalar
+    expires: never
+    kind: uint
+    notification_emails: ["telemetry-client-dev@mozilla.org"]
+    record_in_processes: ["main"]
+    release_channel_collection: opt-in
+    keyed: false
+  withoptout:
+    bug_numbers:
+      - 1456415
+    description: opt-out scalar
+    expires: never
+    kind: string
+    notification_emails: ["telemetry-client-dev@mozilla.org"]
+    record_in_processes: ["main"]
+    release_channel_collection: opt-out
+    keyed: false
+        """
+
+        EXPECTED_JSON = {
+            "newscalar": {
+                "withoptout": {
+                    "kind": "nsITelemetry::SCALAR_TYPE_STRING",
+                    "expired": False,
+                    "record_on_release": True,
+                    "keyed": False
+                },
+                "withoptin": {
+                    "kind": "nsITelemetry::SCALAR_TYPE_COUNT",
+                    "expired": False,
+                    "record_on_release": False,
+                    "keyed": False
+                }
+            }
+        }
+
+        io = StringIO()
+        try:
+            tmpfile = tempfile.NamedTemporaryFile(suffix=".json", delete=False)
+            # Write the scalar definition to the temporary file
+            tmpfile.write(SCALARS_YAML)
+            tmpfile.close()
+
+            # Let the parser generate the artifact definitions
+            gen_scalar_data.generate_JSON_definitions(io, tmpfile.name)
+        finally:
+            if tmpfile:
+                os.unlink(tmpfile.name)
+
+        scalar_definitions = json.loads(io.getvalue())
+
+        # Check that it generated the correct data
+        self.assertEqual(EXPECTED_JSON, scalar_definitions)
+
+
+if __name__ == '__main__':
+    mozunit.main()