Bug 1132748 part 1: Add CSSUnprefixingService API for handling prefixed gradient expressions, with stub JS implementation. r=dbaron
authorDaniel Holbert <dholbert@cs.stanford.edu>
Thu, 07 May 2015 09:04:42 -0700
changeset 274192 522d010bd1ff0641fb215423689a56d7d41af92c
parent 274191 3efe5f06818d272bfe1ac853523984416500a9a3
child 274193 cc88f4422a6f18cb8a55d87c18d165e116b7e8f8
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1132748
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1132748 part 1: Add CSSUnprefixingService API for handling prefixed gradient expressions, with stub JS implementation. r=dbaron
layout/style/CSSUnprefixingService.js
layout/style/nsCSSParser.cpp
layout/style/nsICSSUnprefixingService.idl
--- a/layout/style/CSSUnprefixingService.js
+++ b/layout/style/CSSUnprefixingService.js
@@ -146,11 +146,25 @@ CSSUnprefixingService.prototype = {
       aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + newRightHalf;
 
       return true;
     }
 
     // No known mapping for property aPropName.
     return false;
   },
+
+  // See documentation in nsICSSUnprefixingService.idl
+  generateUnprefixedGradientValue: function(aPrefixedFuncName,
+                                            aPrefixedFuncBody,
+                                            aUnprefixedFuncName, /*[out]*/
+                                            aUnprefixedFuncBody /*[out]*/) {
+    // XXX Implement me!
+    // Sample working output:
+    //    aUnprefixedFuncName.value = "radial-gradient";
+    //    aUnprefixedFuncBody.value = "lime, green";
+    //    return true;
+
+    return false;
+  },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CSSUnprefixingService]);
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -731,16 +731,21 @@ protected:
 
   bool ShouldUseUnprefixingService();
   bool ParsePropertyWithUnprefixingService(const nsAString& aPropertyName,
                                            css::Declaration* aDeclaration,
                                            uint32_t aFlags,
                                            bool aMustCallValueAppended,
                                            bool* aChanged,
                                            nsCSSContextType aContext);
+  // When we detect a webkit-prefixed gradient expression, this function can
+  // be used to parse its body into outparam |aValue|. Only call if
+  // ShouldUseUnprefixingService() returns true.
+  bool ParseWebkitPrefixedGradient(nsAString& aPrefixedFuncName,
+                                   nsCSSValue& aValue);
 
   bool ParseProperty(nsCSSProperty aPropID);
   bool ParsePropertyByFunction(nsCSSProperty aPropID);
   bool ParseSingleValueProperty(nsCSSValue& aValue,
                                   nsCSSProperty aPropID);
 
   enum PriorityParsingStatus {
     ePriority_None,
@@ -6680,16 +6685,82 @@ CSSParserImpl::ParsePropertyWithUnprefix
     // We succeeded, so we'll leave the parser pointing at the end of
     // the declaration; don't restore it to the pre-recording position.
     parserStateBeforeTryingToUnprefix.DoNotRestore();
   }
 
   return success;
 }
 
+bool
+CSSParserImpl::ParseWebkitPrefixedGradient(nsAString& aPrefixedFuncName,
+                                           nsCSSValue& aValue)
+{
+  MOZ_ASSERT(ShouldUseUnprefixingService(),
+             "Should only call if we're allowed to use unprefixing service");
+
+  // Record the body of the "-webkit-*gradient" function into a string.
+  // Note: we're already just after the opening "(".
+  nsAutoString prefixedFuncBody;
+  mScanner->StartRecording();
+  bool gotCloseParen = SkipUntil(')');
+  mScanner->StopRecording(prefixedFuncBody);
+  if (gotCloseParen) {
+    // Strip off trailing close-paren, so that the value we pass to the
+    // unprefixing service is *just* the function-body (no parens).
+    prefixedFuncBody.Truncate(prefixedFuncBody.Length() - 1);
+  }
+
+  // NOTE: Even if we fail, we'll be leaving the parser's cursor just after
+  // the close of the "-webkit-*gradient(...)" expression. This is the same
+  // behavior that the other Parse*Gradient functions have in their failure
+  // cases -- they call "SkipUntil(')') before returning false. So this is
+  // probably what we want.
+  nsCOMPtr<nsICSSUnprefixingService> unprefixingSvc =
+    do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(unprefixingSvc, false);
+
+  bool success;
+  nsAutoString unprefixedFuncName;
+  nsAutoString unprefixedFuncBody;
+  nsresult rv =
+    unprefixingSvc->GenerateUnprefixedGradientValue(aPrefixedFuncName,
+                                                    prefixedFuncBody,
+                                                    unprefixedFuncName,
+                                                    unprefixedFuncBody,
+                                                    &success);
+
+  if (NS_FAILED(rv) || !success) {
+    return false;
+  }
+
+  // JS service thinks it successfully converted the gradient! Now let's try
+  // to parse the resulting string.
+
+  // First, add a close-paren if we originally recorded one (so that what we're
+  // about to put into the CSS parser is a faithful representation of what it
+  // would've seen if it were just parsing the original input stream):
+  if (gotCloseParen) {
+    unprefixedFuncBody.Append(char16_t(')'));
+  }
+
+  nsAutoScannerChanger scannerChanger(this, unprefixedFuncBody);
+  if (unprefixedFuncName.EqualsLiteral("linear-gradient")) {
+    return ParseLinearGradient(aValue, false, false);
+  }
+  if (unprefixedFuncName.EqualsLiteral("radial-gradient")) {
+    return ParseRadialGradient(aValue, false, false);
+  }
+
+  NS_ERROR("CSSUnprefixingService returned an unrecognized type of "
+           "gradient function");
+
+  return false;
+}
+
 //----------------------------------------------------------------------
 
 bool
 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
                                 uint32_t aFlags,
                                 bool aMustCallValueAppended,
                                 bool* aChanged,
                                 nsCSSContextType aContext)
@@ -7303,16 +7374,24 @@ CSSParserImpl::ParseVariant(nsCSSValue& 
     }
 
     if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
       return ParseLinearGradient(aValue, isRepeating, isLegacy);
     }
     if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
       return ParseRadialGradient(aValue, isRepeating, isLegacy);
     }
+    if (ShouldUseUnprefixingService() &&
+        !isRepeating && !isLegacy &&
+        StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
+      // Copy 'tmp' into a string on the stack, since as soon as we
+      // start parsing, its backing store (in "tk") will be overwritten
+      nsAutoString prefixedFuncName(tmp);
+      return ParseWebkitPrefixedGradient(prefixedFuncName, aValue);
+    }
   }
   if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
       eCSSToken_Function == tk->mType &&
       tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
     return ParseImageRect(aValue);
   }
   if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
       eCSSToken_Function == tk->mType &&
@@ -10546,17 +10625,21 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
                  mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
-                 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
+                 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element") ||
+                 (ShouldUseUnprefixingService() &&
+                  (mToken.mIdent.LowerCaseEqualsLiteral("-webkit-gradient") ||
+                   mToken.mIdent.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
+                   mToken.mIdent.LowerCaseEqualsLiteral("-webkit-radial-gradient")))))) {
       if (haveImage)
         return false;
       haveImage = true;
       if (!ParseSingleValueProperty(aState.mImage->mValue,
                                     eCSSProperty_background_image)) {
         return false;
       }
     } else if (tt == eCSSToken_Dimension ||
--- a/layout/style/nsICSSUnprefixingService.idl
+++ b/layout/style/nsICSSUnprefixingService.idl
@@ -3,17 +3,17 @@
  * 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/. */
 
 /* interface for a service that converts certain vendor-prefixed CSS properties
    to their unprefixed equivalents */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(927a5c60-0378-4bcb-a50d-99e6d1fe6063)]
+[scriptable, uuid(a5d6e2f4-d3ec-11e4-b002-782bcbaebb28)]
 interface nsICSSUnprefixingService : nsISupports
 {
   /**
    * This function helps to convert unsupported vendor-prefixed CSS into
    * supported unprefixed CSS. Given a vendor-prefixed property name and a
    * value (or e.g. value + trailing junk like " !important;}"), this function
    * will attempt to produce an equivalent CSS declaration that uses a
    * supported unprefixed CSS property.
@@ -35,14 +35,42 @@ interface nsICSSUnprefixingService : nsI
    *         convert the property to a known unprefixed equivalent, and we also
    *         performed any known-to-be-necessary fixup on the value, and we put
    *         the result in aUnprefixedDecl.
    *         Otherwise, this function returns false.
    */
   boolean generateUnprefixedDeclaration(in AString aPropName,
                                         in AString aRightHalfOfDecl,
                                         out AString aUnprefixedDecl);
+
+  /**
+   * @param   aPrefixedFuncName
+   *          The webkit-prefixed gradient function: either
+   *          "-webkit-gradient", "-webkit-linear-gradient", or
+   *          "-webkit-radial-gradient".
+   *
+   * @param   aPrefixedFuncBody
+   *          The body of the gradient function, inside (& not including) the
+   *          parenthesis.
+   *
+   * @param   aUnprefixedFuncName[out]
+   *          The resulting unprefixed gradient function name:
+   *          either "linear-gradient" or "radial-gradient".
+   *
+   * @param   aUnprefixedFuncBody[out]
+   *          The resulting unprefixed gradient function body, suitable for
+   *          including in a "linear-gradient(...)" or "radial-gradient(...)"
+   *          expression.
+   *
+   * @returns true if we were able to successfully parse aWebkitGradientStr
+   *          and populate the outparams accordingly; false otherwise.
+   *
+   */
+  boolean generateUnprefixedGradientValue(in AString aPrefixedFuncName,
+                                          in AString aPrefixedFuncBody,
+                                          out AString aUnprefixedFuncName,
+                                          out AString aUnprefixedFuncBody);
 };
 
 %{C++
 #define NS_CSSUNPREFIXINGSERVICE_CONTRACTID \
     "@mozilla.org/css-unprefixing-service;1"
 %}