Bug 1419221 - Introduce a new argument for getUnanimatedComputedStyle to be able to flush pending styles. r=birtles
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 21 Nov 2017 18:03:17 +0900
changeset 437480 696edf3997a4e70e0502fa7d9a6b8bc2ab0bb22b
parent 437479 b096c6518c9f8715f22178041eeb18e3a86a00d3
child 437481 a850e3bc63717beb66e9202da6d6930c1e7305a3
push id117
push userfmarier@mozilla.com
push dateTue, 28 Nov 2017 20:17:16 +0000
reviewersbirtles
bugs1419221
milestone59.0a1
Bug 1419221 - Introduce a new argument for getUnanimatedComputedStyle to be able to flush pending styles. r=birtles Even with this patch, in Gecko getUnanimatedComputedStyle flushes pendings styles somehow. Also in Stylo the function flushes pending styles if the target element hasn't yet styled or Servo style data has cleared for some reasons (e.g. the element is in display:none subtree). MozReview-Commit-ID: HCizzM0JnFz
devtools/server/actors/animation.js
dom/base/nsDOMWindowUtils.cpp
dom/base/test/file_domwindowutils_animation.html
dom/interfaces/base/nsIDOMWindowUtils.idl
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -504,17 +504,20 @@ var AnimationPlayerActor = protocol.Acto
           let pseudo = null;
           let target = this.player.effect.target;
           if (target.type) {
             // This target is a pseudo element.
             pseudo = target.type;
             target = target.parentElement;
           }
           const value =
-            DOMWindowUtils.getUnanimatedComputedStyle(target, pseudo, property.name);
+            DOMWindowUtils.getUnanimatedComputedStyle(target,
+                                                      pseudo,
+                                                      property.name,
+                                                      DOMWindowUtils.FLUSH_NONE);
           const animationType = DOMWindowUtils.getAnimationTypeForLonghand(property.name);
           underlyingValue = animationType === "float" ? parseFloat(value, 10) : value;
         }
         values.value = underlyingValue;
       });
     }
 
     // Calculate the distance.
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2965,30 +2965,45 @@ nsDOMWindowUtils::GetAnimationTypeForLon
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetUnanimatedComputedStyle(nsIDOMElement* aElement,
                                              const nsAString& aPseudoElement,
                                              const nsAString& aProperty,
+                                             int32_t aFlushType,
                                              nsAString& aResult)
 {
   nsCOMPtr<Element> element = do_QueryInterface(aElement);
   if (!element) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCSSPropertyID propertyID =
     nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
   if (propertyID == eCSSProperty_UNKNOWN ||
       nsCSSProps::IsShorthand(propertyID)) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  switch (aFlushType) {
+    case FLUSH_NONE:
+      break;
+    case FLUSH_STYLE: {
+      nsIDocument* doc = element->GetComposedDoc();
+      if (doc) {
+        doc->FlushPendingNotifications(FlushType::Style);
+      }
+      break;
+    }
+    default:
+      return NS_ERROR_INVALID_ARG;
+  }
+
   nsIPresShell* shell = GetPresShell();
   if (!shell) {
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<nsAtom> pseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElement);
   RefPtr<nsStyleContext> styleContext =
     nsComputedDOMStyle::GetUnanimatedStyleContextNoFlush(element,
--- a/dom/base/test/file_domwindowutils_animation.html
+++ b/dom/base/test/file_domwindowutils_animation.html
@@ -97,30 +97,55 @@ function test_getUnanimatedComputedStyle
       deleteStyle();
     });
   });
 
   const div = document.createElement("div");
   document.body.appendChild(div);
 
   SimpleTest.doesThrow(
-    () => utils.getUnanimatedComputedStyle(div, null, "background"),
+    () => utils.getUnanimatedComputedStyle(div, null, "background", utils.FLUSH_NONE),
     "NS_ERROR_INVALID_ARG",
     "Shorthand property should throw");
 
   SimpleTest.doesThrow(
-    () => utils.getUnanimatedComputedStyle(div, null, "invalid"),
+    () => utils.getUnanimatedComputedStyle(div, null, "invalid", utils.FLUSH_NONE),
     "NS_ERROR_INVALID_ARG",
     "Invalid property should throw");
 
   SimpleTest.doesThrow(
-    () => utils.getUnanimatedComputedStyle(null, null, "opacity"),
+    () => utils.getUnanimatedComputedStyle(null, null, "opacity", utils.FLUSH_NONE),
     "NS_ERROR_INVALID_ARG",
     "Null element should throw");
 
+  SimpleTest.doesThrow(
+    () => utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_LAYOUT),
+    "NS_ERROR_INVALID_ARG",
+    "FLUSH_LAYOUT option should throw");
+
+  SimpleTest.doesThrow(
+    () => utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_DISPLAY),
+    "NS_ERROR_INVALID_ARG",
+    "FLUSH_DISPLAY option should throw");
+
+  if (utils.isStyledByServo) {
+    // Flush styles since getUnanimatedComputedStyle flushes pending styles even
+    // with FLUSH_NONE option if the element hasn't yet styled.
+    getComputedStyle(div).opacity;
+
+    div.style.opacity = "0";
+    is(utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_NONE),
+       "1",
+       "getUnanimatedComputedStyle with FLUSH_NONE should not flush pending styles");
+
+    is(utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_STYLE),
+       "0",
+       "getUnanimatedComputedStyle with FLUSH_STYLE should flush pending styles");
+  }
+
   div.remove();
 
   next();
   window.close();
 }
 
 function checkUnanimatedComputedStyle(property, initialStyle, pseudoType,
                                       expectedBeforeAnimation,
@@ -131,25 +156,25 @@ function checkUnanimatedComputedStyle(pr
 
   if (initialStyle) {
     div.style[property] = initialStyle;
   }
   if (pseudoType) {
     div.classList.add("pseudo");
   }
 
-  is(utils.getUnanimatedComputedStyle(div, pseudoType, property),
+  is(utils.getUnanimatedComputedStyle(div, pseudoType, property, utils.FLUSH_STYLE),
      expectedBeforeAnimation,
      `'${ property }' property with '${ initialStyle }' style `
      + `should be '${ expectedBeforeAnimation }' `
      + `before animating by ${ animationType }`);
 
   const animation = animate(div);
   animation.currentTime = 500;
-  is(utils.getUnanimatedComputedStyle(div, pseudoType, property),
+  is(utils.getUnanimatedComputedStyle(div, pseudoType, property, utils.FLUSH_STYLE),
      expectedDuringAnimation,
      `'${ property }' property with '${ initialStyle }' style `
      + `should be '${ expectedDuringAnimation }' `
      + `even while animating by ${ animationType }`);
 
   div.remove();
 }
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -985,16 +985,17 @@ interface nsIDOMWindowUtils : nsISupport
    */
   void getScrollbarSize(in boolean aFlushLayout, out long aWidth, out long aHeight);
 
   /**
    * Returns the given element's bounds without flushing pending layout changes.
    */
   nsIDOMClientRect getBoundsWithoutFlushing(in nsIDOMElement aElement);
 
+  const long FLUSH_NONE = -1;
   const long FLUSH_STYLE = 0;
   const long FLUSH_LAYOUT = 1;
   const long FLUSH_DISPLAY = 2;
 
   /**
    * Returns true if a flush of the given type is needed.
    */
   bool needsFlush(in long aFlushtype);
@@ -1576,20 +1577,23 @@ interface nsIDOMWindowUtils : nsISupport
   AString getAnimationTypeForLonghand(in AString aProperty);
 
   /**
    * Returns the computed style for the specified property of given pseudo type
    * on the given element after removing styles from declarative animations.
    * @param aElement - A target element
    * @param aPseudoElement - A pseudo type (e.g. '::before' or null)
    * @param aProperty - A longhand CSS property (e.g. 'background-color')
+   * @param aFlushType - FLUSH_NONE if any pending styles should not happen,
+   *                     FLUSH_STYLE to flush pending styles.
    */
   AString getUnanimatedComputedStyle(in nsIDOMElement aElement,
                                      in AString aPseudoElement,
-                                     in AString aProperty);
+                                     in AString aProperty,
+                                     in long aFlushType);
 
   /**
    * Get the type of the currently focused html input, if any.
    */
   readonly attribute string focusedInputType;
 
   /**
    * Find the view ID for a given element. This is the reverse of