Backed out 4 changesets (bug 1008236, bug 1010344, bug 1004458, bug 1008719) for mochitest crashes on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 15 May 2014 14:24:12 -0400
changeset 183303 2b99b42f1337a55f81ba879eb773288776f72be8
parent 183302 904012cb4646fa029951c83235d4d095a86532fd
child 183304 b8533fbcc8116d7bf6888756961d979d2d5158aa
child 183397 58c5a3427997bcdc216792a3726f26f433e69139
push id43526
push userryanvm@gmail.com
push dateThu, 15 May 2014 18:24:13 +0000
treeherdermozilla-inbound@2b99b42f1337 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1008236, 1010344, 1004458, 1008719
milestone32.0a1
backs out28ecab8814721dd99800cf3951ea5a128c3cbfd0
7eebcecb7e260cdcca1bf259c0dd6a08cb466fc6
17ea7f2276ac29557559c59b4c0f9fc3207b5bc1
184ead7f6e37323c36d99287732718477595b528
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
Backed out 4 changesets (bug 1008236, bug 1010344, bug 1004458, bug 1008719) for mochitest crashes on a CLOSED TREE. Backed out changeset 28ecab881472 (bug 1008719) Backed out changeset 7eebcecb7e26 (bug 1004458) Backed out changeset 17ea7f2276ac (bug 1010344) Backed out changeset 184ead7f6e37 (bug 1008236)
addon-sdk/source/test/addons/private-browsing-supported/test-windows.js
addon-sdk/source/test/test-window-utils-private-browsing.js
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
embedding/components/windowwatcher/src/nsAutoWindowStateHelper.cpp
embedding/components/windowwatcher/src/nsAutoWindowStateHelper.h
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
layout/style/nsStyleUtil.cpp
layout/style/test/test_css_escape_api.html
layout/style/test/test_parser_diagnostics_unprintables.html
--- a/addon-sdk/source/test/addons/private-browsing-supported/test-windows.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-windows.js
@@ -89,17 +89,17 @@ exports.testSettingActiveWindowDoesNotIg
   makeEmptyBrowserWindow({
     private: true
   }).then(function(window) {
     let continueAfterFocus = function(window) onFocus(window).then(nextTest);
 
     // PWPB case
     if (isWindowPBSupported) {
       assert.ok(isPrivate(window), "window is private");
-      assert.notStrictEqual(winUtils.activeBrowserWindow, browserWindow);
+      assert.notDeepEqual(winUtils.activeBrowserWindow, browserWindow);
     }
     // Global case
     else {
       assert.ok(!isPrivate(window), "window is not private");
     }
 
     assert.strictEqual(winUtils.activeBrowserWindow, window,
                  "Correct active browser window pb supported");
@@ -126,29 +126,29 @@ exports.testSettingActiveWindowDoesNotIg
                            "Correct active browser window [2]");
         assert.strictEqual(winUtils.activeWindow, window,
                            "Correct active window [2]");
 
         // test setting a private window
         continueAfterFocus(winUtils.activeWindow = window);
       },
       function() {
-        assert.strictEqual(winUtils.activeBrowserWindow, window,
-                          "Correct active browser window [3]");
-        assert.strictEqual(winUtils.activeWindow, window,
-                          "Correct active window [3]");
+        assert.deepEqual(winUtils.activeBrowserWindow, window,
+                         "Correct active browser window [3]");
+        assert.deepEqual(winUtils.activeWindow, window,
+                         "Correct active window [3]");
 
         // just to get back to original state
         continueAfterFocus(winUtils.activeWindow = browserWindow);
       },
       function() {
-        assert.strictEqual(winUtils.activeBrowserWindow, browserWindow,
-                          "Correct active browser window when pb mode is supported [4]");
-        assert.strictEqual(winUtils.activeWindow, browserWindow,
-                          "Correct active window when pb mode is supported [4]");
+        assert.deepEqual(winUtils.activeBrowserWindow, browserWindow,
+                         "Correct active browser window when pb mode is supported [4]");
+        assert.deepEqual(winUtils.activeWindow, browserWindow,
+                         "Correct active window when pb mode is supported [4]");
 
         close(window).then(done).then(null, assert.fail);
       }
     ];
 
     function nextTest() {
       let args = arguments;
       if (testSteps.length) {
--- a/addon-sdk/source/test/test-window-utils-private-browsing.js
+++ b/addon-sdk/source/test/test-window-utils-private-browsing.js
@@ -90,17 +90,17 @@ exports.testSettingActiveWindowDoesNotIg
                "Browser window is the active browser window.");
   assert.ok(!isPrivate(browserWindow), "Browser window is not private.");
 
   // make a new private window
   makeEmptyBrowserWindow({ private: true }).then(focus).then(window => {
     // PWPB case
     if (isWindowPBSupported) {
       assert.ok(isPrivate(window), "window is private");
-      assert.notStrictEqual(windowUtils.activeBrowserWindow, browserWindow);
+      assert.notDeepEqual(windowUtils.activeBrowserWindow, browserWindow);
     }
     // Global case
     else {
       assert.ok(!isPrivate(window), "window is not private");
     }
 
     assert.strictEqual(windowUtils.activeBrowserWindow, window,
                  "Correct active browser window pb supported");
@@ -122,28 +122,28 @@ exports.testSettingActiveWindowDoesNotIg
       assert.strictEqual(windowUtils.activeBrowserWindow, window,
                          "Correct active browser window [2]");
       assert.strictEqual(windowUtils.activeWindow, window,
                          "Correct active window [2]");
 
       // test setting a private window
       return onFocus(windowUtils.activeWindow = window);
     }).then(function() {
-      assert.strictEqual(windowUtils.activeBrowserWindow, window,
-                         "Correct active browser window [3]");
-      assert.strictEqual(windowUtils.activeWindow, window,
-                         "Correct active window [3]");
+      assert.deepEqual(windowUtils.activeBrowserWindow, window,
+                       "Correct active browser window [3]");
+      assert.deepEqual(windowUtils.activeWindow, window,
+                       "Correct active window [3]");
 
       // just to get back to original state
       return onFocus(windowUtils.activeWindow = browserWindow);
     }).then(_ => {
-      assert.strictEqual(windowUtils.activeBrowserWindow, browserWindow,
-                         "Correct active browser window when pb mode is supported [4]");
-      assert.strictEqual(windowUtils.activeWindow, browserWindow,
-                         "Correct active window when pb mode is supported [4]");
+      assert.deepEqual(windowUtils.activeBrowserWindow, browserWindow,
+                       "Correct active browser window when pb mode is supported [4]");
+      assert.deepEqual(windowUtils.activeWindow, browserWindow,
+                       "Correct active window when pb mode is supported [4]");
 
       return close(window);
     })
   }).then(done).then(null, assert.fail);
 };
 
 exports.testActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
   // make a new private window
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8425,17 +8425,17 @@ nsGlobalWindow::ReallyCloseWindow()
 
     CleanUp();
   }
 }
 
 void
 nsGlobalWindow::EnterModalState()
 {
-  MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
+  FORWARD_TO_OUTER_VOID(EnterModalState, ());
 
   // GetScriptableTop, not GetTop, so that EnterModalState works properly with
   // <iframe mozbrowser>.
   nsGlobalWindow* topWin = GetScriptableTop();
 
   if (!topWin) {
     NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
     return;
@@ -8558,17 +8558,17 @@ public:
 
 private:
   nsRefPtr<nsGlobalWindow> mWindow;
 };
 
 void
 nsGlobalWindow::LeaveModalState()
 {
-  MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
+  FORWARD_TO_OUTER_VOID(LeaveModalState, ());
 
   nsGlobalWindow* topWin = GetScriptableTop();
 
   if (!topWin) {
     NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?");
     return;
   }
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2253,23 +2253,17 @@ GenericBindingGetter(JSContext* cx, unsi
       return ThrowInvalidThis(cx, args,
                               GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
                               protoID);
     }
   }
 
   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
   JSJitGetterOp getter = info->getter;
-  bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
-#ifdef DEBUG
-  if (ok) {
-    AssertReturnTypeMatchesJitinfo(info, args.rval());
-  }
-#endif
-  return ok;
+  return getter(cx, obj, self, JSJitGetterCallArgs(args));
 }
 
 bool
 GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
@@ -2292,20 +2286,17 @@ GenericBindingSetter(JSContext* cx, unsi
   if (args.length() == 0) {
     return ThrowNoSetterArg(cx, protoID);
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
   JSJitSetterOp setter = info->setter;
   if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
     return false;
   }
-  args.rval().setUndefined();
-#ifdef DEBUG
-  AssertReturnTypeMatchesJitinfo(info, args.rval());
-#endif
+  args.rval().set(JSVAL_VOID);
   return true;
 }
 
 bool
 GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
@@ -2323,23 +2314,17 @@ GenericBindingMethod(JSContext* cx, unsi
     if (NS_FAILED(rv)) {
       return ThrowInvalidThis(cx, args,
                               GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
                               protoID);
     }
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Method);
   JSJitMethodOp method = info->method;
-  bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
-#ifdef DEBUG
-  if (ok) {
-    AssertReturnTypeMatchesJitinfo(info, args.rval());
-  }
-#endif
-  return ok;
+  return method(cx, obj, self, JSJitMethodCallArgs(args));
 }
 
 bool
 GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   // Make sure to save the callee before someone maybe messes with rval().
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JSObject*> callee(cx, &args.callee());
@@ -2367,24 +2352,19 @@ GenericPromiseReturningBindingMethod(JSC
       return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
                                        args.rval());
     }
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Method);
   JSJitMethodOp method = info->method;
   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
   if (ok) {
-#ifdef DEBUG
-    AssertReturnTypeMatchesJitinfo(info, args.rval());
-#endif
     return true;
   }
 
-  // Promise-returning methods always return objects
-  MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
   return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
                                    args.rval());
 }
 
 bool
 StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   // Make sure to save the callee before someone maybe messes with rval().
@@ -2444,49 +2424,10 @@ CreateGlobalOptions<nsGlobalWindow>::Tra
 /* static */
 bool
 CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
                                                       JS::Handle<JSObject*> aGlobal)
 {
   return XPCWrappedNativeScope::GetNewOrUsed(aCx, aGlobal);
 }
 
-#ifdef DEBUG
-void
-AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
-                               JS::Handle<JS::Value> aValue)
-{
-  switch (aJitInfo->returnType()) {
-  case JSVAL_TYPE_UNKNOWN:
-    // Any value is good.
-    break;
-  case JSVAL_TYPE_DOUBLE:
-    // The value could actually be an int32 value as well.
-    MOZ_ASSERT(aValue.isNumber());
-    break;
-  case JSVAL_TYPE_INT32:
-    MOZ_ASSERT(aValue.isInt32());
-    break;
-  case JSVAL_TYPE_UNDEFINED:
-    MOZ_ASSERT(aValue.isUndefined());
-    break;
-  case JSVAL_TYPE_BOOLEAN:
-    MOZ_ASSERT(aValue.isBoolean());
-    break;
-  case JSVAL_TYPE_STRING:
-    MOZ_ASSERT(aValue.isString());
-    break;
-  case JSVAL_TYPE_NULL:
-    MOZ_ASSERT(aValue.isNull());
-    break;
-  case JSVAL_TYPE_OBJECT:
-    MOZ_ASSERT(aValue.isObject());
-    break;
-  default:
-    // Someone messed up their jitinfo type.
-    MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
-    break;
-  }
-}
-#endif
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2739,18 +2739,12 @@ ConvertExceptionToPromise(JSContext* cx,
 // DOM global objects live on the object or the prototype, we supply this one
 // place to switch the behaviour, so we can easily turn this off on branches.
 inline bool
 GlobalPropertiesAreOwn()
 {
   return true;
 }
 
-#ifdef DEBUG
-void
-AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
-                               JS::Handle<JS::Value> aValue);
-#endif
-
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -65,17 +65,17 @@ def wantsAddProperty(desc):
 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
 
 
 def indent(s, indentLevel=2):
     """
     Indent C++ code.
 
     Weird secret feature: this doesn't indent lines that start with # (such as
-    #include lines or #ifdef/#endif).
+    #include lines).
     """
     if s == "":
         return s
     return re.sub(lineStartDetector, indentLevel * " ", s)
 
 
 def dedent(s):
     """
@@ -6678,23 +6678,17 @@ class CGGenericMethod(CGAbstractBindingM
                                          unwrapFailureCode=unwrapFailureCode,
                                          allowCrossOriginThis=allowCrossOriginThis)
 
     def generate_code(self):
         return CGGeneric(indent(dedent("""
             const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
             MOZ_ASSERT(info->type() == JSJitInfo::Method);
             JSJitMethodOp method = info->method;
-            bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
-            #ifdef DEBUG
-            if (ok) {
-              AssertReturnTypeMatchesJitinfo(info, args.rval());
-            }
-            #endif
-            return ok;
+            return method(cx, obj, self, JSJitMethodCallArgs(args));
             """)))
 
 
 class CGSpecializedMethod(CGAbstractStaticMethod):
     """
     A class for generating the C++ code for a specialized method that the JIT
     can call with lower overhead.
     """
@@ -6982,23 +6976,17 @@ class CGGenericGetter(CGAbstractBindingM
                                          unwrapFailureCode,
                                          allowCrossOriginThis=allowCrossOriginThis)
 
     def generate_code(self):
         return CGGeneric(indent(dedent("""
             const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
             MOZ_ASSERT(info->type() == JSJitInfo::Getter);
             JSJitGetterOp getter = info->getter;
-            bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
-            #ifdef DEBUG
-            if (ok) {
-              AssertReturnTypeMatchesJitinfo(info, args.rval());
-            }
-            #endif
-            return ok;
+            return getter(cx, obj, self, JSJitGetterCallArgs(args));
             """)))
 
 
 class CGSpecializedGetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute getter
     that the JIT can call with lower overhead.
     """
@@ -7123,19 +7111,16 @@ class CGGenericSetter(CGAbstractBindingM
             }
             const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
             MOZ_ASSERT(info->type() == JSJitInfo::Setter);
             JSJitSetterOp setter = info->setter;
             if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
               return false;
             }
             args.rval().set(JSVAL_VOID);
-            #ifdef DEBUG
-            AssertReturnTypeMatchesJitinfo(info, args.rval());
-            #endif
             return true;
             """,
             name=self.descriptor.interface.identifier.name)))
 
 
 class CGSpecializedSetter(CGAbstractStaticMethod):
     """
     A class for generating the code for a specialized attribute setter
--- a/embedding/components/windowwatcher/src/nsAutoWindowStateHelper.cpp
+++ b/embedding/components/windowwatcher/src/nsAutoWindowStateHelper.cpp
@@ -14,58 +14,61 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /****************************************************************
  ****************** nsAutoWindowStateHelper *********************
  ****************************************************************/
 
-nsAutoWindowStateHelper::nsAutoWindowStateHelper(nsPIDOMWindow *aWindow)
+nsAutoWindowStateHelper::nsAutoWindowStateHelper(nsIDOMWindow *aWindow)
   : mWindow(aWindow),
     mDefaultEnabled(DispatchEventToChrome("DOMWillOpenModalDialog"))
 {
-  if (mWindow) {
-    mWindow->EnterModalState();
+  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
+
+  if (window) {
+    window->EnterModalState();
   }
 }
 
 nsAutoWindowStateHelper::~nsAutoWindowStateHelper()
 {
-  if (mWindow) {
-    mWindow->LeaveModalState();
+  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mWindow));
+
+  if (window) {
+    window->LeaveModalState();
   }
 
   if (mDefaultEnabled) {
     DispatchEventToChrome("DOMModalDialogClosed");
   }
 }
 
 bool
 nsAutoWindowStateHelper::DispatchEventToChrome(const char *aEventName)
 {
-  // XXXbz should we skip dispatching the event if the inner changed?
-  // That is, should we store both the inner and the outer?
-  if (!mWindow) {
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mWindow);
+  if (!window || (window->IsInnerWindow() && !window->IsCurrentInnerWindow())) {
     return true;
   }
 
   // The functions of nsContentUtils do not provide the required behavior,
   // so the following is inlined.
-  nsIDocument* doc = mWindow->GetExtantDoc();
+  nsIDocument* doc = window->GetExtantDoc();
   if (!doc) {
     return true;
   }
 
   ErrorResult rv;
   nsRefPtr<Event> event = doc->CreateEvent(NS_LITERAL_STRING("Events"), rv);
   if (rv.Failed()) {
     return false;
   }
   NS_ENSURE_TRUE(NS_SUCCEEDED(event->InitEvent(NS_ConvertASCIItoUTF16(aEventName), true, true)), false);
   event->SetTrusted(true);
   event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
 
-  nsCOMPtr<EventTarget> target = do_QueryInterface(mWindow);
+  nsCOMPtr<EventTarget> target = do_QueryInterface(window);
   bool defaultActionEnabled;
   target->DispatchEvent(event, &defaultActionEnabled);
   return defaultActionEnabled;
 }
--- a/embedding/components/windowwatcher/src/nsAutoWindowStateHelper.h
+++ b/embedding/components/windowwatcher/src/nsAutoWindowStateHelper.h
@@ -2,37 +2,36 @@
 /* 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/. */
 
 #ifndef __nsAutoWindowStateHelper_h
 #define __nsAutoWindowStateHelper_h
 
 #include "nsCOMPtr.h"
-#include "nsPIDOMWindow.h"
 
 /**
  * Helper class for dealing with notifications around opening modal
  * windows.
  */
 
-class nsPIDOMWindow;
+class nsIDOMWindow;
 
 class nsAutoWindowStateHelper
 {
 public:
-  nsAutoWindowStateHelper(nsPIDOMWindow *aWindow);
+  nsAutoWindowStateHelper(nsIDOMWindow *aWindow);
   ~nsAutoWindowStateHelper();
 
   bool DefaultEnabled()
   {
     return mDefaultEnabled;
   }
 
 protected:
   bool DispatchEventToChrome(const char *aEventName);
 
-  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsIDOMWindow *mWindow;
   bool mDefaultEnabled;
 };
 
 
 #endif
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -950,20 +950,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // Throw an exception here if no web browser chrome is available,
     // we need that to show a modal window.
     NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
 
     // Dispatch dialog events etc, but we only want to do that if
     // we're opening a modal content window (the helper classes are
     // no-ops if given no window), for chrome dialogs we don't want to
     // do any of that (it's done elsewhere for us).
-    // Make sure we maintain the state on an outer window, because
-    // that's where it lives; inner windows assert if you try to
-    // maintain the state on them.
-    nsAutoWindowStateHelper windowStateHelper(parentWindow->GetOuterWindow());
+    nsAutoWindowStateHelper windowStateHelper(aParent);
 
     if (!windowStateHelper.DefaultEnabled()) {
       // Default to cancel not opening the modal window.
       NS_RELEASE(*_retval);
 
       return NS_OK;
     }
 
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -76,51 +76,50 @@ void nsStyleUtil::AppendEscapedCSSString
 
   aReturn.Append(quoteChar);
 }
 
 /* static */ bool
 nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
 {
   // The relevant parts of the CSS grammar are:
-  //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
+  //   ident    [-]?{nmstart}{nmchar}*
   //   nmstart  [_a-z]|{nonascii}|{escape}
   //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
   //   nonascii [^\0-\177]
   //   escape   {unicode}|\\[^\n\r\f0-9a-f]
   //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
-  // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
-  // modified for idents by
-  // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
-  // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
+  // from http://www.w3.org/TR/CSS21/syndata.html#tokenization
 
   const char16_t* in = aIdent.BeginReading();
   const char16_t* const end = aIdent.EndReading();
 
   if (in == end)
     return true;
 
   // A leading dash does not need to be escaped as long as it is not the
   // *only* character in the identifier.
-  if (*in == '-') {
-    if (in + 1 == end) {
-      aReturn.Append(char16_t('\\'));
-      aReturn.Append(char16_t('-'));
-      return true;
-    }
-
+  if (in + 1 != end && *in == '-') {
     aReturn.Append(char16_t('-'));
     ++in;
   }
 
   // Escape a digit at the start (including after a dash),
   // numerically.  If we didn't escape it numerically, it would get
   // interpreted as a numeric escape for the wrong character.
-  if (in != end && ('0' <= *in && *in <= '9')) {
-    aReturn.AppendPrintf("\\%hX ", *in);
+  // A second dash immediately after a leading dash must also be
+  // escaped, but this may be done symbolically.
+  if (in != end && (*in == '-' ||
+                    ('0' <= *in && *in <= '9'))) {
+    if (*in == '-') {
+      aReturn.Append(char16_t('\\'));
+      aReturn.Append(char16_t('-'));
+    } else {
+      aReturn.AppendPrintf("\\%hX ", *in);
+    }
     ++in;
   }
 
   for (; in != end; ++in) {
     char16_t ch = *in;
     if (ch == 0x00) {
       return false;
     }
--- a/layout/style/test/test_css_escape_api.html
+++ b/layout/style/test/test_css_escape_api.html
@@ -61,17 +61,17 @@ is(CSS.escape('-2a'), '-\\32 a', "escapi
 is(CSS.escape('-3a'), '-\\33 a', "escapingFailed Char: -3a");
 is(CSS.escape('-4a'), '-\\34 a', "escapingFailed Char: -4a");
 is(CSS.escape('-5a'), '-\\35 a', "escapingFailed Char: -5a");
 is(CSS.escape('-6a'), '-\\36 a', "escapingFailed Char: -6a");
 is(CSS.escape('-7a'), '-\\37 a', "escapingFailed Char: -7a");
 is(CSS.escape('-8a'), '-\\38 a', "escapingFailed Char: -8a");
 is(CSS.escape('-9a'), '-\\39 a', "escapingFailed Char: -9a");
 
-is(CSS.escape('--a'), '--a', 'Should not need to escape leading "--"');
+is(CSS.escape('--a'), '-\\-a', "escapingFailed Char: --a");
 
 is(CSS.escape('\x80\x2D\x5F\xA9'), '\\80 \x2D\x5F\xA9', "escapingFailed Char: \\x80\\x2D\\x5F\\xA9");
 is(CSS.escape('\xA0\xA1\xA2'), '\xA0\xA1\xA2', "escapingFailed Char: \\xA0\\xA1\\xA2");
 is(CSS.escape('a0123456789b'), 'a0123456789b', "escapingFailed Char: a0123465789");
 is(CSS.escape('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz', "escapingFailed Char: abcdefghijklmnopqrstuvwxyz");
 is(CSS.escape('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', "escapingFailed Char: ABCDEFGHIJKLMNOPQRSTUVWXYZBCDEFGHIJKLMNOPQRSTUVWXYZ");
 
 is(CSS.escape('\x20\x21\x78\x79'), '\\ \\!xy', "escapingFailed Char: \\x20\\x21\\x78\\x79");
--- a/layout/style/test/test_parser_diagnostics_unprintables.html
+++ b/layout/style/test/test_parser_diagnostics_unprintables.html
@@ -69,17 +69,17 @@ const substitutions = [
   { t: "\\32 ",  i: "\\32 ",  s: "2"  },
   { t: "\\33 ",  i: "\\33 ",  s: "3"  },
   { t: "\\34 ",  i: "\\34 ",  s: "4"  },
   { t: "\\35 ",  i: "\\35 ",  s: "5"  },
   { t: "\\36 ",  i: "\\36 ",  s: "6"  },
   { t: "\\37 ",  i: "\\37 ",  s: "7"  },
   { t: "\\38 ",  i: "\\38 ",  s: "8"  },
   { t: "\\39 ",  i: "\\39 ",  s: "9"  },
-  { t: "-\\-",   i: "--",     s: "--" },
+  { t: "-\\-",   i: "-\\-",   s: "--" },
   { t: "-\\30 ", i: "-\\30 ", s: "-0" },
   { t: "-\\31 ", i: "-\\31 ", s: "-1" },
   { t: "-\\32 ", i: "-\\32 ", s: "-2" },
   { t: "-\\33 ", i: "-\\33 ", s: "-3" },
   { t: "-\\34 ", i: "-\\34 ", s: "-4" },
   { t: "-\\35 ", i: "-\\35 ", s: "-5" },
   { t: "-\\36 ", i: "-\\36 ", s: "-6" },
   { t: "-\\37 ", i: "-\\37 ", s: "-7" },