Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 08 Dec 2014 15:46:14 -0500
changeset 218736 f1f48ccb2d4e2ca9226c01fe31a271ebdd8b64a0
parent 218709 3f430cc77fb09d8a3e76f68fd14184dab1acdf31 (current diff)
parent 218735 09ca5cf1876ff10d25546da446c68101d36ef3ce (diff)
child 218737 7b6ed01b41b6f868130d83ae6e0b134ba04747b5
child 218746 a4b4cd74709c18dda99a4b68507527cbf9c66bf3
child 218823 854ebf59f564adb4f95f772703f6179cc0a103f6
push id27942
push userryanvm@gmail.com
push dateMon, 08 Dec 2014 20:46:16 +0000
treeherdermozilla-central@f1f48ccb2d4e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
f1f48ccb2d4e / 37.0a1 / 20141209030205 / files
nightly linux64
f1f48ccb2d4e / 37.0a1 / 20141209030205 / files
nightly mac
f1f48ccb2d4e / 37.0a1 / 20141209030205 / files
nightly win32
f1f48ccb2d4e / 37.0a1 / 20141209030205 / files
nightly win64
f1f48ccb2d4e / 37.0a1 / 20141209030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
dom/workers/test/browser.ini
dom/workers/test/browser_bug1104623.js
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -485,9 +485,10 @@ skip-if = e10s
 [browser_addCertException.js]
 skip-if = e10s # Bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_bug1045809.js]
 [browser_e10s_switchbrowser.js]
 [browser_blockHPKP.js]
 skip-if = e10s # bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_mcb_redirect.js]
 skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redirection into account
+[browser_updatecommands.js]
 [browser_windowactivation.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_updatecommands.js
@@ -0,0 +1,93 @@
+let testPage = "data:text/html,<body><input id='input1' value='value'><select size=2><option val=1>One</select></body>";
+
+let browser;
+
+function test() {
+  waitForExplicitFinish();
+
+  gURLBar.focus();
+
+  var tab = gBrowser.addTab();
+  browser = gBrowser.getBrowserForTab(tab);
+  gBrowser.selectedTab = tab;
+
+  addEventListener("commandupdate", checkTest, false);
+
+  function runFirstTest(event) {
+    browser.removeEventListener("load", runFirstTest, true);
+    doTest();
+  }
+
+  browser.addEventListener("load", runFirstTest, true);
+  browser.contentWindow.location = testPage;
+}
+
+let currentTest;
+
+let tests = [
+  // Switch focus to 'input1'. Paste and select all should be enabled.
+  { name: "focus input", test: function() { EventUtils.synthesizeKey("VK_TAB", {}) },
+    commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+  // Move cursor to end which will deselect the text. Copy should be disabled but paste and select all should still be enabled.
+  { name: "cursor right", test: function() { EventUtils.synthesizeKey("VK_RIGHT", {}) },
+    commands: { "cmd_copy" : false, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+  // Select all of the text. Copy should become enabled.
+  { name: "select all", test: function() { EventUtils.synthesizeKey("a", { accelKey: true }) },
+    commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+
+  // Replace the text with 'c'. Copy should now be disabled and undo enabled.
+  { name: "change value", test: function() { EventUtils.synthesizeKey("c", {}) },
+    commands: { "cmd_copy" : false, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : true, "cmd_redo": false } },
+
+  // Undo. Undo should be disabled and redo enabled. The text is reselected so copy is enabled.
+  { name: "undo", test: function() { EventUtils.synthesizeKey("z", {accelKey: true }) },
+    commands: { "cmd_copy" : true, "cmd_paste": true, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": true  } },
+
+  // Switch focus to the select. Only select all should now be enabled.
+  { name: "focus select", test: function() { EventUtils.synthesizeKey("VK_TAB", {}) },
+    commands: { "cmd_copy" : false, "cmd_paste": false, "cmd_selectAll" : true, "cmd_undo" : false, "cmd_redo": false } },
+];
+
+function doTest()
+{
+  if (!tests.length) {
+    removeEventListener("commandupdate", checkTest, false);
+    gBrowser.removeCurrentTab();
+    finish();
+    return;
+  }
+
+  currentTest = tests.shift();
+  currentTest.test();
+}
+
+function checkTest(event)
+{
+  // Ignore commandupdates before the test starts
+  if (document.activeElement != browser || !currentTest) {
+    return;
+  }
+
+  // Skip events fired on command updaters other than the main edit menu one.
+  if (event.target != document.getElementById("editMenuCommandSetAll")) {
+    return;
+  }
+
+  for (let command in currentTest.commands) {
+    // Command updates can come several at a time, and, especially with multiple
+    // processes, the updates can come asynchronously. Handle this by just waiting
+    // until the command have the correct state. The test will timeout if the
+    // correct command update never occurs.
+    if ((document.getElementById(command).getAttribute("disabled") != "true") != currentTest.commands[command]) {
+      return;
+    }
+
+    is(document.getElementById(command).getAttribute("disabled") != "true", currentTest.commands[command],
+       currentTest["name"] + " " + command);
+  }
+
+  currentTest = null; // prevent the check from running again
+  SimpleTest.executeSoon(doTest);
+}
--- a/docshell/base/LoadInfo.cpp
+++ b/docshell/base/LoadInfo.cpp
@@ -117,15 +117,9 @@ LoadInfo::GetContentPolicyType(nsContent
 NS_IMETHODIMP
 LoadInfo::GetBaseURI(nsIURI** aBaseURI)
 {
   *aBaseURI = mBaseURI;
   NS_IF_ADDREF(*aBaseURI);
   return NS_OK;
 }
 
-nsIURI*
-LoadInfo::BaseURI()
-{
-  return mBaseURI;
-}
-
 } // namespace mozilla
--- a/docshell/base/nsILoadInfo.idl
+++ b/docshell/base/nsILoadInfo.idl
@@ -12,17 +12,17 @@ interface nsINode;
 interface nsIPrincipal;
 interface nsIURI;
 
 typedef unsigned long nsSecurityFlags;
 
 /**
  * An nsILoadOwner represents per-load information about who started the load.
  */
-[scriptable, builtinclass, uuid(768a1f20-57d4-462a-812a-41c04e5d1e19)]
+[scriptable, builtinclass, uuid(da363267-236d-49bf-83a2-33da8d892728)]
 interface nsILoadInfo : nsISupports
 {
   /**
    * No special security flags:
    */
   const unsigned long SEC_NORMAL = 0;
 
   /**
@@ -151,33 +151,27 @@ interface nsILoadInfo : nsISupports
    * hence should use a NullPrincipal.
    */
   [infallible] readonly attribute boolean loadingSandboxed;
 
   /**
    * The contentPolicyType of the channel, used for security checks
    * like Mixed Content Blocking and Content Security Policy.
    */
-  readonly attribute nsContentPolicyType contentPolicyType;
+   readonly attribute nsContentPolicyType contentPolicyType;
 
 %{ C++
   inline nsContentPolicyType GetContentPolicyType()
   {
     nsContentPolicyType result;
     mozilla::DebugOnly<nsresult> rv = GetContentPolicyType(&result);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     return result;
   }
 %}
 
   /**
    * A base URI for use in situations where it cannot otherwise be inferred.
    * This attribute may be null.  The value of this attribute may be
    * ignored if the base URI can be inferred by the channel's URI.
    */
-  readonly attribute nsIURI baseURI;
-
-  /**
-   * A C++-friendly version of baseURI.
-   */
-  [noscript, notxpcom, nostdcall, binaryname(BaseURI)]
-  nsIURI binaryBaseURI();
+   readonly attribute nsIURI baseURI;
 };
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -21,16 +21,17 @@
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsLayoutUtils.h"
 #include "nsIPresShell.h"
 #include "nsFrameTraversal.h"
 #include "nsIWebNavigation.h"
 #include "nsCaret.h"
 #include "nsIBaseWindow.h"
+#include "nsIXULWindow.h"
 #include "nsViewManager.h"
 #include "nsFrameSelection.h"
 #include "mozilla/dom/Selection.h"
 #include "nsXULPopupManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIPrincipal.h"
 #include "nsIObserverService.h"
 #include "nsIObjectFrame.h"
@@ -729,17 +730,20 @@ nsFocusManager::WindowRaised(nsIDOMWindo
   nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell();
   if (presShell) {
     // disable selection mousedown state on activation
     // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
     nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
     frameSelection->SetDragState(false);
   }
 
-  Focus(currentWindow, currentFocus, 0, true, false, true, true);
+  // If there is no nsIXULWindow, then this is an embedded or child process window.
+  // Pass false for aWindowRaised so that commands get updated.
+  nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(baseWindow));
+  Focus(currentWindow, currentFocus, 0, true, false, xulWin != nullptr, true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9265,16 +9265,46 @@ nsGlobalWindow::ShowModalDialog(const ns
 
   ErrorResult rv;
   nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aURI, aArgs, aOptions, rv);
   retVal.forget(aRetVal);
 
   return rv.ErrorCode();
 }
 
+class ChildCommandDispatcher : public nsRunnable
+{
+public:
+  ChildCommandDispatcher(nsGlobalWindow* aWindow,
+                         nsITabChild* aTabChild,
+                         const nsAString& aAction)
+  : mWindow(aWindow), mTabChild(aTabChild), mAction(aAction) {}
+
+  NS_IMETHOD Run()
+  {
+    nsCOMPtr<nsPIWindowRoot> root = mWindow->GetTopWindowRoot();
+    if (!root) {
+      return NS_OK;
+    }
+
+    nsTArray<nsCString> enabledCommands, disabledCommands;
+    root->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
+    if (enabledCommands.Length() || disabledCommands.Length()) {
+      mTabChild->EnableDisableCommands(mAction, enabledCommands, disabledCommands);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsGlobalWindow>             mWindow;
+  nsCOMPtr<nsITabChild>                mTabChild;
+  nsString                             mAction;
+};
+
 class CommandDispatcher : public nsRunnable
 {
 public:
   CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
                     const nsAString& aAction)
   : mDispatcher(aDispatcher), mAction(aAction) {}
 
   NS_IMETHOD Run()
@@ -9284,16 +9314,22 @@ public:
 
   nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
   nsString                             mAction;
 };
 
 NS_IMETHODIMP
 nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
 {
+  // If this is a child process, redirect to the parent process.
+  if (nsCOMPtr<nsITabChild> child = do_GetInterface(GetDocShell())) {
+    nsContentUtils::AddScriptRunner(new ChildCommandDispatcher(this, child, anAction));
+    return NS_OK;
+  }
+
   nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
   if (!rootWindow)
     return NS_OK;
 
   nsCOMPtr<nsIDOMXULDocument> xulDoc =
     do_QueryInterface(rootWindow->GetExtantDoc());
   // See if we contain a XUL document.
   // selectionchange action is only used for mozbrowser, not for XUL. So we bypass
--- a/dom/base/nsPIWindowRoot.h
+++ b/dom/base/nsPIWindowRoot.h
@@ -10,34 +10,37 @@
 #include "nsISupports.h"
 #include "mozilla/dom/EventTarget.h"
 
 class nsPIDOMWindow;
 class nsIControllers;
 class nsIController;
 
 #define NS_IWINDOWROOT_IID \
-{ 0x3f71f50c, 0xa7e0, 0x43bc, \
- { 0xac, 0x25, 0x4d, 0xbb, 0x88, 0x7b, 0x21, 0x09 } }
+{ 0x728a2682, 0x55c0, 0x4860, \
+ { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } }
 
 class nsPIWindowRoot : public mozilla::dom::EventTarget
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID)
 
   virtual nsPIDOMWindow* GetWindow()=0;
 
   // get and set the node that is the context of a popup menu
   virtual nsIDOMNode* GetPopupNode() = 0;
   virtual void SetPopupNode(nsIDOMNode* aNode) = 0;
 
   virtual nsresult GetControllerForCommand(const char *aCommand,
                                            nsIController** aResult) = 0;
   virtual nsresult GetControllers(nsIControllers** aResult) = 0;
 
+  virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+                                          nsTArray<nsCString>& aDisabledCommands) = 0;
+
   virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0;
   virtual mozilla::dom::EventTarget* GetParentTarget() = 0;
   virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID)
 
 #endif // nsPIWindowRoot_h__
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -276,16 +276,85 @@ nsWindowRoot::GetControllerForCommand(co
     // XXXndeakin P3 is this casting safe?
     nsGlobalWindow *win = static_cast<nsGlobalWindow*>(focusedWindow.get());
     focusedWindow = win->GetPrivateParent();
   }
   
   return NS_OK;
 }
 
+void
+nsWindowRoot::GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
+                                                       nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
+                                                       nsTArray<nsCString>& aEnabledCommands,
+                                                       nsTArray<nsCString>& aDisabledCommands)
+{
+  uint32_t controllerCount;
+  aControllers->GetControllerCount(&controllerCount);
+  for (uint32_t c = 0; c < controllerCount; c++) {
+    nsCOMPtr<nsIController> controller;
+    aControllers->GetControllerAt(c, getter_AddRefs(controller));
+
+    nsCOMPtr<nsICommandController> commandController(do_QueryInterface(controller));
+    if (commandController) {
+      uint32_t commandsCount;
+      char** commands;
+      if (NS_SUCCEEDED(commandController->GetSupportedCommands(&commandsCount, &commands))) {
+        for (uint32_t e = 0; e < commandsCount; e++) {
+          // Use a hash to determine which commands have already been handled by
+          // earlier controllers, as the earlier controller's result should get
+          // priority.
+          if (!aCommandsHandled.Contains(commands[e])) {
+            aCommandsHandled.PutEntry(commands[e]);
+
+            bool enabled = false;
+            controller->IsCommandEnabled(commands[e], &enabled);
+
+            const nsDependentCSubstring commandStr(commands[e], strlen(commands[e]));
+            if (enabled) {
+              aEnabledCommands.AppendElement(commandStr);
+            } else {
+              aDisabledCommands.AppendElement(commandStr);
+            }
+          }
+        }
+
+        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(commandsCount, commands);
+      }
+    }
+  }
+}
+
+void
+nsWindowRoot::GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+                                         nsTArray<nsCString>& aDisabledCommands)
+{
+  nsTHashtable<nsCharPtrHashKey> commandsHandled;
+
+  nsCOMPtr<nsIControllers> controllers;
+  GetControllers(getter_AddRefs(controllers));
+  if (controllers) {
+    GetEnabledDisabledCommandsForControllers(controllers, commandsHandled,
+                                             aEnabledCommands, aDisabledCommands);
+  }
+
+  nsCOMPtr<nsPIDOMWindow> focusedWindow;
+  nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
+  while (focusedWindow) {
+    focusedWindow->GetControllers(getter_AddRefs(controllers));
+    if (controllers) {
+      GetEnabledDisabledCommandsForControllers(controllers, commandsHandled,
+                                               aEnabledCommands, aDisabledCommands);
+    }
+
+    nsGlobalWindow* win = static_cast<nsGlobalWindow*>(focusedWindow.get());
+    focusedWindow = win->GetPrivateParent();
+  }
+}
+
 nsIDOMNode*
 nsWindowRoot::GetPopupNode()
 {
   return mPopupNode;
 }
 
 void
 nsWindowRoot::SetPopupNode(nsIDOMNode* aNode)
--- a/dom/base/nsWindowRoot.h
+++ b/dom/base/nsWindowRoot.h
@@ -18,16 +18,18 @@ class EventChainPreVisitor;
 } // namespace mozilla
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIDOMEventTarget.h"
 #include "nsPIWindowRoot.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
 
 class nsWindowRoot : public nsPIWindowRoot
 {
 public:
   explicit nsWindowRoot(nsPIDOMWindow* aWindow);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIDOMEVENTTARGET
@@ -47,16 +49,19 @@ public:
   // nsPIWindowRoot
 
   virtual nsPIDOMWindow* GetWindow() MOZ_OVERRIDE;
 
   virtual nsresult GetControllers(nsIControllers** aResult) MOZ_OVERRIDE;
   virtual nsresult GetControllerForCommand(const char * aCommand,
                                            nsIController** _retval) MOZ_OVERRIDE;
 
+  virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
+                                          nsTArray<nsCString>& aDisabledCommands) MOZ_OVERRIDE;
+
   virtual nsIDOMNode* GetPopupNode() MOZ_OVERRIDE;
   virtual void SetPopupNode(nsIDOMNode* aNode) MOZ_OVERRIDE;
 
   virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) MOZ_OVERRIDE
   {
     mParent = aTarget;
   }
   virtual mozilla::dom::EventTarget* GetParentTarget() MOZ_OVERRIDE { return mParent; }
@@ -67,16 +72,21 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot,
                                                          nsIDOMEventTarget)
 
 protected:
   virtual ~nsWindowRoot();
 
+  void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
+                                                nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
+                                                nsTArray<nsCString>& aEnabledCommands,
+                                                nsTArray<nsCString>& aDisabledCommands);
+
   // Members
   nsCOMPtr<nsPIDOMWindow> mWindow;
   // We own the manager, which owns event listeners attached to us.
   nsRefPtr<mozilla::EventListenerManager> mListenerManager; // [Strong]
   nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER]
 
   nsCOMPtr<mozilla::dom::EventTarget> mParent;
 };
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -557,23 +557,26 @@ ClearWithTempFB(WebGLContext* webgl, GLu
     case LOCAL_GL_RGB:
     case LOCAL_GL_RGBA:
     case LOCAL_GL_BGR:
     case LOCAL_GL_BGRA:
         mask = LOCAL_GL_COLOR_BUFFER_BIT;
         gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                   texImageTarget.get(), tex, level);
         break;
-
+    case LOCAL_GL_DEPTH_COMPONENT32_OES:
+    case LOCAL_GL_DEPTH_COMPONENT24_OES:
+    case LOCAL_GL_DEPTH_COMPONENT16:
     case LOCAL_GL_DEPTH_COMPONENT:
         mask = LOCAL_GL_DEPTH_BUFFER_BIT;
         gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                   texImageTarget.get(), tex, level);
         break;
 
+    case LOCAL_GL_DEPTH24_STENCIL8:
     case LOCAL_GL_DEPTH_STENCIL:
         mask = LOCAL_GL_DEPTH_BUFFER_BIT |
                LOCAL_GL_STENCIL_BUFFER_BIT;
         gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                   texImageTarget.get(), tex, level);
         gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
                                   texImageTarget.get(), tex, level);
         break;
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -26,16 +26,17 @@ XPIDL_SOURCES += [
     'nsIDOMScreen.idl',
     'nsIDOMWindow.idl',
     'nsIDOMWindowCollection.idl',
     'nsIDOMWindowUtils.idl',
     'nsIFocusManager.idl',
     'nsIFrameRequestCallback.idl',
     'nsIIdleObserver.idl',
     'nsIQueryContentEventResult.idl',
+    'nsIRemoteBrowser.idl',
     'nsIServiceWorkerManager.idl',
     'nsIStructuredCloneContainer.idl',
     'nsITabChild.idl',
     'nsITabParent.idl',
 ]
 
 XPIDL_MODULE = 'dom_base'
 
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIRemoteBrowser.idl
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(C8379366-F79F-4D25-89A6-22BEC0A93D16)]
+interface nsIRemoteBrowser : nsISupports
+{
+  /*
+   * Called by the child to inform the parent that a command update has occurred
+   * and the supplied set of commands are now enabled and disabled.
+   *
+   * @param action command updater action
+   * @param enabledLength length of enabledCommands array
+   * @param enabledCommands commands to enable
+   * @param disabledLength length of disabledCommands array
+   * @param disabledCommand commands to disable
+   */
+  void enableDisableCommands(in AString action,
+                             in unsigned long enabledLength,
+                             [array, size_is(enabledLength)] in string enabledCommands,
+                             in unsigned long disabledLength,
+                             [array, size_is(disabledLength)] in string disabledCommands);
+};
+
--- a/dom/interfaces/base/nsITabChild.idl
+++ b/dom/interfaces/base/nsITabChild.idl
@@ -2,18 +2,25 @@
  * 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/. */
 
 
 #include "domstubs.idl"
 interface nsIContentFrameMessageManager;
 interface nsIWebBrowserChrome3;
 
-[scriptable, uuid(2eb3bc54-78bf-40f2-b301-a5b5b70f7da0)]
+native CommandsArray(nsTArray<nsCString>);
+[ref] native CommandsArrayRef(nsTArray<nsCString>);
+
+[scriptable, uuid(7227bac4-b6fe-4090-aeb4-bc288b790925)]
 interface nsITabChild : nsISupports
 {
   readonly attribute nsIContentFrameMessageManager messageManager;
 
   attribute nsIWebBrowserChrome3 webBrowserChrome;
 
   [notxpcom] void sendRequestFocus(in boolean canFocus);
+
+  [noscript, notxpcom] void enableDisableCommands(in AString action,
+                                                  in CommandsArrayRef enabledCommands,
+                                                  in CommandsArrayRef disabledCommands);
 };
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -239,16 +239,24 @@ parent:
     prio(urgent) sync EndIMEComposition(bool cancel) returns (nsString composition);
 
     /**
      * Request that the parent process move focus to the browser's frame. If
      * canRaise is true, the window can be raised if it is inactive.
      */
     RequestFocus(bool canRaise);
 
+    /**
+     * Indicate, based on the current state, that some commands are enabled and
+     * some are disabled.
+     */
+    EnableDisableCommands(nsString action,
+                          nsCString[] enabledCommands,
+                          nsCString[] disabledCommands);
+
     prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled,
                                                  int32_t IMEOpen,
                                                  intptr_t NativeIMEContext);
 
     prio(urgent) async SetInputContext(int32_t IMEEnabled,
                                        int32_t IMEOpen,
                                        nsString type,
                                        nsString inputmode,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3221,16 +3221,25 @@ TabChild::SetWebBrowserChrome(nsIWebBrow
 }
 
 void
 TabChild::SendRequestFocus(bool aCanFocus)
 {
   PBrowserChild::SendRequestFocus(aCanFocus);
 }
 
+void
+TabChild::EnableDisableCommands(const nsAString& aAction,
+                                nsTArray<nsCString>& aEnabledCommands,
+                                nsTArray<nsCString>& aDisabledCommands)
+{
+  PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction),
+                                           aEnabledCommands, aDisabledCommands);
+}
+
 bool
 TabChild::DoSendBlockingMessage(JSContext* aCx,
                                 const nsAString& aMessage,
                                 const StructuredCloneData& aData,
                                 JS::Handle<JSObject *> aCpows,
                                 nsIPrincipal* aPrincipal,
                                 InfallibleTArray<nsString>* aJSONRetVal,
                                 bool aIsSync)
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -42,16 +42,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadInfo.h"
 #include "nsIPromptFactory.h"
 #include "nsIURI.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
+#include "nsIRemoteBrowser.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsOpenURIInFrameParams.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowWatcher.h"
 #include "nsPresShell.h"
 #include "nsPrintfCString.h"
@@ -1462,16 +1463,47 @@ TabParent::RecvRequestFocus(const bool& 
   if (aCanRaise)
     flags |= nsIFocusManager::FLAG_RAISE;
 
   nsCOMPtr<nsIDOMElement> node = do_QueryInterface(mFrameElement);
   fm->SetFocus(node, flags);
   return true;
 }
 
+bool
+TabParent::RecvEnableDisableCommands(const nsString& aAction,
+                                     const nsTArray<nsCString>& aEnabledCommands,
+                                     const nsTArray<nsCString>& aDisabledCommands)
+{
+  nsCOMPtr<nsIRemoteBrowser> remoteBrowser = do_QueryInterface(mFrameElement);
+  if (remoteBrowser) {
+    nsAutoArrayPtr<const char*> enabledCommands, disabledCommands;
+
+    if (aEnabledCommands.Length()) {
+      enabledCommands = new const char* [aEnabledCommands.Length()];
+      for (uint32_t c = 0; c < aEnabledCommands.Length(); c++) {
+        enabledCommands[c] = aEnabledCommands[c].get();
+      }
+    }
+
+    if (aDisabledCommands.Length()) {
+      disabledCommands = new const char* [aDisabledCommands.Length()];
+      for (uint32_t c = 0; c < aDisabledCommands.Length(); c++) {
+        disabledCommands[c] = aDisabledCommands[c].get();
+      }
+    }
+
+    remoteBrowser->EnableDisableCommands(aAction,
+                                         aEnabledCommands.Length(), enabledCommands,
+                                         aDisabledCommands.Length(), disabledCommands);
+  }
+
+  return true;
+}
+
 nsIntPoint
 TabParent::GetChildProcessOffset()
 {
   // The "toplevel widget" in child processes is always at position
   // 0,0.  Map the event coordinates to match that.
 
   nsIntPoint offset(0, 0);
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -186,16 +186,19 @@ public:
     virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
                                      const int32_t& aIMEOpen,
                                      const nsString& aType,
                                      const nsString& aInputmode,
                                      const nsString& aActionHint,
                                      const int32_t& aCause,
                                      const int32_t& aFocusChange) MOZ_OVERRIDE;
     virtual bool RecvRequestFocus(const bool& aCanRaise) MOZ_OVERRIDE;
+    virtual bool RecvEnableDisableCommands(const nsString& aAction,
+                                           const nsTArray<nsCString>& aEnabledCommands,
+                                           const nsTArray<nsCString>& aDisabledCommands) MOZ_OVERRIDE;
     virtual bool RecvSetCursor(const uint32_t& aValue, const bool& aForce) MOZ_OVERRIDE;
     virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE;
     virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE;
     virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible);
     virtual bool RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip);
     virtual bool RecvHideTooltip();
     virtual bool RecvGetDPI(float* aValue) MOZ_OVERRIDE;
     virtual bool RecvGetDefaultScale(double* aValue) MOZ_OVERRIDE;
--- a/dom/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -31,16 +31,20 @@ public:
   static PlatformDecoderModule* Create();
 };
 
 static const AvFormatLib sLibs[] = {
   { "libavformat.so.56", FFmpegDecoderModule<55>::Create, 55 },
   { "libavformat.so.55", FFmpegDecoderModule<55>::Create, 55 },
   { "libavformat.so.54", FFmpegDecoderModule<54>::Create, 54 },
   { "libavformat.so.53", FFmpegDecoderModule<53>::Create, 53 },
+  { "libavformat.56.dylib", FFmpegDecoderModule<55>::Create, 55 },
+  { "libavformat.55.dylib", FFmpegDecoderModule<55>::Create, 55 },
+  { "libavformat.54.dylib", FFmpegDecoderModule<54>::Create, 54 },
+  { "libavformat.53.dylib", FFmpegDecoderModule<53>::Create, 53 },
 };
 
 void* FFmpegRuntimeLinker::sLinkedLib = nullptr;
 const AvFormatLib* FFmpegRuntimeLinker::sLib = nullptr;
 
 #define AV_FUNC(func, ver) void (*func)();
 #define LIBAVCODEC_ALLVERSION
 #include "FFmpegFunctionList.h"
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -101,10 +101,8 @@ MOCHITEST_MANIFESTS += [
     'test/fetch/mochitest.ini',
     'test/mochitest.ini',
     'test/serviceworkers/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
-
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
deleted file mode 100644
--- a/dom/workers/test/browser.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[DEFAULT]
-
-[browser_bug1104623.js]
-run-if = buildapp == 'browser'
deleted file mode 100644
--- a/dom/workers/test/browser_bug1104623.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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/. */
-
-function whenBrowserLoaded(aBrowser, aCallback) {
-  aBrowser.addEventListener("load", function onLoad(event) {
-    if (event.target == aBrowser.contentDocument) {
-      aBrowser.removeEventListener("load", onLoad, true);
-      executeSoon(aCallback);
-    }
-  }, true);
-}
-
-function test() {
-  waitForExplicitFinish();
-
-  let testURL = "chrome://mochitests/content/chrome/dom/base/test/file_empty.html";
-
-  let tab = gBrowser.addTab(testURL);
-  gBrowser.selectedTab = tab;
-
-  whenBrowserLoaded(tab.linkedBrowser, function() {
-    let doc = tab.linkedBrowser.contentDocument;
-
-    let blob = new tab.linkedBrowser.contentWindow.Blob(['onmessage = function() { postMessage(true); }']);
-    ok(blob, "Blob has been created");
-
-    let blobURL = tab.linkedBrowser.contentWindow.URL.createObjectURL(blob);
-    ok(blobURL, "Blob URL has been created");
-
-    let worker = new tab.linkedBrowser.contentWindow.Worker(blobURL);
-    ok(worker, "Worker has been created");
-
-    worker.onerror = function(error) {
-      ok(false, "Worker.onerror:" + error.message);
-      gBrowser.removeTab(tab);
-      finish();
-    }
-
-    worker.onmessage = function() {
-      ok(true, "Worker.onmessage");
-      gBrowser.removeTab(tab);
-      finish();
-    }
-
-    worker.postMessage(true);
-  });
-}
--- a/dom/xul/nsIController.idl
+++ b/dom/xul/nsIController.idl
@@ -2,42 +2,44 @@
 /* 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/. */
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(D5B61B82-1DA4-11d3-BF87-00105A1B0627)]
 interface nsIController : nsISupports {
-	boolean isCommandEnabled(in string command);
-	boolean supportsCommand(in string command);
+  boolean isCommandEnabled(in string command);
+  boolean supportsCommand(in string command);
 
-	void doCommand(in string command);
+  void doCommand(in string command);
 
-	void onEvent(in string eventName);
+  void onEvent(in string eventName);
 };
 
 
 /*
 
   Enhanced controller interface that allows for passing of parameters
   to commands.
   
 */
 
 interface nsICommandParams;
 
-[scriptable, uuid(EBE55080-C8A9-11D5-A73C-DD620D6E04BC)]
+[scriptable, uuid(EEC0B435-7F53-44FE-B00A-CF3EED65C01A)]
 interface nsICommandController : nsISupports
 {
   
   void        getCommandStateWithParams( in string command, in nsICommandParams aCommandParams);
     
   void        doCommandWithParams(in string command, in nsICommandParams aCommandParams);
 
+  void getSupportedCommands(out unsigned long count,
+                            [array, size_is(count), retval] out string commands);
 };
 
 
 /*
   An API for registering commands in groups, to allow for 
   updating via nsIDOMWindow::UpdateCommands.
 */
 interface nsISimpleEnumerator;
--- a/embedding/components/commandhandler/nsBaseCommandController.cpp
+++ b/embedding/components/commandhandler/nsBaseCommandController.cpp
@@ -169,8 +169,15 @@ nsBaseCommandController::GetCommandState
 }
 
 NS_IMETHODIMP
 nsBaseCommandController::OnEvent(const char * aEventName)
 {
   NS_ENSURE_ARG_POINTER(aEventName);
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsBaseCommandController::GetSupportedCommands(uint32_t* aCount, char*** aCommands)
+{
+  NS_ENSURE_STATE(mCommandTable);
+  return mCommandTable->GetSupportedCommands(aCount, aCommands);
+}
--- a/embedding/components/commandhandler/nsControllerCommandTable.cpp
+++ b/embedding/components/commandhandler/nsControllerCommandTable.cpp
@@ -183,16 +183,40 @@ nsControllerCommandTable::GetCommandStat
 #if DEBUG
     NS_WARNING("Controller command table asked to do a command that it does not handle -- ");
 #endif
     return NS_OK;    // we don't handle this command
   }
   return commandHandler->GetCommandStateParams(aCommandName, aParams, aCommandRefCon);
 }
 
+static PLDHashOperator
+AddCommand(const nsACString& aKey, nsIControllerCommand* aData, void* aArg)
+{
+  // aArg is a pointer to a array of strings. It gets incremented after
+  // allocating each one so that it points to the next location for AddCommand
+  // to assign a string to.
+  char*** commands = static_cast<char***>(aArg);
+  (**commands) = ToNewCString(aKey);
+  (*commands)++;
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsControllerCommandTable::GetSupportedCommands(uint32_t* aCount,
+                                               char*** aCommands)
+{
+  char** commands =
+    static_cast<char **>(NS_Alloc(sizeof(char *) * mCommandsTable.Count()));
+  *aCount = mCommandsTable.Count();
+  *aCommands = commands;
+
+  mCommandsTable.EnumerateRead(AddCommand, &commands);
+  return NS_OK;
+}
 
 nsresult
 NS_NewControllerCommandTable(nsIControllerCommandTable** aResult)
 {
   NS_PRECONDITION(aResult != nullptr, "null ptr");
   if (! aResult)
     return NS_ERROR_NULL_POINTER;
 
--- a/embedding/components/commandhandler/nsIControllerCommandTable.idl
+++ b/embedding/components/commandhandler/nsIControllerCommandTable.idl
@@ -13,17 +13,17 @@
  * and efficiently dispatch commands to their respective handlers.
  *
  * Controllers that use an nsIControllerCommandTable should support
  * nsIInterfaceRequestor, and be able to return an interface to their
  * controller command table via getInterface().
  * 
  */
 
-[scriptable, uuid(d1a47834-6ad4-11d7-bfad-000393636592)]
+[scriptable, uuid(c847f90e-b8f3-49db-a4df-8867831f2800)]
 interface nsIControllerCommandTable : nsISupports
 {
   /**
    * Make this command table immutable, so that commands cannot
    * be registered or unregistered. Some command tables are made
    * mutable after command registration so that they can be 
    * used as singletons.
    */
@@ -77,16 +77,19 @@ interface nsIControllerCommandTable : ns
    * @param aCommandName    the name of the command to execute
    * @param aCommandRefCon  the command context data
    */
 	void    doCommand(in string aCommandName, in nsISupports aCommandRefCon);
 
 	void    doCommandParams(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
 
 	void    getCommandState(in string aCommandName, in nsICommandParams aParam, in nsISupports aCommandRefCon);
+
+  void getSupportedCommands(out unsigned long count,
+                            [array, size_is(count), retval] out string commands);
 };
 
 
 
 %{C++
 // {670ee5da-6ad5-11d7-9950-000393636592}
 #define NS_CONTROLLERCOMMANDTABLE_CID \
   {0x670ee5da, 0x6ad5, 0x11d7, \
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -303,17 +303,17 @@ IsIncrementalBarrierNeeded(JSRuntime *rt
 extern JS_FRIEND_API(bool)
 IsIncrementalBarrierNeeded(JSContext *cx);
 
 /*
  * Notify the GC that a reference to a GC thing is about to be overwritten.
  * These methods must be called if IsIncrementalBarrierNeeded.
  */
 extern JS_FRIEND_API(void)
-IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind);
+IncrementalReferenceBarrier(GCCellPtr thing);
 
 extern JS_FRIEND_API(void)
 IncrementalValueBarrier(const Value &v);
 
 extern JS_FRIEND_API(void)
 IncrementalObjectBarrier(JSObject *obj);
 
 /*
@@ -474,82 +474,82 @@ extern JS_FRIEND_API(bool)
 UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind);
 
 } /* namespace JS */
 
 namespace js {
 namespace gc {
 
 static MOZ_ALWAYS_INLINE void
-ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind)
+ExposeGCThingToActiveJS(JS::GCCellPtr thing)
 {
-    MOZ_ASSERT(kind != JSTRACE_SHAPE);
+    MOZ_ASSERT(thing.kind() != JSTRACE_SHAPE);
 
-    JS::shadow::Runtime *rt = GetGCThingRuntime(thing);
+    JS::shadow::Runtime *rt = GetGCThingRuntime(thing.asCell());
 #ifdef JSGC_GENERATIONAL
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
-    if (IsInsideNursery((Cell *)thing))
+    if (IsInsideNursery(thing.asCell()))
         return;
 #endif
-    if (JS::IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing, kind))
-        JS::IncrementalReferenceBarrier(thing, kind);
-    else if (JS::GCThingIsMarkedGray(thing))
-        JS::UnmarkGrayGCThingRecursively(thing, kind);
+    if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
+        JS::IncrementalReferenceBarrier(thing);
+    else if (JS::GCThingIsMarkedGray(thing.asCell()))
+        JS::UnmarkGrayGCThingRecursively(thing.asCell(), thing.kind());
+}
+
+static MOZ_ALWAYS_INLINE void
+MarkGCThingAsLive(JSRuntime *aRt, JS::GCCellPtr thing)
+{
+    JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(aRt);
+#ifdef JSGC_GENERATIONAL
+    /*
+     * Any object in the nursery will not be freed during any GC running at that time.
+     */
+    if (IsInsideNursery(thing.asCell()))
+        return;
+#endif
+    if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
+        JS::IncrementalReferenceBarrier(thing);
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
 
 /*
  * This should be called when an object that is marked gray is exposed to the JS
  * engine (by handing it to running JS code or writing it into live JS
  * data). During incremental GC, since the gray bits haven't been computed yet,
  * we conservatively mark the object black.
  */
 static MOZ_ALWAYS_INLINE void
 ExposeObjectToActiveJS(JSObject *obj)
 {
-    js::gc::ExposeGCThingToActiveJS(obj, JSTRACE_OBJECT);
+    js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
 }
 
 static MOZ_ALWAYS_INLINE void
 ExposeScriptToActiveJS(JSScript *script)
 {
-    js::gc::ExposeGCThingToActiveJS(script, JSTRACE_SCRIPT);
+    js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
 }
 
 /*
- * If a GC is currently marking, mark the object black.
+ * If a GC is currently marking, mark the string black.
  */
 static MOZ_ALWAYS_INLINE void
-MarkGCThingAsLive(JSRuntime *rt_, void *thing, JSGCTraceKind kind)
-{
-    shadow::Runtime *rt = shadow::Runtime::asShadowRuntime(rt_);
-#ifdef JSGC_GENERATIONAL
-    /*
-     * Any object in the nursery will not be freed during any GC running at that time.
-     */
-    if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
-        return;
-#endif
-    if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing, kind))
-        IncrementalReferenceBarrier(thing, kind);
-}
-
-static MOZ_ALWAYS_INLINE void
 MarkStringAsLive(Zone *zone, JSString *string)
 {
     JSRuntime *rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromMainThread();
-    MarkGCThingAsLive(rt, string, JSTRACE_STRING);
+    js::gc::MarkGCThingAsLive(rt, GCCellPtr(string));
 }
 
 /*
  * Internal to Firefox.
  *
  * Note: this is not related to the PokeGC in nsJSEnvironment.
  */
 extern JS_FRIEND_API(void)
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -4,18 +4,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/. */
 
 #ifndef js_HeapAPI_h
 #define js_HeapAPI_h
 
 #include <limits.h>
 
-#include "jspubtd.h"
-
+#include "js/TracingAPI.h"
 #include "js/Utility.h"
 
 /* These values are private to the JS engine. */
 namespace js {
 
 // Whether the current thread is permitted access to any part of the specified
 // runtime or zone.
 JS_FRIEND_API(bool)
@@ -199,16 +198,105 @@ struct Zone
     }
 
     static JS::shadow::Zone *asShadowZone(JS::Zone *zone) {
         return reinterpret_cast<JS::shadow::Zone*>(zone);
     }
 };
 
 } /* namespace shadow */
+
+// A GC pointer, tagged with the trace kind.
+//
+// In general, a GC pointer should be stored with an exact type. This class
+// is for use when that is not possible because a single pointer must point
+// to several kinds of GC thing.
+class JS_FRIEND_API(GCCellPtr)
+{
+    typedef void (GCCellPtr::* ConvertibleToBool)();
+    void nonNull() {}
+
+  public:
+    // Construction from a void* and trace kind.
+    GCCellPtr(void *gcthing, JSGCTraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
+
+    // Construction from an explicit type.
+    explicit GCCellPtr(JSObject *obj) : ptr(checkedCast(obj, JSTRACE_OBJECT)) { }
+    explicit GCCellPtr(JSFunction *fun) : ptr(checkedCast(fun, JSTRACE_OBJECT)) { }
+    explicit GCCellPtr(JSString *str) : ptr(checkedCast(str, JSTRACE_STRING)) { }
+    explicit GCCellPtr(JSFlatString *str) : ptr(checkedCast(str, JSTRACE_STRING)) { }
+    explicit GCCellPtr(JSScript *script) : ptr(checkedCast(script, JSTRACE_SCRIPT)) { }
+    explicit GCCellPtr(const Value &v);
+
+    // Not all compilers have nullptr_t yet, so use this instead of GCCellPtr(nullptr).
+    static GCCellPtr NullPtr() { return GCCellPtr(nullptr, JSTRACE_NULL); }
+
+    JSGCTraceKind kind() const {
+        JSGCTraceKind traceKind = JSGCTraceKind(ptr & JSTRACE_OUTOFLINE);
+        if (traceKind != JSTRACE_OUTOFLINE)
+            return traceKind;
+        return outOfLineKind();
+    }
+
+    // Allow GCCellPtr to be used in a boolean context.
+    operator ConvertibleToBool() const {
+        MOZ_ASSERT(bool(asCell()) == (kind() != JSTRACE_NULL));
+        return asCell() ? &GCCellPtr::nonNull : 0;
+    }
+
+    // Simplify checks to the kind.
+    bool isObject() const { return kind() == JSTRACE_OBJECT; }
+    bool isScript() const { return kind() == JSTRACE_SCRIPT; }
+    bool isString() const { return kind() == JSTRACE_STRING; }
+    bool isSymbol() const { return kind() == JSTRACE_SYMBOL; }
+
+    // Conversions to more specific types must match the kind. Access to
+    // further refined types is not allowed directly from a GCCellPtr.
+    JSObject *toObject() const {
+        MOZ_ASSERT(kind() == JSTRACE_OBJECT);
+        return reinterpret_cast<JSObject *>(asCell());
+    }
+    JSString *toString() const {
+        MOZ_ASSERT(kind() == JSTRACE_STRING);
+        return reinterpret_cast<JSString *>(asCell());
+    }
+    JSScript *toScript() const {
+        MOZ_ASSERT(kind() == JSTRACE_SCRIPT);
+        return reinterpret_cast<JSScript *>(asCell());
+    }
+    Symbol *toSymbol() const {
+        MOZ_ASSERT(kind() == JSTRACE_SYMBOL);
+        return reinterpret_cast<Symbol *>(asCell());
+    }
+    js::gc::Cell *asCell() const {
+        return reinterpret_cast<js::gc::Cell *>(ptr & ~JSTRACE_OUTOFLINE);
+    }
+
+    // The CC's trace logger needs an identity that is XPIDL serializable.
+    void *unsafeGetUntypedPtr() const {
+        return reinterpret_cast<void *>(asCell());
+    }
+
+  private:
+    uintptr_t checkedCast(void *p, JSGCTraceKind traceKind) {
+        js::gc::Cell *cell = static_cast<js::gc::Cell *>(p);
+        MOZ_ASSERT((uintptr_t(p) & JSTRACE_OUTOFLINE) == 0);
+        AssertGCThingHasType(cell, traceKind);
+        // Note: the JSTRACE_OUTOFLINE bits are set on all out-of-line kinds
+        // so that we can mask instead of branching.
+        MOZ_ASSERT_IF(traceKind >= JSTRACE_OUTOFLINE,
+                      (traceKind & JSTRACE_OUTOFLINE) == JSTRACE_OUTOFLINE);
+        return uintptr_t(p) | (traceKind & JSTRACE_OUTOFLINE);
+    }
+
+    JSGCTraceKind outOfLineKind() const;
+
+    uintptr_t ptr;
+};
+
 } /* namespace JS */
 
 namespace js {
 namespace gc {
 
 static MOZ_ALWAYS_INLINE uintptr_t *
 GetGCThingMarkBitmap(const void *thing)
 {
@@ -263,17 +351,16 @@ IsInsideNursery(const js::gc::Cell *cell
     MOZ_ASSERT(location != 0);
     return location & ChunkLocationAnyNursery;
 #else
     return false;
 #endif
 }
 
 } /* namespace gc */
-
 } /* namespace js */
 
 namespace JS {
 
 static MOZ_ALWAYS_INLINE Zone *
 GetTenuredGCThingZone(void *thing)
 {
     MOZ_ASSERT(thing);
@@ -299,24 +386,30 @@ GCThingIsMarkedGray(void *thing)
     if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
         return false;
 #endif
     uintptr_t *word, mask;
     js::gc::GetGCThingMarkWordAndMask(thing, js::gc::GRAY, &word, &mask);
     return *word & mask;
 }
 
+} /* namespace JS */
+
+namespace js {
+namespace gc {
+
 static MOZ_ALWAYS_INLINE bool
-IsIncrementalBarrierNeededOnTenuredGCThing(shadow::Runtime *rt, void *thing, JSGCTraceKind kind)
+IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime *rt, const JS::GCCellPtr thing)
 {
     MOZ_ASSERT(thing);
 #ifdef JSGC_GENERATIONAL
-    MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing));
+    MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
 #endif
     if (!rt->needsIncrementalBarrier())
         return false;
-    JS::Zone *zone = GetTenuredGCThingZone(thing);
-    return reinterpret_cast<shadow::Zone *>(zone)->needsIncrementalBarrier();
+    JS::Zone *zone = JS::GetTenuredGCThingZone(thing.asCell());
+    return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
 }
 
-} /* namespace JS */
+} /* namespace gc */
+} /* namespace js */
 
 #endif /* js_HeapAPI_h */
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -16,16 +16,59 @@
 
 class JS_PUBLIC_API(JSTracer);
 
 namespace JS {
 template <typename T> class Heap;
 template <typename T> class TenuredHeap;
 }
 
+// When tracing a thing, the GC needs to know about the layout of the object it
+// is looking at. There are a fixed number of different layouts that the GC
+// knows about. The "trace kind" is a static map which tells which layout a GC
+// thing has.
+//
+// Although this map is public, the details are completely hidden. Not all of
+// the matching C++ types are exposed, and those that are, are opaque.
+//
+// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
+enum JSGCTraceKind
+{
+    // These trace kinds have a publicly exposed, although opaque, C++ type.
+    // Note: The order here is determined by our Value packing. Other users
+    //       should sort alphabetically, for consistency.
+    JSTRACE_OBJECT = 0x00,
+    JSTRACE_STRING = 0x01,
+    JSTRACE_SYMBOL = 0x02,
+    JSTRACE_SCRIPT = 0x03,
+
+    // Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
+    JSTRACE_SHAPE = 0x04,
+
+    // The kind associated with a nullptr.
+    JSTRACE_NULL = 0x06,
+
+    // A kind that indicates the real kind should be looked up in the arena.
+    JSTRACE_OUTOFLINE = 0x07,
+
+    // The following kinds do not have an exposed C++ idiom.
+    JSTRACE_BASE_SHAPE = 0x0F,
+    JSTRACE_JITCODE = 0x1F,
+    JSTRACE_LAZY_SCRIPT = 0x2F,
+    JSTRACE_TYPE_OBJECT = 0x3F,
+
+    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
+};
+
+namespace JS {
+// Returns a static string equivalent of |kind|.
+JS_FRIEND_API(const char *)
+GCTraceKindToAscii(JSGCTraceKind kind);
+}
+
 // Tracer callback, called for each traceable thing directly referenced by a
 // particular object or runtime structure. It is the callback responsibility
 // to ensure the traversal of the full object graph via calling eventually
 // JS_TraceChildren on the passed thing. In this case the callback must be
 // prepared to deal with cycles in the traversal graph.
 //
 // kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
 // internal implementation-specific traversal kind. In the latter case the only
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1364,17 +1364,17 @@ IsOptimizedPlaceholderMagicValue(const V
     }
     return false;
 }
 
 static MOZ_ALWAYS_INLINE void
 ExposeValueToActiveJS(const Value &v)
 {
     if (v.isMarkable())
-        js::gc::ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind());
+        js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
 }
 
 /************************************************************************/
 
 static inline Value
 NullValue()
 {
     Value v;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -684,40 +684,42 @@ gc::MarkKind(JSTracer *trc, void **thing
     MOZ_ASSERT(*thingp);
     DebugOnly<Cell *> cell = static_cast<Cell *>(*thingp);
     MOZ_ASSERT_IF(cell->isTenured(),
                   kind == MapAllocToTraceKind(cell->asTenured().getAllocKind()));
     switch (kind) {
       case JSTRACE_OBJECT:
         MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
         break;
+      case JSTRACE_SCRIPT:
+        MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
+        break;
       case JSTRACE_STRING:
         MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
         break;
       case JSTRACE_SYMBOL:
         MarkInternal(trc, reinterpret_cast<JS::Symbol **>(thingp));
         break;
-      case JSTRACE_SCRIPT:
-        MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
+      case JSTRACE_BASE_SHAPE:
+        MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
+        break;
+      case JSTRACE_JITCODE:
+        MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
         break;
       case JSTRACE_LAZY_SCRIPT:
         MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
         break;
       case JSTRACE_SHAPE:
         MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
         break;
-      case JSTRACE_BASE_SHAPE:
-        MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
-        break;
       case JSTRACE_TYPE_OBJECT:
         MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
         break;
-      case JSTRACE_JITCODE:
-        MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
-        break;
+      default:
+        MOZ_CRASH("Invalid trace kind in MarkKind.");
     }
 }
 
 static void
 MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
 {
     trc->setTracingName(name);
     MOZ_ASSERT(thingp);
@@ -1537,47 +1539,50 @@ PushArenaTyped(GCMarker *gcmarker, Arena
 void
 gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
 {
     switch (MapAllocToTraceKind(aheader->getAllocKind())) {
       case JSTRACE_OBJECT:
         PushArenaTyped<JSObject>(gcmarker, aheader);
         break;
 
+      case JSTRACE_SCRIPT:
+        PushArenaTyped<JSScript>(gcmarker, aheader);
+        break;
+
       case JSTRACE_STRING:
         PushArenaTyped<JSString>(gcmarker, aheader);
         break;
 
       case JSTRACE_SYMBOL:
         PushArenaTyped<JS::Symbol>(gcmarker, aheader);
         break;
 
-      case JSTRACE_SCRIPT:
-        PushArenaTyped<JSScript>(gcmarker, aheader);
+      case JSTRACE_BASE_SHAPE:
+        PushArenaTyped<js::BaseShape>(gcmarker, aheader);
+        break;
+
+      case JSTRACE_JITCODE:
+        PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         PushArenaTyped<LazyScript>(gcmarker, aheader);
         break;
 
       case JSTRACE_SHAPE:
         PushArenaTyped<js::Shape>(gcmarker, aheader);
         break;
 
-      case JSTRACE_BASE_SHAPE:
-        PushArenaTyped<js::BaseShape>(gcmarker, aheader);
-        break;
-
       case JSTRACE_TYPE_OBJECT:
         PushArenaTyped<js::types::TypeObject>(gcmarker, aheader);
         break;
 
-      case JSTRACE_JITCODE:
-        PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
-        break;
+      default:
+        MOZ_CRASH("Invalid trace kind in PushArena.");
     }
 }
 
 struct SlotArrayLayout
 {
     union {
         HeapSlot *end;
         uintptr_t kind;
@@ -1941,47 +1946,50 @@ GCMarker::drainMarkStack(SliceBudget &bu
 void
 js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     switch (kind) {
       case JSTRACE_OBJECT:
         MarkChildren(trc, static_cast<JSObject *>(thing));
         break;
 
+      case JSTRACE_SCRIPT:
+        MarkChildren(trc, static_cast<JSScript *>(thing));
+        break;
+
       case JSTRACE_STRING:
         MarkChildren(trc, static_cast<JSString *>(thing));
         break;
 
       case JSTRACE_SYMBOL:
         MarkChildren(trc, static_cast<JS::Symbol *>(thing));
         break;
 
-      case JSTRACE_SCRIPT:
-        MarkChildren(trc, static_cast<JSScript *>(thing));
+      case JSTRACE_BASE_SHAPE:
+        MarkChildren(trc, static_cast<BaseShape *>(thing));
+        break;
+
+      case JSTRACE_JITCODE:
+        MarkChildren(trc, (js::jit::JitCode *)thing);
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         MarkChildren(trc, static_cast<LazyScript *>(thing));
         break;
 
       case JSTRACE_SHAPE:
         MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
-      case JSTRACE_JITCODE:
-        MarkChildren(trc, (js::jit::JitCode *)thing);
-        break;
-
-      case JSTRACE_BASE_SHAPE:
-        MarkChildren(trc, static_cast<BaseShape *>(thing));
-        break;
-
       case JSTRACE_TYPE_OBJECT:
         MarkChildren(trc, (types::TypeObject *)thing);
         break;
+
+      default:
+        MOZ_CRASH("Invalid trace kind in TraceChildren.");
     }
 }
 
 #ifdef DEBUG
 static void
 AssertNonGrayGCThing(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     MOZ_ASSERT(!JS::GCThingIsMarkedGray(*thingp));
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -400,46 +400,16 @@ ToMarkable(const Value &v)
 }
 
 inline Cell *
 ToMarkable(Cell *cell)
 {
     return cell;
 }
 
-inline JSGCTraceKind
-TraceKind(const Value &v)
-{
-    MOZ_ASSERT(v.isMarkable());
-    if (v.isObject())
-        return JSTRACE_OBJECT;
-    if (v.isString())
-        return JSTRACE_STRING;
-    MOZ_ASSERT(v.isSymbol());
-    return JSTRACE_SYMBOL;
-}
-
-inline JSGCTraceKind
-TraceKind(JSObject *obj)
-{
-    return JSTRACE_OBJECT;
-}
-
-inline JSGCTraceKind
-TraceKind(JSScript *script)
-{
-    return JSTRACE_SCRIPT;
-}
-
-inline JSGCTraceKind
-TraceKind(LazyScript *lazy)
-{
-    return JSTRACE_LAZY_SCRIPT;
-}
-
 } /* namespace gc */
 
 void
 TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 } /* namespace js */
 
 #endif /* gc_Marking_h */
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -198,48 +198,52 @@ JS_GetTraceThingInfo(char *buf, size_t b
 
     switch (kind) {
       case JSTRACE_OBJECT:
       {
         name = static_cast<JSObject *>(thing)->getClass()->name;
         break;
       }
 
+      case JSTRACE_SCRIPT:
+        name = "script";
+        break;
+
       case JSTRACE_STRING:
         name = ((JSString *)thing)->isDependent()
                ? "substring"
                : "string";
         break;
 
       case JSTRACE_SYMBOL:
         name = "symbol";
         break;
 
-      case JSTRACE_SCRIPT:
-        name = "script";
+      case JSTRACE_BASE_SHAPE:
+        name = "base_shape";
+        break;
+
+      case JSTRACE_JITCODE:
+        name = "jitcode";
         break;
 
       case JSTRACE_LAZY_SCRIPT:
         name = "lazyscript";
         break;
 
-      case JSTRACE_JITCODE:
-        name = "jitcode";
-        break;
-
       case JSTRACE_SHAPE:
         name = "shape";
         break;
 
-      case JSTRACE_BASE_SHAPE:
-        name = "base_shape";
+      case JSTRACE_TYPE_OBJECT:
+        name = "type_object";
         break;
 
-      case JSTRACE_TYPE_OBJECT:
-        name = "type_object";
+      default:
+        name = "INVALID";
         break;
     }
 
     n = strlen(name);
     if (n > bufsize - 1)
         n = bufsize - 1;
     js_memcpy(buf, name, n + 1);
     buf += n;
@@ -261,16 +265,23 @@ JS_GetTraceThingInfo(char *buf, size_t b
             } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
                 JS_snprintf(buf, bufsize, " %p", obj->as<NativeObject>().getPrivate());
             } else {
                 JS_snprintf(buf, bufsize, " <no private>");
             }
             break;
           }
 
+          case JSTRACE_SCRIPT:
+          {
+            JSScript *script = static_cast<JSScript *>(thing);
+            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno()));
+            break;
+          }
+
           case JSTRACE_STRING:
           {
             *buf++ = ' ';
             bufsize--;
             JSString *str = (JSString *)thing;
 
             if (str->isLinear()) {
                 bool willFit = str->length() + strlen("<length > ") +
@@ -301,28 +312,17 @@ JS_GetTraceThingInfo(char *buf, size_t b
                     JS_snprintf(buf, bufsize, "<nonlinear desc>");
                 }
             } else {
                 JS_snprintf(buf, bufsize, "<null>");
             }
             break;
           }
 
-          case JSTRACE_SCRIPT:
-          {
-            JSScript *script = static_cast<JSScript *>(thing);
-            JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno()));
-            break;
-          }
-
-          case JSTRACE_LAZY_SCRIPT:
-          case JSTRACE_JITCODE:
-          case JSTRACE_SHAPE:
-          case JSTRACE_BASE_SHAPE:
-          case JSTRACE_TYPE_OBJECT:
+          default:
             break;
         }
     }
     buf[bufsize - 1] = '\0';
 }
 
 JSTracer::JSTracer(JSRuntime *rt, JSTraceCallback traceCallback,
                    WeakMapTraceKind weakTraceKind /* = TraceWeakMapValues */)
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -28,16 +28,17 @@ UNIFIED_SOURCES += [
     'testErrorCopying.cpp',
     'testException.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForOfIterator.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
+    'testGCCellPtr.cpp',
     'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCMarking.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testHashTable.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testGCCellPtr.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* vim: set ts=8 sts=4 et sw=4 tw=99:
+*/
+/* 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/. */
+
+#include "jsapi.h"
+#include "jspubtd.h"
+
+#include "gc/Heap.h"
+
+#include "jsapi-tests/tests.h"
+
+JS::GCCellPtr
+GivesAndTakesCells(JS::GCCellPtr cell)
+{
+    return cell;
+}
+
+BEGIN_TEST(testGCCellPtr)
+{
+    JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
+    CHECK(obj);
+
+    JS::RootedString str(cx, JS_NewStringCopyZ(cx, "probably foobar"));
+    CHECK(str);
+
+    const char *code = "function foo() { return 'bar'; }";
+    JS::CompileOptions opts(cx);
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, obj, code, strlen(code), opts, &script));
+    CHECK(script);
+
+    CHECK(!JS::GCCellPtr::NullPtr());
+
+    CHECK(JS::GCCellPtr(obj.get()));
+    CHECK(JS::GCCellPtr(obj.get()).kind() == JSTRACE_OBJECT);
+    CHECK(JS::GCCellPtr(JS::ObjectValue(*obj)).kind() == JSTRACE_OBJECT);
+
+    CHECK(JS::GCCellPtr(str.get()));
+    CHECK(JS::GCCellPtr(str.get()).kind() == JSTRACE_STRING);
+    CHECK(JS::GCCellPtr(JS::StringValue(str)).kind() == JSTRACE_STRING);
+
+    CHECK(JS::GCCellPtr(script.get()));
+    CHECK(!JS::GCCellPtr::NullPtr());
+    CHECK(JS::GCCellPtr(script.get()).kind() == JSTRACE_SCRIPT);
+
+    JS::GCCellPtr objcell(obj.get());
+    JS::GCCellPtr scriptcell = JS::GCCellPtr(script.get());
+    CHECK(GivesAndTakesCells(objcell));
+    CHECK(GivesAndTakesCells(scriptcell));
+
+    JS::GCCellPtr copy = objcell;
+    CHECK(copy == objcell);
+
+    CHECK(js::gc::GetGCThingRuntime(scriptcell.asCell()) == rt);
+
+    return true;
+}
+END_TEST(testGCCellPtr)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1173,53 +1173,49 @@ JS::IncrementalObjectBarrier(JSObject *o
         return;
 
     MOZ_ASSERT(!obj->zone()->runtimeFromMainThread()->isHeapMajorCollecting());
 
     JSObject::writeBarrierPre(obj);
 }
 
 JS_FRIEND_API(void)
-JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
+JS::IncrementalReferenceBarrier(GCCellPtr thing)
 {
-    if (!ptr)
+    if (!thing)
         return;
 
-    if (kind == JSTRACE_STRING && StringIsPermanentAtom(static_cast<JSString *>(ptr)))
+    if (thing.isString() && StringIsPermanentAtom(thing.toString()))
         return;
 
-    gc::Cell *cell = static_cast<gc::Cell *>(ptr);
-
 #ifdef DEBUG
-    Zone *zone = kind == JSTRACE_OBJECT
-                 ? static_cast<JSObject *>(cell)->zone()
-                 : cell->asTenured().zone();
+    Zone *zone = thing.isObject()
+                 ? thing.toObject()->zone()
+                 : thing.asCell()->asTenured().zone();
     MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting());
 #endif
 
-    if (kind == JSTRACE_OBJECT)
-        JSObject::writeBarrierPre(static_cast<JSObject*>(cell));
-    else if (kind == JSTRACE_STRING)
-        JSString::writeBarrierPre(static_cast<JSString*>(cell));
-    else if (kind == JSTRACE_SYMBOL)
-        JS::Symbol::writeBarrierPre(static_cast<JS::Symbol*>(cell));
-    else if (kind == JSTRACE_SCRIPT)
-        JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
-    else if (kind == JSTRACE_LAZY_SCRIPT)
-        LazyScript::writeBarrierPre(static_cast<LazyScript*>(cell));
-    else if (kind == JSTRACE_JITCODE)
-        jit::JitCode::writeBarrierPre(static_cast<jit::JitCode*>(cell));
-    else if (kind == JSTRACE_SHAPE)
-        Shape::writeBarrierPre(static_cast<Shape*>(cell));
-    else if (kind == JSTRACE_BASE_SHAPE)
-        BaseShape::writeBarrierPre(static_cast<BaseShape*>(cell));
-    else if (kind == JSTRACE_TYPE_OBJECT)
-        types::TypeObject::writeBarrierPre(static_cast<types::TypeObject *>(cell));
-    else
-        MOZ_CRASH("invalid trace kind");
+    switch(thing.kind()) {
+      case JSTRACE_OBJECT: return JSObject::writeBarrierPre(thing.toObject());
+      case JSTRACE_STRING: return JSString::writeBarrierPre(thing.toString());
+      case JSTRACE_SCRIPT: return JSScript::writeBarrierPre(thing.toScript());
+      case JSTRACE_SYMBOL: return JS::Symbol::writeBarrierPre(thing.toSymbol());
+      case JSTRACE_LAZY_SCRIPT:
+        return LazyScript::writeBarrierPre(static_cast<LazyScript*>(thing.asCell()));
+      case JSTRACE_JITCODE:
+        return jit::JitCode::writeBarrierPre(static_cast<jit::JitCode*>(thing.asCell()));
+      case JSTRACE_SHAPE:
+        return Shape::writeBarrierPre(static_cast<Shape*>(thing.asCell()));
+      case JSTRACE_BASE_SHAPE:
+        return BaseShape::writeBarrierPre(static_cast<BaseShape*>(thing.asCell()));
+      case JSTRACE_TYPE_OBJECT:
+        return types::TypeObject::writeBarrierPre(static_cast<types::TypeObject *>(thing.asCell()));
+      default:
+        MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier.");
+    }
 }
 
 JS_FRIEND_API(void)
 JS::IncrementalValueBarrier(const Value &v)
 {
     js::HeapValue::writeBarrierPre(v);
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -488,19 +488,17 @@ struct WeakMapTracer;
 
 /*
  * Weak map tracer callback, called once for every binding of every
  * weak map that was live at the time of the last garbage collection.
  *
  * m will be nullptr if the weak map is not contained in a JS Object.
  */
 typedef void
-(* WeakMapTraceCallback)(WeakMapTracer *trc, JSObject *m,
-                         void *k, JSGCTraceKind kkind,
-                         void *v, JSGCTraceKind vkind);
+(* WeakMapTraceCallback)(WeakMapTracer *trc, JSObject *m, JS::GCCellPtr key, JS::GCCellPtr value);
 
 struct WeakMapTracer {
     JSRuntime            *runtime;
     WeakMapTraceCallback callback;
 
     WeakMapTracer(JSRuntime *rt, WeakMapTraceCallback cb)
         : runtime(rt), callback(cb) {}
 };
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -323,33 +323,16 @@ const uint32_t Arena::FirstThingOffsets[
     OFFSET(JSString),           /* FINALIZE_STRING              */
     OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     OFFSET(JS::Symbol),         /* FINALIZE_SYMBOL              */
     OFFSET(jit::JitCode),       /* FINALIZE_JITCODE             */
 };
 
 #undef OFFSET
 
-const char *
-js::gc::TraceKindAsAscii(JSGCTraceKind kind)
-{
-    switch(kind) {
-      case JSTRACE_OBJECT: return "JSTRACE_OBJECT";
-      case JSTRACE_STRING: return "JSTRACE_STRING";
-      case JSTRACE_SYMBOL: return "JSTRACE_SYMBOL";
-      case JSTRACE_SCRIPT: return "JSTRACE_SCRIPT";
-      case JSTRACE_LAZY_SCRIPT: return "JSTRACE_SCRIPT";
-      case JSTRACE_JITCODE: return "JSTRACE_JITCODE";
-      case JSTRACE_SHAPE: return "JSTRACE_SHAPE";
-      case JSTRACE_BASE_SHAPE: return "JSTRACE_BASE_SHAPE";
-      case JSTRACE_TYPE_OBJECT: return "JSTRACE_TYPE_OBJECT";
-      default: return "INVALID";
-    }
-}
-
 struct js::gc::FinalizePhase
 {
     size_t length;
     const AllocKind *kinds;
     gcstats::Phase statsPhase;
 };
 
 #define PHASE(x, p) { ArrayLength(x), x, p }
@@ -6951,18 +6934,19 @@ JS::AssertGCThingMustBeTenured(JSObject 
 {
     MOZ_ASSERT(obj->isTenured() &&
                (!IsNurseryAllocable(obj->asTenured().getAllocKind()) || obj->getClass()->finalize));
 }
 
 JS_FRIEND_API(void)
 js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind)
 {
-    MOZ_ASSERT(cell);
-    if (IsInsideNursery(cell))
+    if (!cell)
+        MOZ_ASSERT(kind == JSTRACE_NULL);
+    else if (IsInsideNursery(cell))
         MOZ_ASSERT(kind == JSTRACE_OBJECT);
     else
         MOZ_ASSERT(MapAllocToTraceKind(cell->asTenured().getAllocKind()) == kind);
 }
 
 JS_FRIEND_API(size_t)
 JS::GetGCNumber()
 {
@@ -7041,16 +7025,54 @@ JS::AutoAssertNoAlloc::~AutoAssertNoAllo
 #endif
 
 JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj)
   : AutoSuppressGCAnalysis()
 {
     MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting());
 }
 
+JS_FRIEND_API(const char *)
+JS::GCTraceKindToAscii(JSGCTraceKind kind)
+{
+    switch(kind) {
+      case JSTRACE_OBJECT: return "Object";
+      case JSTRACE_SCRIPT: return "Script";
+      case JSTRACE_STRING: return "String";
+      case JSTRACE_SYMBOL: return "Symbol";
+      case JSTRACE_SHAPE: return "Shape";
+      case JSTRACE_BASE_SHAPE: return "BaseShape";
+      case JSTRACE_LAZY_SCRIPT: return "LazyScript";
+      case JSTRACE_JITCODE: return "JitCode";
+      case JSTRACE_TYPE_OBJECT: return "TypeObject";
+      default: return "Invalid";
+    }
+}
+
+JS::GCCellPtr::GCCellPtr(const Value &v)
+  : ptr(0)
+{
+    if (v.isString())
+        ptr = checkedCast(v.toString(), JSTRACE_STRING);
+    else if (v.isObject())
+        ptr = checkedCast(&v.toObject(), JSTRACE_OBJECT);
+    else if (v.isSymbol())
+        ptr = checkedCast(v.toSymbol(), JSTRACE_SYMBOL);
+    else
+        ptr = checkedCast(nullptr, JSTRACE_NULL);
+}
+
+JSGCTraceKind
+JS::GCCellPtr::outOfLineKind() const
+{
+    MOZ_ASSERT(JSGCTraceKind(ptr & JSTRACE_OUTOFLINE) == JSTRACE_OUTOFLINE);
+    MOZ_ASSERT(asCell()->isTenured());
+    return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
+}
+
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -56,20 +56,16 @@ struct FinalizePhase;
 enum State {
     NO_INCREMENTAL,
     MARK_ROOTS,
     MARK,
     SWEEP,
     COMPACT
 };
 
-/* Return a printable string for the given kind, for diagnostic purposes. */
-const char *
-TraceKindAsAscii(JSGCTraceKind kind);
-
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; };
 template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const AllocKind kind = FINALIZE_TYPE_OBJECT; };
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -97,35 +97,16 @@ enum JSIterateOp {
 
     /* Iterate once. */
     JSENUMERATE_NEXT,
 
     /* Destroy iterator state. */
     JSENUMERATE_DESTROY
 };
 
-/* See Value::gcKind() and JSTraceCallback in Tracer.h. */
-enum JSGCTraceKind {
-    JSTRACE_OBJECT,
-    JSTRACE_STRING,
-    JSTRACE_SYMBOL,
-    JSTRACE_SCRIPT,
-
-    /*
-     * Trace kinds internal to the engine. The embedding can only see them if
-     * it implements JSTraceCallback.
-     */
-    JSTRACE_LAZY_SCRIPT,
-    JSTRACE_JITCODE,
-    JSTRACE_SHAPE,
-    JSTRACE_BASE_SHAPE,
-    JSTRACE_TYPE_OBJECT,
-    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
-};
-
 /* Struct forward declarations. */
 struct JSClass;
 struct JSCompartment;
 struct JSCrossCompartmentCall;
 class JSErrorReport;
 struct JSExceptionState;
 struct JSFunctionSpec;
 struct JSIdArray;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -532,36 +532,16 @@ js_str_toString(JSContext *cx, unsigned 
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsString, str_toString_impl>(cx, args);
 }
 
 /*
  * Java-like string native methods.
  */
 
-static MOZ_ALWAYS_INLINE bool
-ValueToIntegerRange(JSContext *cx, HandleValue v, int32_t *out)
-{
-    if (v.isInt32()) {
-        *out = v.toInt32();
-    } else {
-        double d;
-        if (!ToInteger(cx, v, &d))
-            return false;
-        if (d > INT32_MAX)
-            *out = INT32_MAX;
-        else if (d < INT32_MIN)
-            *out = INT32_MIN;
-        else
-            *out = int32_t(d);
-    }
-
-    return true;
-}
-
 JSString *
 js::SubstringKernel(JSContext *cx, HandleString str, int32_t beginInt, int32_t lengthInt)
 {
     MOZ_ASSERT(0 <= beginInt);
     MOZ_ASSERT(0 <= lengthInt);
     MOZ_ASSERT(uint32_t(beginInt) <= str->length());
     MOZ_ASSERT(uint32_t(lengthInt) <= str->length() - beginInt);
 
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -241,12 +241,12 @@ WatchpointMap::traceAll(WeakMapTracer *t
 }
 
 void
 WatchpointMap::trace(WeakMapTracer *trc)
 {
     for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
         Map::Entry &entry = r.front();
         trc->callback(trc, nullptr,
-                      entry.key().object.get(), JSTRACE_OBJECT,
-                      entry.value().closure.get(), JSTRACE_OBJECT);
+                      JS::GCCellPtr(entry.key().object.get()),
+                      JS::GCCellPtr(entry.value().closure.get()));
     }
 }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -246,18 +246,18 @@ class WeakMap : public HashMap<Key, Valu
 
     /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
     void traceMappings(WeakMapTracer *tracer) {
         for (Range r = Base::all(); !r.empty(); r.popFront()) {
             gc::Cell *key = gc::ToMarkable(r.front().key());
             gc::Cell *value = gc::ToMarkable(r.front().value());
             if (key && value) {
                 tracer->callback(tracer, memberOf,
-                                 key, gc::TraceKind(r.front().key()),
-                                 value, gc::TraceKind(r.front().value()));
+                                 JS::GCCellPtr(r.front().key()),
+                                 JS::GCCellPtr(r.front().value()));
             }
         }
     }
 
     /* Rekey an entry when moved, ensuring we do not trigger barriers. */
     void entryMoved(Enum &eArg, const Key &k) {
         typedef typename HashMap<typename Unbarriered<Key>::type,
                                  typename Unbarriered<Value>::type,
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -423,84 +423,16 @@ StatsCellCallback(JSRuntime *rt, void *d
         if (ObjectPrivateVisitor *opv = closure->opv) {
             nsISupports *iface;
             if (opv->getISupports_(obj, &iface) && iface)
                 cStats->objectsPrivate += opv->sizeOfIncludingThis(iface);
         }
         break;
       }
 
-      case JSTRACE_STRING: {
-        JSString *str = static_cast<JSString *>(thing);
-
-        JS::StringInfo info;
-        if (str->hasLatin1Chars()) {
-            info.gcHeapLatin1 = thingSize;
-            info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
-        } else {
-            info.gcHeapTwoByte = thingSize;
-            info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
-        }
-        info.numCopies = 1;
-
-        zStats->stringInfo.add(info);
-
-        // The primary use case for anonymization is automated crash submission
-        // (to help detect OOM crashes). In that case, we don't want to pay the
-        // memory cost required to do notable string detection.
-        if (granularity == FineGrained && !closure->anonymize) {
-            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
-            if (!p) {
-                // Ignore failure -- we just won't record the string as notable.
-                (void)zStats->allStrings->add(p, str, info);
-            } else {
-                p->value().add(info);
-            }
-        }
-        break;
-      }
-
-      case JSTRACE_SYMBOL:
-        zStats->symbolsGCHeap += thingSize;
-        break;
-
-      case JSTRACE_SHAPE: {
-        Shape *shape = static_cast<Shape *>(thing);
-        CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
-        JS::ClassInfo info;        // This zeroes all the sizes.
-        if (shape->inDictionary())
-            info.shapesGCHeapDict += thingSize;
-        else
-            info.shapesGCHeapTree += thingSize;
-        shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
-        cStats->classInfo.add(info);
-
-        const BaseShape *base = shape->base();
-        const Class *clasp = base->clasp();
-        const char *className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
-        break;
-      }
-
-      case JSTRACE_BASE_SHAPE: {
-        BaseShape *base = static_cast<BaseShape *>(thing);
-        CompartmentStats *cStats = GetCompartmentStats(base->compartment());
-
-        JS::ClassInfo info;        // This zeroes all the sizes.
-        info.shapesGCHeapBase += thingSize;
-        // No malloc-heap measurements.
-
-        cStats->classInfo.add(info);
-
-        const Class *clasp = base->clasp();
-        const char *className = clasp->name;
-        AddClassInfo(granularity, cStats, className, info);
-        break;
-      }
-
       case JSTRACE_SCRIPT: {
         JSScript *script = static_cast<JSScript *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(script->compartment());
         cStats->scriptsGCHeap += thingSize;
         cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_);
         cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_);
         jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
                                    &cStats->baselineStubsFallback);
@@ -531,38 +463,106 @@ StatsCellCallback(JSRuntime *rt, void *d
                     p->value().add(info);
                 }
             }
         }
 
         break;
       }
 
+      case JSTRACE_STRING: {
+        JSString *str = static_cast<JSString *>(thing);
+
+        JS::StringInfo info;
+        if (str->hasLatin1Chars()) {
+            info.gcHeapLatin1 = thingSize;
+            info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        } else {
+            info.gcHeapTwoByte = thingSize;
+            info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        }
+        info.numCopies = 1;
+
+        zStats->stringInfo.add(info);
+
+        // The primary use case for anonymization is automated crash submission
+        // (to help detect OOM crashes). In that case, we don't want to pay the
+        // memory cost required to do notable string detection.
+        if (granularity == FineGrained && !closure->anonymize) {
+            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
+            if (!p) {
+                // Ignore failure -- we just won't record the string as notable.
+                (void)zStats->allStrings->add(p, str, info);
+            } else {
+                p->value().add(info);
+            }
+        }
+        break;
+      }
+
+      case JSTRACE_SYMBOL:
+        zStats->symbolsGCHeap += thingSize;
+        break;
+
+      case JSTRACE_BASE_SHAPE: {
+        BaseShape *base = static_cast<BaseShape *>(thing);
+        CompartmentStats *cStats = GetCompartmentStats(base->compartment());
+
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        info.shapesGCHeapBase += thingSize;
+        // No malloc-heap measurements.
+
+        cStats->classInfo.add(info);
+
+        const Class *clasp = base->clasp();
+        const char *className = clasp->name;
+        AddClassInfo(granularity, cStats, className, info);
+        break;
+      }
+
+      case JSTRACE_JITCODE: {
+        zStats->jitCodesGCHeap += thingSize;
+        // The code for a script is counted in ExecutableAllocator::sizeOfCode().
+        break;
+      }
+
       case JSTRACE_LAZY_SCRIPT: {
         LazyScript *lazy = static_cast<LazyScript *>(thing);
         zStats->lazyScriptsGCHeap += thingSize;
         zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
-      case JSTRACE_JITCODE: {
-        zStats->jitCodesGCHeap += thingSize;
-        // The code for a script is counted in ExecutableAllocator::sizeOfCode().
+      case JSTRACE_SHAPE: {
+        Shape *shape = static_cast<Shape *>(thing);
+        CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        if (shape->inDictionary())
+            info.shapesGCHeapDict += thingSize;
+        else
+            info.shapesGCHeapTree += thingSize;
+        shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
+        cStats->classInfo.add(info);
+
+        const BaseShape *base = shape->base();
+        const Class *clasp = base->clasp();
+        const char *className = clasp->name;
+        AddClassInfo(granularity, cStats, className, info);
         break;
       }
 
       case JSTRACE_TYPE_OBJECT: {
         types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
         zStats->typeObjectsGCHeap += thingSize;
         zStats->typeObjectsMallocHeap += obj->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
       default:
-        MOZ_CRASH("invalid traceKind");
+        MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     zStats->unusedGCThings -= thingSize;
 }
 
 bool
 ZoneStats::initStrings(JSRuntime *rt)
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -54,23 +54,23 @@ Concrete<void>::size(mozilla::MallocSize
 {
     MOZ_CRASH("null ubi::Node");
 }
 
 Node::Node(JSGCTraceKind kind, void *ptr)
 {
     switch (kind) {
       case JSTRACE_OBJECT:      construct(static_cast<JSObject *>(ptr));              break;
+      case JSTRACE_SCRIPT:      construct(static_cast<JSScript *>(ptr));              break;
       case JSTRACE_STRING:      construct(static_cast<JSString *>(ptr));              break;
       case JSTRACE_SYMBOL:      construct(static_cast<JS::Symbol *>(ptr));            break;
-      case JSTRACE_SCRIPT:      construct(static_cast<JSScript *>(ptr));              break;
-      case JSTRACE_LAZY_SCRIPT: construct(static_cast<js::LazyScript *>(ptr));        break;
+      case JSTRACE_BASE_SHAPE:  construct(static_cast<js::BaseShape *>(ptr));         break;
       case JSTRACE_JITCODE:     construct(static_cast<js::jit::JitCode *>(ptr));      break;
+      case JSTRACE_LAZY_SCRIPT: construct(static_cast<js::LazyScript *>(ptr));        break;
       case JSTRACE_SHAPE:       construct(static_cast<js::Shape *>(ptr));             break;
-      case JSTRACE_BASE_SHAPE:  construct(static_cast<js::BaseShape *>(ptr));         break;
       case JSTRACE_TYPE_OBJECT: construct(static_cast<js::types::TypeObject *>(ptr)); break;
 
       default:
         MOZ_CRASH("bad JSGCTraceKind passed to JS::ubi::Node::Node");
     }
 }
 
 Node::Node(HandleValue value)
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -195,27 +195,23 @@ NS_NewFileURI(nsIURI* *result,
  *  3) Call NS_NewChannelInternal *only* if requesting Principal and
  *     the Node's Principal have to be different.
  *     >> Most likely this is not the case! <<
  *     Needs special approval!
  */
 inline nsresult
 NS_NewChannelInternal(nsIChannel**           outChannel,
                       nsIURI*                aUri,
-                      nsINode*               aRequestingNode,
-                      nsIPrincipal*          aRequestingPrincipal,
-                      nsIPrincipal*          aTriggeringPrincipal,
-                      nsSecurityFlags        aSecurityFlags,
-                      nsContentPolicyType    aContentPolicyType,
-                      nsIURI*                aBaseURI = nullptr,
+                      nsILoadInfo*           aLoadInfo,
                       nsILoadGroup*          aLoadGroup = nullptr,
                       nsIInterfaceRequestor* aCallbacks = nullptr,
                       nsLoadFlags            aLoadFlags = nsIRequest::LOAD_NORMAL,
                       nsIIOService*          aIoService = nullptr)
 {
+  NS_ASSERTION(aLoadInfo, "Can not create channel without aLoadInfo!");
   NS_ENSURE_ARG_POINTER(outChannel);
 
   nsCOMPtr<nsIIOService> grip;
   nsresult rv = net_EnsureIOService(&aIoService, grip);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> channel;
   rv = aIoService->NewChannelFromURI(aUri, getter_AddRefs(channel));
@@ -233,72 +229,56 @@ NS_NewChannelInternal(nsIChannel**      
 
   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
     // Retain the LOAD_REPLACE load flag if set.
     nsLoadFlags normalLoadFlags = 0;
     channel->GetLoadFlags(&normalLoadFlags);
     rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
     NS_ENSURE_SUCCESS(rv, rv);
   }
-
-  // Some channels might already have a loadInfo attached at this
-  // point (see bug 1104623). We have to make sure to update
-  // security flags in such cases before we set the loadinfo.
-  // Once bug 1087442 lands, this problem disappears because we
-  // attach the loadinfo in each individual protocol handler.
-  nsCOMPtr<nsILoadInfo> loadInfo;
-  channel->GetLoadInfo(getter_AddRefs(loadInfo));
-  if (loadInfo) {
-    aSecurityFlags |= loadInfo->GetSecurityFlags();
-  }
-
-  // create a new Loadinfo with the potentially updated securityFlags
-  loadInfo =
-    new mozilla::LoadInfo(aRequestingPrincipal, aTriggeringPrincipal,
-                          aRequestingNode, aSecurityFlags,
-                          aContentPolicyType, aBaseURI);
-  if (!loadInfo) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  channel->SetLoadInfo(loadInfo);
+  channel->SetLoadInfo(aLoadInfo);
 
   // If we're sandboxed, make sure to clear any owner the channel
   // might already have.
-  if (loadInfo->GetLoadingSandboxed()) {
+  if (aLoadInfo->GetLoadingSandboxed()) {
     channel->SetOwner(nullptr);
   }
 
   channel.forget(outChannel);
   return NS_OK;
 }
 
 inline nsresult
 NS_NewChannelInternal(nsIChannel**           outChannel,
                       nsIURI*                aUri,
-                      nsILoadInfo*           aLoadInfo,
+                      nsINode*               aRequestingNode,
+                      nsIPrincipal*          aRequestingPrincipal,
+                      nsIPrincipal*          aTriggeringPrincipal,
+                      nsSecurityFlags        aSecurityFlags,
+                      nsContentPolicyType    aContentPolicyType,
                       nsILoadGroup*          aLoadGroup = nullptr,
                       nsIInterfaceRequestor* aCallbacks = nullptr,
                       nsLoadFlags            aLoadFlags = nsIRequest::LOAD_NORMAL,
                       nsIIOService*          aIoService = nullptr)
 {
-  MOZ_ASSERT(aLoadInfo, "Can not create a channel without a loadInfo");
-  nsresult rv = NS_NewChannelInternal(outChannel,
-                                      aUri,
-                                      aLoadInfo->LoadingNode(),
-                                      aLoadInfo->LoadingPrincipal(),
-                                      aLoadInfo->TriggeringPrincipal(),
-                                      aLoadInfo->GetSecurityFlags(),
-                                      aLoadInfo->GetContentPolicyType(),
-                                      aLoadInfo->BaseURI(),
-                                      aLoadGroup,
-                                      aCallbacks,
-                                      aLoadFlags,
-                                      aIoService);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
+  NS_ASSERTION(aRequestingPrincipal, "Can not create channel without a requesting Principal!");
+
+  nsCOMPtr<nsILoadInfo> loadInfo =
+    new mozilla::LoadInfo(aRequestingPrincipal, aTriggeringPrincipal,
+                          aRequestingNode, aSecurityFlags, aContentPolicyType);
+  if (!loadInfo) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  return NS_NewChannelInternal(outChannel,
+                               aUri,
+                               loadInfo,
+                               aLoadGroup,
+                               aCallbacks,
+                               aLoadFlags,
+                               aIoService);
 }
 
 inline nsresult /*NS_NewChannelWithNodeAndTriggeringPrincipal */
 NS_NewChannelWithTriggeringPrincipal(nsIChannel**           outChannel,
                                      nsIURI*                aUri,
                                      nsINode*               aRequestingNode,
                                      nsIPrincipal*          aTriggeringPrincipal,
                                      nsSecurityFlags        aSecurityFlags,
@@ -312,17 +292,16 @@ NS_NewChannelWithTriggeringPrincipal(nsI
   NS_ASSERTION(aTriggeringPrincipal, "Can not create channel without a triggering Principal!");
   return NS_NewChannelInternal(outChannel,
                                aUri,
                                aRequestingNode,
                                aRequestingNode->NodePrincipal(),
                                aTriggeringPrincipal,
                                aSecurityFlags,
                                aContentPolicyType,
-                               nullptr, // aBaseURI
                                aLoadGroup,
                                aCallbacks,
                                aLoadFlags,
                                aIoService);
 }
 
 inline nsresult /*NS_NewChannelWithPrincipalAndTriggeringPrincipal */
 NS_NewChannelWithTriggeringPrincipal(nsIChannel**           outChannel,
@@ -339,17 +318,16 @@ NS_NewChannelWithTriggeringPrincipal(nsI
   NS_ASSERTION(aRequestingPrincipal, "Can not create channel without a requesting Principal!");
   return NS_NewChannelInternal(outChannel,
                                aUri,
                                nullptr, // aRequestingNode
                                aRequestingPrincipal,
                                aTriggeringPrincipal,
                                aSecurityFlags,
                                aContentPolicyType,
-                               nullptr, // aBaseURI
                                aLoadGroup,
                                aCallbacks,
                                aLoadFlags,
                                aIoService);
 }
 
 inline nsresult /* NS_NewChannelNode */
 NS_NewChannel(nsIChannel**           outChannel,
@@ -365,17 +343,16 @@ NS_NewChannel(nsIChannel**           out
   NS_ASSERTION(aRequestingNode, "Can not create channel without a requesting Node!");
   return NS_NewChannelInternal(outChannel,
                                aUri,
                                aRequestingNode,
                                aRequestingNode->NodePrincipal(),
                                nullptr, // aTriggeringPrincipal
                                aSecurityFlags,
                                aContentPolicyType,
-                               nullptr, // aBaseURI
                                aLoadGroup,
                                aCallbacks,
                                aLoadFlags,
                                aIoService);
 }
 
 inline nsresult /* NS_NewChannelPrincipal */
 NS_NewChannel(nsIChannel**           outChannel,
@@ -390,17 +367,16 @@ NS_NewChannel(nsIChannel**           out
 {
   return NS_NewChannelInternal(outChannel,
                                aUri,
                                nullptr, // aRequestingNode,
                                aRequestingPrincipal,
                                nullptr, // aTriggeringPrincipal
                                aSecurityFlags,
                                aContentPolicyType,
-                               nullptr, // aBaseURI
                                aLoadGroup,
                                aCallbacks,
                                aLoadFlags,
                                aIoService);
 }
 
 // Use this function with CAUTION. It creates a stream that blocks when you
 // Read() from it and blocking the UI thread is a bad idea. If you don't want
@@ -425,17 +401,16 @@ NS_OpenURIInternal(nsIInputStream**     
   nsCOMPtr<nsIChannel> channel;
   nsresult rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                       aUri,
                                       aRequestingNode,
                                       aRequestingPrincipal,
                                       aTriggeringPrincipal,
                                       aSecurityFlags,
                                       aContentPolicyType,
-                                      nullptr, // aBaseURI
                                       aLoadGroup,
                                       aCallbacks,
                                       aLoadFlags,
                                       aIoService);
 
   NS_ENSURE_SUCCESS(rv, rv);
   nsIInputStream *stream;
   rv = channel->Open(&stream);
@@ -982,17 +957,16 @@ NS_NewStreamLoaderInternal(nsIStreamLoad
    nsCOMPtr<nsIChannel> channel;
    nsresult rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                        aUri,
                                        aRequestingNode,
                                        aRequestingPrincipal,
                                        nullptr, // aTriggeringPrincipal
                                        aSecurityFlags,
                                        aContentPolicyType,
-                                       nullptr, // aBaseURI
                                        aLoadGroup,
                                        aCallbacks,
                                        aLoadFlags);
 
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     httpChannel->SetReferrer(aReferrer);
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -16,49 +16,53 @@ extern "C" void sandbox_free_error(char 
 
 namespace mozilla {
 
 static const char pluginSandboxRules[] =
   "(version 1)\n"
   "(deny default)\n"
   "(allow signal (target self))\n"
   "(allow sysctl-read)\n"
+  // Illegal syntax on OS X 10.6, needed on 10.7 and up.
+  "%s(allow iokit-open (iokit-user-client-class \"IOHIDParamUserClient\"))\n"
   // Needed only on OS X 10.6
   "%s(allow file-read-data (literal \"%s\"))\n"
   "(allow mach-lookup\n"
   "    (global-name \"com.apple.cfprefsd.agent\")\n"
   "    (global-name \"com.apple.cfprefsd.daemon\")\n"
   "    (global-name \"com.apple.system.opendirectoryd.libinfo\")\n"
   "    (global-name \"com.apple.system.logger\")\n"
   "    (global-name \"com.apple.ls.boxd\"))\n"
   "(allow file-read*\n"
   "    (regex #\"^/etc$\")\n"
   "    (regex #\"^/dev/u?random$\")\n"
   "    (regex #\"^/(private/)?var($|/)\")\n"
   "    (literal \"/usr/share/icu/icudt51l.dat\")\n"
+  "    (regex #\"^/System/Library/Displays/Overrides/*\")\n"
+  "    (regex #\"^/System/Library/CoreServices/CoreTypes.bundle/*\")\n"
   "    (literal \"%s\")\n"
   "    (literal \"%s\")\n"
   "    (literal \"%s\"))\n";
 
 static const char contentSandboxRules[] =
   "(version 1)\n"
   "(allow default)\n";
 
 bool StartMacSandbox(MacSandboxInfo aInfo, nsCString &aErrorMessage)
 {
   nsAutoCString profile;
   if (aInfo.type == MacSandboxType_Plugin) {
     if (nsCocoaFeatures::OnLionOrLater()) {
-      profile.AppendPrintf(pluginSandboxRules, ";",
+      profile.AppendPrintf(pluginSandboxRules, "", ";",
                            aInfo.pluginInfo.pluginPath.get(),
                            aInfo.pluginInfo.pluginBinaryPath.get(),
                            aInfo.appPath.get(),
                            aInfo.appBinaryPath.get());
     } else {
-      profile.AppendPrintf(pluginSandboxRules, "",
+      profile.AppendPrintf(pluginSandboxRules, ";", "",
                            aInfo.pluginInfo.pluginPath.get(),
                            aInfo.pluginInfo.pluginBinaryPath.get(),
                            aInfo.appPath.get(),
                            aInfo.appBinaryPath.get());
     }
   }
   else if (aInfo.type == MacSandboxType_Content) {
     profile.AppendPrintf(contentSandboxRules);
--- a/testing/mozbase/manifestparser/manifestparser/manifestparser.py
+++ b/testing/mozbase/manifestparser/manifestparser/manifestparser.py
@@ -872,118 +872,78 @@ class ManifestParser(object):
                     print >> sys.stderr, message + " Skipping."
                     continue
                 destination = os.path.join(rootdir, _relpath)
                 shutil.copy(source, destination)
 
     ### directory importers
 
     @classmethod
-    def _walk_directories(cls, directories, function, pattern=None, ignore=()):
+    def _walk_directories(cls, directories, callback, pattern=None, ignore=()):
         """
         internal function to import directories
         """
 
-        class FilteredDirectoryContents(object):
-            """class to filter directory contents"""
-
-            sort = sorted
-
-            def __init__(self, pattern=pattern, ignore=ignore, cache=None):
-                if pattern is None:
-                    pattern = set()
-                if isinstance(pattern, basestring):
-                    pattern = [pattern]
-                self.patterns = pattern
-                self.ignore = set(ignore)
-
-                # cache of (dirnames, filenames) keyed on directory real path
-                # assumes volume is frozen throughout scope
-                self._cache = cache or {}
-
-            def __call__(self, directory):
-                """returns 2-tuple: dirnames, filenames"""
-                directory = os.path.realpath(directory)
-                if directory not in self._cache:
-                    dirnames, filenames = self.contents(directory)
+        if isinstance(pattern, basestring):
+            patterns = [pattern]
+        else:
+            patterns = pattern
+        ignore = set(ignore)
 
-                    # filter out directories without progeny
-                    # XXX recursive: should keep track of seen directories
-                    dirnames = [ dirname for dirname in dirnames
-                                 if not self.empty(os.path.join(directory, dirname)) ]
-
-                    self._cache[directory] = (tuple(dirnames), filenames)
-
-                # return cached values
-                return self._cache[directory]
+        if not patterns:
+            accept_filename = lambda filename: True
+        else:
+            def accept_filename(filename):
+                for pattern in patterns:
+                    if fnmatch.fnmatch(filename, pattern):
+                        return True
 
-            def empty(self, directory):
-                """
-                returns if a directory and its descendents are empty
-                """
-                return self(directory) == ((), ())
-
-            def contents(self, directory, sort=None):
-                """
-                return directory contents as (dirnames, filenames)
-                with `ignore` and `pattern` applied
-                """
-
-                if sort is None:
-                    sort = self.sort
+        if not ignore:
+            accept_dirname = lambda dirname: True
+        else:
+            accept_dirname = lambda dirname: dirname not in ignore
 
-                # split directories and files
-                dirnames = []
-                filenames = []
-                for item in os.listdir(directory):
-                    path = os.path.join(directory, item)
-                    if os.path.isdir(path):
-                        dirnames.append(item)
-                    else:
-                        # XXX not sure what to do if neither a file or directory
-                        # (if anything)
-                        assert os.path.isfile(path)
-                        filenames.append(item)
+        rootdirectories = directories[:]
+        seen_directories = set()
+        for rootdirectory in rootdirectories:
+            # let's recurse directories using list
+            directories = [os.path.realpath(rootdirectory)]
+            while directories:
+                directory = directories.pop(0)
+                if directory in seen_directories:
+                    # eliminate possible infinite recursion due to
+                    # symbolic links
+                    continue
+                seen_directories.add(directory)
 
-                # filter contents;
-                # this could be done in situ re the above for loop
-                # but it is really disparate in intent
-                # and could conceivably go to a separate method
-                dirnames = [dirname for dirname in dirnames
-                            if dirname not in self.ignore]
-                filenames = set(filenames)
-                # we use set functionality to filter filenames
-                if self.patterns:
-                    matches = set()
-                    matches.update(*[fnmatch.filter(filenames, pattern)
-                                     for pattern in self.patterns])
-                    filenames = matches
+                files = []
+                subdirs = []
+                for name in sorted(os.listdir(directory)):
+                    path = os.path.join(directory, name)
+                    if os.path.isfile(path):
+                        # os.path.isfile follow symbolic links, we don't
+                        # need to handle them here.
+                        if accept_filename(name):
+                            files.append(name)
+                        continue
+                    elif os.path.islink(path):
+                        # eliminate symbolic links
+                        path = os.path.realpath(path)
 
-                if sort is not None:
-                    # sort dirnames, filenames
-                    dirnames = sort(dirnames)
-                    filenames = sort(filenames)
-
-                return (tuple(dirnames), tuple(filenames))
-
-        # make a filtered directory object
-        directory_contents = FilteredDirectoryContents(pattern=pattern, ignore=ignore)
+                    # we must have a directory here
+                    if accept_dirname(name):
+                        subdirs.append(name)
+                        # this subdir is added for recursion
+                        directories.insert(0, path)
 
-        # walk the directories, generating manifests
-        for index, directory in enumerate(directories):
-
-            for dirpath, dirnames, filenames in os.walk(directory):
+                # here we got all subdirs and files filtered, we can
+                # call the callback function if directory is not empty
+                if subdirs or files:
+                    callback(rootdirectory, directory, subdirs, files)
 
-                # get the directory contents from the caching object
-                _dirnames, filenames = directory_contents(dirpath)
-                # filter out directory names
-                dirnames[:] = _dirnames
-
-                # call callback function
-                function(directory, dirpath, dirnames, filenames)
 
     @classmethod
     def populate_directory_manifests(cls, directories, filename, pattern=None, ignore=(), overwrite=False):
         """
         walks directories and writes manifests of name `filename` in-place; returns `cls` instance populated
         with the given manifests
 
         filename -- filename of manifests to write
--- a/testing/mozbase/manifestparser/tests/test_convert_directory.py
+++ b/testing/mozbase/manifestparser/tests/test_convert_directory.py
@@ -9,25 +9,37 @@ import shutil
 import tempfile
 import unittest
 
 from manifestparser import convert
 from manifestparser import ManifestParser
 
 here = os.path.dirname(os.path.abspath(__file__))
 
+# In some cases tempfile.mkdtemp() may returns a path which contains
+# symlinks. Some tests here will then break, as the manifestparser.convert
+# function returns paths that does not contains symlinks.
+#
+# Workaround is to use the following function, if absolute path of temp dir
+# must be compared.
+def create_realpath_tempdir():
+    """
+    Create a tempdir without symlinks.
+    """
+    return os.path.realpath(tempfile.mkdtemp())
+
 class TestDirectoryConversion(unittest.TestCase):
     """test conversion of a directory tree to a manifest structure"""
 
     def create_stub(self, directory=None):
         """stub out a directory with files in it"""
 
         files = ('foo', 'bar', 'fleem')
         if directory is None:
-            directory = tempfile.mkdtemp()
+            directory = create_realpath_tempdir()
         for i in files:
             file(os.path.join(directory, i), 'w').write(i)
         subdir = os.path.join(directory, 'subdir')
         os.mkdir(subdir)
         file(os.path.join(subdir, 'subfile'), 'w').write('baz')
         return directory
 
     def test_directory_to_manifest(self):
@@ -122,22 +134,22 @@ subsuite =
 
     def test_update(self):
         """
         Test our ability to update tests from a manifest and a directory of
         files
         """
 
         # boilerplate
-        tempdir = tempfile.mkdtemp()
+        tempdir = create_realpath_tempdir()
         for i in range(10):
             file(os.path.join(tempdir, str(i)), 'w').write(str(i))
 
         # otherwise empty directory with a manifest file
-        newtempdir = tempfile.mkdtemp()
+        newtempdir = create_realpath_tempdir()
         manifest_file = os.path.join(newtempdir, 'manifest.ini')
         manifest_contents = str(convert([tempdir], relative_to=tempdir))
         with file(manifest_file, 'w') as f:
             f.write(manifest_contents)
 
         # get the manifest
         manifest = ManifestParser(manifests=(manifest_file,))
 
--- a/testing/mozbase/manifestparser/tests/test_convert_symlinks.py
+++ b/testing/mozbase/manifestparser/tests/test_convert_symlinks.py
@@ -4,27 +4,23 @@
 # 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/.
 
 import os
 import shutil
 import tempfile
 import unittest
 
-from manifestparser import convert
+from manifestparser import convert, ManifestParser
 
 class TestSymlinkConversion(unittest.TestCase):
     """
     test conversion of a directory tree with symlinks to a manifest structure
     """
 
-    # Currently broken: see
-    # https://bugzilla.mozilla.org/show_bug.cgi?id=902610
-    # https://bugzilla.mozilla.org/show_bug.cgi?id=920938
-
     def create_stub(self, directory=None):
         """stub out a directory with files in it"""
 
         files = ('foo', 'bar', 'fleem')
         if directory is None:
             directory = tempfile.mkdtemp()
         for i in files:
             file(os.path.join(directory, i), 'w').write(i)
@@ -47,33 +43,31 @@ class TestSymlinkConversion(unittest.Tes
             self.assertEqual([i['name'] for i in parser.tests],
                              files)
         except:
             raise
         finally:
             shutil.rmtree(stub)
             os.chdir(oldcwd)
 
+    @unittest.skipIf(not hasattr(os, 'symlink'),
+                     "symlinks unavailable on this platform")
     def test_relpath_symlink(self):
         """
         Ensure `relative_to` works in a symlink.
         Not available on windows.
         """
 
-        symlink = getattr(os, 'symlink', None)
-        if symlink is None:
-            return # symlinks unavailable on this platform
-
         oldcwd = os.getcwd()
         workspace = tempfile.mkdtemp()
         try:
             tmpdir = os.path.join(workspace, 'directory')
             os.makedirs(tmpdir)
             linkdir = os.path.join(workspace, 'link')
-            symlink(tmpdir, linkdir)
+            os.symlink(tmpdir, linkdir)
             self.create_stub(tmpdir)
 
             # subdir with in-memory manifest
             files = ['../bar', '../fleem', '../foo', 'subfile']
             subdir = os.path.join(linkdir, 'subdir')
             os.chdir(os.path.realpath(subdir))
             for directory in (tmpdir, linkdir):
                 parser = convert([directory], relative_to='.')
@@ -85,31 +79,59 @@ class TestSymlinkConversion(unittest.Tes
 
         # a more complicated example
         oldcwd = os.getcwd()
         workspace = tempfile.mkdtemp()
         try:
             tmpdir = os.path.join(workspace, 'directory')
             os.makedirs(tmpdir)
             linkdir = os.path.join(workspace, 'link')
-            symlink(tmpdir, linkdir)
+            os.symlink(tmpdir, linkdir)
             self.create_stub(tmpdir)
             files = ['../bar', '../fleem', '../foo', 'subfile']
             subdir = os.path.join(linkdir, 'subdir')
             subsubdir = os.path.join(subdir, 'sub')
             os.makedirs(subsubdir)
             linksubdir = os.path.join(linkdir, 'linky')
             linksubsubdir = os.path.join(subsubdir, 'linky')
-            symlink(subdir, linksubdir)
-            symlink(subdir, linksubsubdir)
+            os.symlink(subdir, linksubdir)
+            os.symlink(subdir, linksubsubdir)
             for dest in (subdir,):
                 os.chdir(dest)
                 for directory in (tmpdir, linkdir):
                     parser = convert([directory], relative_to='.')
                     self.assertEqual([i['name'] for i in parser.tests],
                                      files)
         finally:
             shutil.rmtree(workspace)
             os.chdir(oldcwd)
 
+    @unittest.skipIf(not hasattr(os, 'symlink'),
+                     "symlinks unavailable on this platform")
+    def test_recursion_symlinks(self):
+        workspace = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, workspace)
+
+        # create two dirs
+        os.makedirs(os.path.join(workspace, 'dir1'))
+        os.makedirs(os.path.join(workspace, 'dir2'))
+
+        # create cyclical symlinks
+        os.symlink(os.path.join('..', 'dir1'),
+                   os.path.join(workspace, 'dir2', 'ldir1'))
+        os.symlink(os.path.join('..', 'dir2'),
+                   os.path.join(workspace, 'dir1', 'ldir2'))
+
+        # create one file in each dir
+        open(os.path.join(workspace, 'dir1', 'f1.txt'), 'a').close()
+        open(os.path.join(workspace, 'dir1', 'ldir2', 'f2.txt'), 'a').close()
+
+        data = []
+        def callback(rootdirectory, directory, subdirs, files):
+            for f in files:
+                data.append(f)
+
+        ManifestParser._walk_directories([workspace], callback)
+        self.assertEqual(sorted(data), ['f1.txt', 'f2.txt'])
+
 
 if __name__ == '__main__':
     unittest.main()
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -5,17 +5,18 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <bindings id="firefoxBrowserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser">
 
-    <implementation type="application/javascript" implements="nsIObserver, nsIDOMEventListener, nsIMessageListener">
+    <implementation type="application/javascript"
+                    implements="nsIObserver, nsIDOMEventListener, nsIMessageListener, nsIRemoteBrowser">
 
       <field name="_securityUI">null</field>
 
       <property name="securityUI"
                 readonly="true">
         <getter><![CDATA[
           if (!this._securityUI) {
             let jsm = "resource://gre/modules/RemoteSecurityUI.jsm";
@@ -367,13 +368,28 @@
         <body>
         <![CDATA[
           return { x: aScreenX + this.boxObject.screenX,
                    y: aScreenY + this.boxObject.screenY };
         ]]>
         </body>
       </method>
 
+      <method name="enableDisableCommands">
+        <parameter name="aAction"/>
+        <parameter name="aEnabledLength"/>
+        <parameter name="aEnabledCommands"/>
+        <parameter name="aDisabledLength"/>
+        <parameter name="aDisabledCommands"/>
+        <body>
+          if (this._controller) {
+            this._controller.enableDisableCommands(aAction,
+                                                   aEnabledLength, aEnabledCommands,
+                                                   aDisabledLength, aDisabledCommands);
+          }
+        </body>
+      </method>
+
     </implementation>
 
   </binding>
 
 </bindings>
--- a/toolkit/modules/RemoteController.jsm
+++ b/toolkit/modules/RemoteController.jsm
@@ -9,50 +9,50 @@ const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function RemoteController(browser)
 {
   this._browser = browser;
+
+  // A map of commands that have had their enabled/disabled state assigned. The
+  // value of each key will be true if enabled, and false if disabled.
+  this._supportedCommands = { };
 }
 
 RemoteController.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIController]),
 
   isCommandEnabled: function(aCommand) {
-    // We can't synchronously ask content if a command is enabled,
-    // so we always pretend is.
-    // The right way forward would be to never use nsIController
-    // to ask if something in content is enabled. Maybe even
-    // by replacing the nsIController architecture by something else.
-    // See bug 905768.
-    return true;
+    return this._supportedCommands[aCommand] || false;
   },
 
   supportsCommand: function(aCommand) {
-    // Optimize the lookup a bit.
-    if (!aCommand.startsWith("cmd_"))
-      return false;
-
-    // For now only support the commands used in "browser-context.inc"
-    let commands = [
-      "cmd_copyLink",
-      "cmd_copyImage",
-      "cmd_undo",
-      "cmd_cut",
-      "cmd_copy",
-      "cmd_paste",
-      "cmd_delete",
-      "cmd_selectAll",
-      "cmd_switchTextDirection"
-    ];
-
-    return commands.indexOf(aCommand) >= 0;
+    return aCommand in this._supportedCommands;
   },
 
   doCommand: function(aCommand) {
     this._browser.messageManager.sendAsyncMessage("ControllerCommands:Do", aCommand);
   },
 
-  onEvent: function () {}
+  onEvent: function () {},
+
+  // This is intended to be called from the remote-browser binding to update
+  // the enabled and disabled commands.
+  enableDisableCommands: function(aAction,
+                                  aEnabledLength, aEnabledCommands,
+                                  aDisabledLength, aDisabledCommands) {
+    // Clear the list first
+    this._supportedCommands = { };
+
+    for (let c = 0; c < aEnabledLength; c++) {
+      this._supportedCommands[aEnabledCommands[c]] = true;
+    }
+
+    for (let c = 0; c < aDisabledLength; c++) {
+      this._supportedCommands[aDisabledCommands[c]] = false;
+    }
+
+    this._browser.ownerDocument.defaultView.updateCommands(aAction);
+  }
 };
--- a/widget/gtk/gtk3drawing.c
+++ b/widget/gtk/gtk3drawing.c
@@ -59,23 +59,28 @@ static GtkWidget* gExpanderWidget;
 static GtkWidget* gToolbarSeparatorWidget;
 static GtkWidget* gMenuSeparatorWidget;
 static GtkWidget* gHPanedWidget;
 static GtkWidget* gVPanedWidget;
 static GtkWidget* gScrolledWindowWidget;
 
 static style_prop_t style_prop_func;
 static gboolean have_arrow_scaling;
+static gboolean checkbox_check_state;
 static gboolean is_initialized;
 
 #define ARROW_UP      0
 #define ARROW_DOWN    G_PI
 #define ARROW_RIGHT   G_PI_2
 #define ARROW_LEFT    (G_PI+G_PI_2)
 
+#if !GTK_CHECK_VERSION(3,14,0)
+#define GTK_STATE_FLAG_CHECKED (1 << 11)
+#endif
+
 static GtkStateFlags
 GetStateFlagsFromGtkWidgetState(GtkWidgetState* state)
 {
     GtkStateFlags stateFlags = GTK_STATE_FLAG_NORMAL;
 
     if (state->disabled)
         stateFlags = GTK_STATE_FLAG_INSENSITIVE;
     else {    
@@ -709,16 +714,21 @@ moz_gtk_init()
     GtkWidgetClass *entry_class;
 
     if (is_initialized)
         return MOZ_GTK_SUCCESS;
 
     is_initialized = TRUE;
     have_arrow_scaling = (gtk_major_version > 2 ||
                           (gtk_major_version == 2 && gtk_minor_version >= 12));
+    if (gtk_major_version > 3 ||
+       (gtk_major_version == 3 && gtk_minor_version >= 14))
+        checkbox_check_state = GTK_STATE_FLAG_CHECKED;
+    else
+        checkbox_check_state = GTK_STATE_FLAG_ACTIVE;
 
     /* Add style property to GtkEntry.
      * Adding the style property to the normal GtkEntry class means that it
      * will work without issues inside GtkComboBox and for Spinbuttons. */
     entry_class = g_type_class_ref(GTK_TYPE_ENTRY);
 
     return MOZ_GTK_SUCCESS;
 }
@@ -999,20 +1009,20 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRec
     focus_width = width + 2 * indicator_spacing;
     focus_height = height + 2 * indicator_spacing;
   
     style = gtk_widget_get_style_context(w);
 
     gtk_widget_set_sensitive(w, !state->disabled);
     gtk_widget_set_direction(w, direction);
     gtk_style_context_save(style);
-      
+
     if (isradio) {
         gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO);
-        gtk_style_context_set_state(style, selected ? GTK_STATE_FLAG_ACTIVE :
+        gtk_style_context_set_state(style, selected ? checkbox_check_state :
                                                       GTK_STATE_FLAG_NORMAL);
         gtk_render_option(style, cr, x, y, width, height);
         if (state->focused) {
             gtk_render_focus(style, cr, focus_x, focus_y,
                             focus_width, focus_height);
         }
     }
     else {
@@ -1020,17 +1030,17 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRec
         * 'indeterminate' type on checkboxes. In GTK, the shadow type
         * must also be changed for the state to be drawn.
         */        
         gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK);
         if (inconsistent) {
             gtk_style_context_set_state(style, GTK_STATE_FLAG_INCONSISTENT);
             gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE);
         } else if (selected) {
-            gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE);
+            gtk_style_context_set_state(style, checkbox_check_state);
         } else {
             gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE);
         }
         gtk_render_check(style, cr, x, y, width, height);        
         if (state->focused) {
             gtk_render_focus(style, cr, 
                              focus_x, focus_y, focus_width, focus_height);
         }
@@ -2583,18 +2593,19 @@ moz_gtk_check_menu_item_paint(cairo_t *c
     style = gtk_widget_get_style_context(gCheckMenuItemWidget);
     gtk_style_context_save(style);
     if (isradio) {
       gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO);
     } else {
       gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK);
     }
 
-    if (checked)
-      state_flags |= GTK_STATE_FLAG_ACTIVE;
+    if (checked) {
+      state_flags |= checkbox_check_state;
+    }
     
     gtk_style_context_set_state(style, state_flags);
     gtk_style_context_get_padding(style, state_flags, &padding);
 
     offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) +
                                             padding.left + 2;
 
     if (direction == GTK_TEXT_DIR_RTL) {
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -33,32 +33,30 @@ using mozilla::LookAndFeel;
                        (int)((c).blue*255), (int)((c).alpha*255)))
 
 nsLookAndFeel::nsLookAndFeel()
     : nsXPLookAndFeel(),
 #if (MOZ_WIDGET_GTK == 2)
       mStyle(nullptr),
 #else
       mBackgroundStyle(nullptr),
-      mViewStyle(nullptr),
       mButtonStyle(nullptr),
 #endif
       mDefaultFontCached(false), mButtonFontCached(false),
       mFieldFontCached(false), mMenuFontCached(false)
 {
     Init();    
 }
 
 nsLookAndFeel::~nsLookAndFeel()
 {
 #if (MOZ_WIDGET_GTK == 2)
     g_object_unref(mStyle);
 #else
     g_object_unref(mBackgroundStyle);
-    g_object_unref(mViewStyle);
     g_object_unref(mButtonStyle);
 #endif
 }
 
 nsresult
 nsLookAndFeel::NativeGetColor(ColorID aID, nscolor& aColor)
 {
 #if (MOZ_WIDGET_GTK == 3)
@@ -109,23 +107,29 @@ nsLookAndFeel::NativeGetColor(ColorID aI
     case eColorID__moz_dialogtext:
         aColor = sMozWindowText;
         break;
     case eColorID_WidgetSelectBackground:
     case eColorID_TextSelectBackground:
     case eColorID_IMESelectedRawTextBackground:
     case eColorID_IMESelectedConvertedTextBackground:
     case eColorID__moz_dragtargetzone:
-        aColor = sMozWindowSelectedBackground;
+    case eColorID__moz_cellhighlight:
+    case eColorID__moz_html_cellhighlight:
+    case eColorID_highlight: // preference selected item,
+        aColor = sTextSelectedBackground;
         break;
     case eColorID_WidgetSelectForeground:
     case eColorID_TextSelectForeground:
     case eColorID_IMESelectedRawTextForeground:
     case eColorID_IMESelectedConvertedTextForeground:
-        aColor = sMozWindowSelectedText;
+    case eColorID_highlighttext:
+    case eColorID__moz_cellhighlighttext:
+    case eColorID__moz_html_cellhighlighttext:
+        aColor = sTextSelectedText;
         break;
 #endif
     case eColorID_Widget3DHighlight:
         aColor = NS_RGB(0xa0,0xa0,0xa0);
         break;
     case eColorID_Widget3DShadow:
         aColor = NS_RGB(0x40,0x40,0x40);
         break;
@@ -233,29 +237,16 @@ nsLookAndFeel::NativeGetColor(ColorID aI
         aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
         break;
     case eColorID_graytext: // disabled text in windows, menus, etc.
     case eColorID_inactivecaptiontext: // text in inactive window caption
         gtk_style_context_get_color(mBackgroundStyle, 
                                     GTK_STATE_FLAG_INSENSITIVE, &gdk_color);
         aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
         break;
-    case eColorID_highlight: // preference selected item,
-        // background of selected item
-        gtk_style_context_get_background_color(mViewStyle, 
-                                               GTK_STATE_FLAG_SELECTED, 
-                                               &gdk_color);
-        aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
-        break;
-    case eColorID_highlighttext:
-        // text of selected item
-        gtk_style_context_get_color(mViewStyle, 
-                                    GTK_STATE_FLAG_SELECTED, &gdk_color);
-        aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
-        break;
     case eColorID_inactivecaption:
         // inactive window caption
         gtk_style_context_get_background_color(mBackgroundStyle, 
                                                GTK_STATE_FLAG_INSENSITIVE, 
                                                &gdk_color);
         aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
         break;
 #endif
@@ -388,29 +379,16 @@ nsLookAndFeel::NativeGetColor(ColorID aI
         gtk_style_context_get_background_color(mButtonStyle, 
                                                GTK_STATE_FLAG_PRELIGHT, 
                                                &gdk_color);
         aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
         break;
     case eColorID__moz_buttonhovertext:
         aColor = sButtonHoverText;
         break;
-    case eColorID__moz_cellhighlight:
-    case eColorID__moz_html_cellhighlight:
-        gtk_style_context_get_background_color(mViewStyle, 
-                                               GTK_STATE_FLAG_SELECTED, 
-                                               &gdk_color);
-        aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
-        break;
-    case eColorID__moz_cellhighlighttext:
-    case eColorID__moz_html_cellhighlighttext:
-        gtk_style_context_get_color(mViewStyle, 
-                                    GTK_STATE_FLAG_SELECTED, &gdk_color);
-        aColor = GDK_RGBA_TO_NS_RGBA(gdk_color);
-        break;
 #endif
     case eColorID__moz_menuhover:
         aColor = sMenuHover;
         break;
     case eColorID__moz_menuhovertext:
         aColor = sMenuHoverText;
         break;
     case eColorID__moz_oddtreerow:
@@ -962,60 +940,64 @@ nsLookAndFeel::Init()
     }
 
     g_object_unref(menu);
 #else
     GdkRGBA color;
     GtkStyleContext *style;
 
     // Gtk manages a screen's CSS in the settings object so we
-    // ask Gtk to create it explicitly. Otherwise we may end up 
+    // ask Gtk to create it explicitly. Otherwise we may end up
     // with wrong color theme, see Bug 972382
     (void)gtk_settings_get_for_screen(gdk_screen_get_default());
 
     GtkWidgetPath *path = gtk_widget_path_new();
     gtk_widget_path_append_type(path, GTK_TYPE_WINDOW);
 
     mBackgroundStyle = create_context(path);
     gtk_style_context_add_class(mBackgroundStyle, GTK_STYLE_CLASS_BACKGROUND);
 
-    mViewStyle = create_context(path);
-    gtk_style_context_add_class(mViewStyle, GTK_STYLE_CLASS_VIEW);
-
     mButtonStyle = create_context(path);
     gtk_style_context_add_class(mButtonStyle, GTK_STYLE_CLASS_BUTTON); 
 
     // Scrollbar colors
     style = create_context(path);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozScrollbar = GDK_RGBA_TO_NS_RGBA(color);
     g_object_unref(style);
 
     // Text colors
-    gtk_style_context_get_background_color(mViewStyle, GTK_STATE_FLAG_NORMAL, &color);
+    style = create_context(path);
+    gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW);
+    gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozFieldBackground = GDK_RGBA_TO_NS_RGBA(color);
-    gtk_style_context_get_color(mViewStyle, GTK_STATE_FLAG_NORMAL, &color);
+    gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozFieldText = GDK_RGBA_TO_NS_RGBA(color);
 
+    // Selected text and background
+    gtk_style_context_get_background_color(style,
+        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
+        &color);
+    sTextSelectedBackground = GDK_RGBA_TO_NS_RGBA(color);
+    gtk_style_context_get_color(style,
+        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
+        &color);
+    sTextSelectedText = GDK_RGBA_TO_NS_RGBA(color);
+    g_object_unref(style);
+
     // Window colors
     style = create_context(path);
     gtk_style_context_save(style);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozWindowBackground = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozWindowText = GDK_RGBA_TO_NS_RGBA(color);
-
-    // Selected text and background
-    gtk_style_context_get_background_color(style, GTK_STATE_FLAG_SELECTED, &color);
-    sMozWindowSelectedBackground = GDK_RGBA_TO_NS_RGBA(color);
-    gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &color);
-    sMozWindowSelectedText = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_restore(style);
 
     // tooltip foreground and background
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sInfoBackground = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sInfoText = GDK_RGBA_TO_NS_RGBA(color);
@@ -1251,21 +1233,19 @@ nsLookAndFeel::RefreshImpl()
     mFieldFontCached = false;
     mMenuFontCached = false;
 
 #if (MOZ_WIDGET_GTK == 2)
     g_object_unref(mStyle);
     mStyle = nullptr;
 #else
     g_object_unref(mBackgroundStyle);
-    g_object_unref(mViewStyle);
     g_object_unref(mButtonStyle);
 
     mBackgroundStyle = nullptr;
-    mViewStyle = nullptr;
     mButtonStyle = nullptr;
 #endif
 
     Init();
 }
 
 bool
 nsLookAndFeel::GetEchoPasswordImpl() {
--- a/widget/gtk/nsLookAndFeel.h
+++ b/widget/gtk/nsLookAndFeel.h
@@ -30,17 +30,16 @@ public:
     virtual char16_t GetPasswordCharacterImpl();
     virtual bool GetEchoPasswordImpl();
 
 protected:
 #if (MOZ_WIDGET_GTK == 2)
     struct _GtkStyle *mStyle;
 #else
     struct _GtkStyleContext *mBackgroundStyle;
-    struct _GtkStyleContext *mViewStyle;
     struct _GtkStyleContext *mButtonStyle;
 #endif
 
     // Cached fonts
     bool mDefaultFontCached;
     bool mButtonFontCached;
     bool mFieldFontCached;
     bool mMenuFontCached;
@@ -70,18 +69,18 @@ protected:
     nscolor sOddCellBackground;
     nscolor sNativeHyperLinkText;
     nscolor sComboBoxText;
     nscolor sComboBoxBackground;
     nscolor sMozFieldText;
     nscolor sMozFieldBackground;
     nscolor sMozWindowText;
     nscolor sMozWindowBackground;
-    nscolor sMozWindowSelectedText;
-    nscolor sMozWindowSelectedBackground;
+    nscolor sTextSelectedText;
+    nscolor sTextSelectedBackground;
     nscolor sMozScrollbar;
     char16_t sInvisibleCharacter;
     float   sCaretRatio;
     bool    sMenuSupportsDrag;
 
     void Init();
 };
 
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -175,67 +175,65 @@ struct NoteWeakMapsTracer : public js::W
   {
   }
   nsCycleCollectionNoteRootCallback& mCb;
   NoteWeakMapChildrenTracer mChildTracer;
 };
 
 static void
 TraceWeakMapping(js::WeakMapTracer* aTrc, JSObject* aMap,
-                 void* aKey, JSGCTraceKind aKeyKind,
-                 void* aValue, JSGCTraceKind aValueKind)
+                 JS::GCCellPtr aKey, JS::GCCellPtr aValue)
 {
   MOZ_ASSERT(aTrc->callback == TraceWeakMapping);
   NoteWeakMapsTracer* tracer = static_cast<NoteWeakMapsTracer*>(aTrc);
 
   // If nothing that could be held alive by this entry is marked gray, return.
-  if ((!aKey || !xpc_IsGrayGCThing(aKey)) &&
+  if ((!aKey || !xpc_IsGrayGCThing(aKey.asCell())) &&
       MOZ_LIKELY(!tracer->mCb.WantAllTraces())) {
-    if (!aValue || !xpc_IsGrayGCThing(aValue) ||
-        aValueKind == JSTRACE_STRING) {
+    if (!aValue || !xpc_IsGrayGCThing(aValue.asCell()) || aValue.isString()) {
       return;
     }
   }
 
   // The cycle collector can only properly reason about weak maps if it can
   // reason about the liveness of their keys, which in turn requires that
   // the key can be represented in the cycle collector graph.  All existing
   // uses of weak maps use either objects or scripts as keys, which are okay.
-  MOZ_ASSERT(AddToCCKind(aKeyKind));
+  MOZ_ASSERT(AddToCCKind(aKey.kind()));
 
   // As an emergency fallback for non-debug builds, if the key is not
   // representable in the cycle collector graph, we treat it as marked.  This
   // can cause leaks, but is preferable to ignoring the binding, which could
   // cause the cycle collector to free live objects.
-  if (!AddToCCKind(aKeyKind)) {
-    aKey = nullptr;
+  if (!AddToCCKind(aKey.kind())) {
+    aKey = JS::GCCellPtr::NullPtr();
   }
 
   JSObject* kdelegate = nullptr;
-  if (aKey && aKeyKind == JSTRACE_OBJECT) {
-    kdelegate = js::GetWeakmapKeyDelegate((JSObject*)aKey);
+  if (aKey.isObject()) {
+    kdelegate = js::GetWeakmapKeyDelegate(aKey.toObject());
   }
 
-  if (AddToCCKind(aValueKind)) {
-    tracer->mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
+  if (AddToCCKind(aValue.kind())) {
+    tracer->mCb.NoteWeakMapping(aMap, aKey.asCell(), kdelegate, aValue.asCell());
   } else {
     tracer->mChildTracer.mTracedAny = false;
     tracer->mChildTracer.mMap = aMap;
-    tracer->mChildTracer.mKey = aKey;
+    tracer->mChildTracer.mKey = aKey.asCell();
     tracer->mChildTracer.mKeyDelegate = kdelegate;
 
-    if (aValue && aValueKind != JSTRACE_STRING) {
-      JS_TraceChildren(&tracer->mChildTracer, aValue, aValueKind);
+    if (aValue.isString()) {
+      JS_TraceChildren(&tracer->mChildTracer, aValue.asCell(), aValue.kind());
     }
 
     // The delegate could hold alive the key, so report something to the CC
     // if we haven't already.
     if (!tracer->mChildTracer.mTracedAny &&
-        aKey && xpc_IsGrayGCThing(aKey) && kdelegate) {
-      tracer->mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
+        aKey && xpc_IsGrayGCThing(aKey.asCell()) && kdelegate) {
+      tracer->mCb.NoteWeakMapping(aMap, aKey.asCell(), kdelegate, nullptr);
     }
   }
 }
 
 // This is based on the logic in TraceWeakMapping.
 struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
 {
   explicit FixWeakMappingGrayBitsTracer(JSRuntime* aRt)
@@ -251,48 +249,47 @@ struct FixWeakMappingGrayBitsTracer : pu
       js::TraceWeakMaps(this);
     } while (mAnyMarked);
   }
 
 private:
 
   static void
   FixWeakMappingGrayBits(js::WeakMapTracer* aTrc, JSObject* aMap,
-                         void* aKey, JSGCTraceKind aKeyKind,
-                         void* aValue, JSGCTraceKind aValueKind)
+                         JS::GCCellPtr aKey, JS::GCCellPtr aValue)
   {
     FixWeakMappingGrayBitsTracer* tracer =
       static_cast<FixWeakMappingGrayBitsTracer*>(aTrc);
 
     // If nothing that could be held alive by this entry is marked gray, return.
-    bool delegateMightNeedMarking = aKey && xpc_IsGrayGCThing(aKey);
-    bool valueMightNeedMarking = aValue && xpc_IsGrayGCThing(aValue) &&
-                                 aValueKind != JSTRACE_STRING;
+    bool delegateMightNeedMarking = aKey && xpc_IsGrayGCThing(aKey.asCell());
+    bool valueMightNeedMarking = aValue && xpc_IsGrayGCThing(aValue.asCell()) &&
+                                 aValue.kind() != JSTRACE_STRING;
     if (!delegateMightNeedMarking && !valueMightNeedMarking) {
       return;
     }
 
-    if (!AddToCCKind(aKeyKind)) {
-      aKey = nullptr;
+    if (!AddToCCKind(aKey.kind())) {
+      aKey = JS::GCCellPtr::NullPtr();
     }
 
-    if (delegateMightNeedMarking && aKeyKind == JSTRACE_OBJECT) {
-      JSObject* kdelegate = js::GetWeakmapKeyDelegate((JSObject*)aKey);
+    if (delegateMightNeedMarking && aKey.isObject()) {
+      JSObject* kdelegate = js::GetWeakmapKeyDelegate(aKey.toObject());
       if (kdelegate && !xpc_IsGrayGCThing(kdelegate)) {
-        if (JS::UnmarkGrayGCThingRecursively(aKey, JSTRACE_OBJECT)) {
+        if (JS::UnmarkGrayGCThingRecursively(aKey.asCell(), JSTRACE_OBJECT)) {
           tracer->mAnyMarked = true;
         }
       }
     }
 
-    if (aValue && xpc_IsGrayGCThing(aValue) &&
-        (!aKey || !xpc_IsGrayGCThing(aKey)) &&
+    if (aValue && xpc_IsGrayGCThing(aValue.asCell()) &&
+        (!aKey || !xpc_IsGrayGCThing(aKey.asCell())) &&
         (!aMap || !xpc_IsGrayGCThing(aMap)) &&
-        aValueKind != JSTRACE_SHAPE) {
-      if (JS::UnmarkGrayGCThingRecursively(aValue, aValueKind)) {
+        aValue.kind() != JSTRACE_SHAPE) {
+      if (JS::UnmarkGrayGCThingRecursively(aValue.asCell(), aValue.kind())) {
         tracer->mAnyMarked = true;
       }
     }
   }
 
   bool mAnyMarked;
 };
 
@@ -577,34 +574,20 @@ CycleCollectedJSRuntime::DescribeGCThing
         AssignJSFlatString(chars, flat);
         NS_ConvertUTF16toUTF8 fname(chars);
         JS_snprintf(name, sizeof(name),
                     "JS Object (Function - %s)", fname.get());
       } else {
         JS_snprintf(name, sizeof(name), "JS Object (Function)");
       }
     } else {
-      JS_snprintf(name, sizeof(name), "JS Object (%s)",
-                  clasp->name);
+      JS_snprintf(name, sizeof(name), "JS Object (%s)", clasp->name);
     }
   } else {
-    static const char trace_types[][11] = {
-      "Object",
-      "String",
-      "Symbol",
-      "Script",
-      "LazyScript",
-      "IonCode",
-      "Shape",
-      "BaseShape",
-      "TypeObject",
-    };
-    static_assert(MOZ_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1,
-                  "JSTRACE_LAST enum must match trace_types count.");
-    JS_snprintf(name, sizeof(name), "JS %s", trace_types[aTraceKind]);
+    JS_snprintf(name, sizeof(name), "JS %s", JS::GCTraceKindToAscii(aTraceKind));
   }
 
   // Disable printing global for objects while we figure out ObjShrink fallout.
   aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
 }
 
 void
 CycleCollectedJSRuntime::NoteGCThingJSChildren(void* aThing,
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozilla/JSONWriter.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/nsMemoryInfoDumper.h"
+#include "mozilla/DebugOnly.h"
 #include "nsDumpUtils.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "nsIConsoleService.h"
 #include "nsCycleCollector.h"
 #include "nsICycleCollectorListener.h"
@@ -216,17 +217,17 @@ doGCCCDump(const nsCString& aInputStr)
                                doAllTracesGCCCDump,
                                /* dumpChildProcesses = */ true);
   NS_DispatchToMainThread(runnable);
 }
 
 bool
 SetupFifo()
 {
-  static bool fifoCallbacksRegistered = false;
+  static DebugOnly<bool> fifoCallbacksRegistered = false;
 
   if (!FifoWatcher::MaybeCreate()) {
     return false;
   }
 
   MOZ_ASSERT(!fifoCallbacksRegistered,
              "FifoWatcher callbacks should be registered only once");