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 231011 23fb39cb0f9761b56d8207abc6aa431aa1addf43
parent 231010 db9a70fc3230fbe0572a3fac0931c6b8cd06dde8
child 231012 960037d0fc98f36d1ed9e8c47a03b677f4f5c778
push id56157
push userdholbert@mozilla.com
push dateThu, 26 Feb 2015 20:08:08 +0000
treeherdermozilla-inbound@44144b892414 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1107378
milestone39.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 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