Bug 1107378 part 1: Create a JS-implemented "CSS Unprefixing Service" that can convert certain -webkit prefixed CSS to an unprefixed form. r=dbaron
☠☠ backed out by 68df163b1792 ☠ ☠
authorDaniel Holbert <dholbert@cs.stanford.edu>
Thu, 26 Feb 2015 12:07:05 -0800
changeset 249278 23fb39cb0f9761b56d8207abc6aa431aa1addf43
parent 249277 db9a70fc3230fbe0572a3fac0931c6b8cd06dde8
child 249279 960037d0fc98f36d1ed9e8c47a03b677f4f5c778
push id7860
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:46:02 +0000
treeherdermozilla-aurora@8ac636cd51f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1107378
milestone39.0a1
Bug 1107378 part 1: Create a JS-implemented "CSS Unprefixing Service" that can convert certain -webkit prefixed CSS to an unprefixed form. r=dbaron
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
layout/style/CSSUnprefixingService.js
layout/style/CSSUnprefixingService.manifest
layout/style/moz.build
layout/style/nsICSSUnprefixingService.idl
mobile/android/installer/package-manifest.in
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -532,16 +532,18 @@
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
 @BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/formautofill.manifest
 @BINPATH@/components/FormAutofillContentService.js
 @BINPATH@/components/FormAutofillStartup.js
+@BINPATH@/components/CSSUnprefixingService.js
+@BINPATH@/components/CSSUnprefixingService.manifest
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/SettingsService.js
 @BINPATH@/components/SettingsService.manifest
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -471,16 +471,18 @@
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
 @RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
 @RESPATH@/components/nsInputListAutoComplete.js
 @RESPATH@/components/formautofill.manifest
 @RESPATH@/components/FormAutofillContentService.js
 @RESPATH@/components/FormAutofillStartup.js
+@RESPATH@/components/CSSUnprefixingService.js
+@RESPATH@/components/CSSUnprefixingService.manifest
 @RESPATH@/components/contentAreaDropListener.manifest
 @RESPATH@/components/contentAreaDropListener.js
 @RESPATH@/browser/components/BrowserProfileMigrators.manifest
 @RESPATH@/browser/components/ProfileMigrator.js
 @RESPATH@/browser/components/ChromeProfileMigrator.js
 @RESPATH@/browser/components/FirefoxProfileMigrator.js
 #ifdef XP_WIN
 @RESPATH@/browser/components/IEProfileMigrator.js
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSUnprefixingService.js
@@ -0,0 +1,157 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* 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/. */
+
+/* Implementation of a service that converts certain vendor-prefixed CSS
+   properties to their unprefixed equivalents, for sites on a whitelist. */
+// XXXdholbert whitelist is coming in bug 1132743
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function CSSUnprefixingService() {
+}
+
+CSSUnprefixingService.prototype = {
+  // Boilerplate:
+  classID:        Components.ID("{f0729490-e15c-4a2f-a3fb-99e1cc946b42}"),
+  _xpcom_factory: XPCOMUtils.generateSingletonFactory(CSSUnprefixingService),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICSSUnprefixingService]),
+
+  // See documentation in nsICSSUnprefixingService.idl
+  generateUnprefixedDeclaration: function(aPropName, aRightHalfOfDecl,
+                                          aUnprefixedDecl /*out*/) {
+
+    // Convert our input strings to lower-case, for easier string-matching.
+    // (NOTE: If we ever need to add support for unprefixing properties that
+    // have case-sensitive parts, then we should do these toLowerCase()
+    // conversions in a more targeted way, to avoid breaking those properties.)
+    aPropName = aPropName.toLowerCase();
+    aRightHalfOfDecl = aRightHalfOfDecl.toLowerCase();
+
+    // We have several groups of supported properties:
+    // FIRST GROUP: Properties that can just be handled as aliases:
+    // ============================================================
+    const propertiesThatAreJustAliases = {
+      "-webkit-background-size":   "background-size",
+      "-webkit-box-flex":          "flex-grow",
+      "-webkit-box-ordinal-group": "order",
+      "-webkit-box-sizing":        "box-sizing",
+      "-webkit-transform":         "transform",
+    };
+
+    let unprefixedPropName = propertiesThatAreJustAliases[aPropName];
+    if (unprefixedPropName !== undefined) {
+      aUnprefixedDecl.value = unprefixedPropName + ":" + aRightHalfOfDecl;
+      return true;
+    }
+
+    // SECOND GROUP: Properties that take a single keyword, where the
+    // unprefixed version takes a different (but analogous) set of keywords:
+    // =====================================================================
+    const propertiesThatNeedKeywordMapping = {
+      "-webkit-box-align" : {
+        unprefixedPropName : "align-items",
+        valueMap : {
+          "start"    : "flex-start",
+          "center"   : "center",
+          "end"      : "flex-end",
+          "baseline" : "baseline",
+          "stretch"  : "stretch"
+        }
+      },
+      "-webkit-box-orient" : {
+        unprefixedPropName : "flex-direction",
+        valueMap : {
+          "horizontal"  : "row",
+          "inline-axis" : "row",
+          "vertical"    : "column",
+          "block-axis"  : "column"
+        }
+      },
+      "-webkit-box-pack" : {
+        unprefixedPropName : "justify-content",
+        valueMap : {
+          "start"    : "flex-start",
+          "center"   : "center",
+          "end"      : "flex-end",
+          "justify"  : "space-between"
+        }
+      },
+    };
+
+    let propInfo = propertiesThatNeedKeywordMapping[aPropName];
+    if (typeof(propInfo) != "undefined") {
+      // Regexp for parsing the right half of a declaration, for keyword-valued
+      // properties. Divides the right half of the declaration into:
+      //  1) any leading whitespace
+      //  2) the property value (one or more alphabetical character or hyphen)
+      //  3) anything after that (e.g. "!important", ";")
+      // Then we can look up the appropriate unprefixed-property value for the
+      // value (part 2), and splice that together with the other parts and with
+      // the unprefixed property-name to make the final declaration.
+      const keywordValuedPropertyRegexp = /^(\s*)([a-z\-]+)(.*)/;
+      let parts = keywordValuedPropertyRegexp.exec(aRightHalfOfDecl);
+      if (!parts) {
+        // Failed to parse a keyword out of aRightHalfOfDecl. (It probably has
+        // no alphabetical characters.)
+        return false;
+      }
+
+      let mappedKeyword = propInfo.valueMap[parts[2]];
+      if (mappedKeyword === undefined) {
+        // We found a keyword in aRightHalfOfDecl, but we don't have a mapping
+        // to an equivalent keyword for the unprefixed version of the property.
+        return false;
+      }
+
+      aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" +
+        parts[1] + // any leading whitespace
+        mappedKeyword +
+        parts[3]; // any trailing text (e.g. !important, semicolon, etc)
+
+      return true;
+    }
+
+    // THIRD GROUP: Properties that may need arbitrary string-replacement:
+    // ===================================================================
+    const propertiesThatNeedStringReplacement = {
+      // "-webkit-transition" takes a multi-part value. If "-webkit-transform"
+      // appears as part of that value, replace it w/ "transform".
+      // And regardless, we unprefix the "-webkit-transition" property-name.
+      // (We could handle other prefixed properties in addition to 'transform'
+      // here, but in practice "-webkit-transform" is the main one that's
+      // likely to be transitioned & that we're concerned about supporting.)
+      "-webkit-transition": {
+        unprefixedPropName : "transition",
+        stringMap : {
+          "-webkit-transform" : "transform",
+        }
+      },
+    };
+
+    propInfo = propertiesThatNeedStringReplacement[aPropName];
+    if (typeof(propInfo) != "undefined") {
+      let newRightHalf = aRightHalfOfDecl;
+      for (let strToReplace in propInfo.stringMap) {
+        let replacement = propInfo.stringMap[strToReplace];
+        newRightHalf = newRightHalf.replace(strToReplace, replacement, "g");
+      }
+      aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + newRightHalf;
+
+      return true;
+    }
+
+    // No known mapping for property aPropName.
+    return false;
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CSSUnprefixingService]);
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSUnprefixingService.manifest
@@ -0,0 +1,2 @@
+component {f0729490-e15c-4a2f-a3fb-99e1cc946b42} CSSUnprefixingService.js
+contract @mozilla.org/css-unprefixing-service;1 {f0729490-e15c-4a2f-a3fb-99e1cc946b42}
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -2,16 +2,22 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['xbl-marquee']
 TEST_DIRS += ['test']
 
+XPIDL_SOURCES += [
+    'nsICSSUnprefixingService.idl',
+]
+
+XPIDL_MODULE = 'layout_base'
+
 EXPORTS += [
     'AnimationCommon.h',
     'CounterStyleManager.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
     'nsCSSCounterDescList.h',
@@ -142,16 +148,21 @@ UNIFIED_SOURCES += [
 
 # FontFaceSet.cpp needs to be built separately because it redefines LOG.
 # nsCSSRuleProcessor.cpp needs to be built separately because it uses plarena.h.
 SOURCES += [
     'FontFaceSet.cpp',
     'nsCSSRuleProcessor.cpp',
 ]
 
+EXTRA_COMPONENTS += [
+    'CSSUnprefixingService.js',
+    'CSSUnprefixingService.manifest',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
new file mode 100644
--- /dev/null
+++ b/layout/style/nsICSSUnprefixingService.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* 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)]
+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.
+   *
+   * @param   aPropName
+   *          The vendor-prefixed property name.
+   *
+   * @param   aRightHalfOfDecl
+   *          Everything after the ":" in the CSS declaration. This includes
+   *          the property's value, along with possibly some leading whitespace
+   *          and trailing text like "!important", and possibly a ';' and/or
+   *          '}' (along with any other bogus text the author happens to
+   *          include before those, which will probably make the decl invalid).
+   *
+   * @param   aUnprefixedDecl[out]
+   *          The resulting unprefixed declaration, if we return true.
+   *
+   * @return true if we were able to unprefix -- i.e. if we were able to
+   *         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);
+};
+
+%{C++
+#define NS_CSSUNPREFIXINGSERVICE_CONTRACTID \
+    "@mozilla.org/css-unprefixing-service;1"
+%}
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -386,16 +386,18 @@
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
 @BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/formautofill.manifest
 @BINPATH@/components/FormAutofillContentService.js
 @BINPATH@/components/FormAutofillStartup.js
+@BINPATH@/components/CSSUnprefixingService.js
+@BINPATH@/components/CSSUnprefixingService.manifest
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest