Bug 1419221 - Introduce a new argument for getUnanimatedComputedStyle to be able to flush pending styles. r?birtles draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 21 Nov 2017 18:03:17 +0900
changeset 701140 aa9ba5615f8030af3474becabb0cd89694a07f77
parent 701051 1d177f46ed3bab2a6abbeb18c964b2ea1d74e089
child 701144 299d7b9ac6603c523199c061565ef4ef554c6ef7
push id90085
push userhikezoe@mozilla.com
push dateTue, 21 Nov 2017 09:03:43 +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