Merge m-c to fx-team.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 28 Feb 2014 10:17:27 -0500
changeset 171627 9f98b006e8966216829b2f68a5fda26b04a780b4
parent 171626 8bd3a004c5f48fac14d626745ecd89598e957981 (current diff)
parent 171578 53dbf19f2f8d5de981e24cd488e5a1cbb8d2b677 (diff)
child 171667 74cf230995da4bcbc7bc864402989cb95c97b734
child 171775 ae231333480e32538662ee8c0332423bb7d5afdf
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
Merge m-c to fx-team.
browser/base/content/browser.xul
content/base/public/nsINameSpaceManager.h
dom/base/ConsoleAPI.js
dom/base/ConsoleAPIStorage.jsm
dom/webidl/WorkerConsole.webidl
dom/workers/Console.h
xpcom/base/WindowsVersion.h
--- a/accessible/src/generic/BaseAccessibles.cpp
+++ b/accessible/src/generic/BaseAccessibles.cpp
@@ -7,17 +7,16 @@
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessibleWrap.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "Role.h"
 #include "States.h"
-#include "nsINameSpaceManager.h"
 #include "nsIURI.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // LeafAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -25,26 +25,26 @@
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMXULPopupElement.h"
 #include "nsIEditingSession.h"
 #include "nsEventStateManager.h"
 #include "nsIFrame.h"
 #include "nsIInterfaceRequestorUtils.h"
-#include "nsINameSpaceManager.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIPresShell.h"
 #include "nsIServiceManager.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsUnicharUtils.h"
 #include "nsIURI.h"
 #include "nsIWebNavigation.h"
 #include "nsFocusManager.h"
+#include "nsNameSpaceManager.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/Element.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif
@@ -390,18 +390,17 @@ DocAccessible::GetDocType(nsAString& aDo
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetNameSpaceURIForID(int16_t aNameSpaceID, nsAString& aNameSpaceURI)
 {
   if (mDocumentNode) {
-    nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
-        do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
+    nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance();
     if (nameSpaceManager)
       return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetWindowHandle(void** aWindow)
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -15,21 +15,21 @@
 
 #include "nsContentList.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIEditor.h"
 #include "nsIFormControl.h"
-#include "nsINameSpaceManager.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlFrame.h"
+#include "nsNameSpaceManager.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::a11y;
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -17,28 +17,28 @@
 #include "Role.h"
 #include "States.h"
 #include "TreeWalker.h"
 
 #include "mozilla/dom/HTMLTableElement.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMRange.h"
 #include "nsISelectionPrivate.h"
-#include "nsINameSpaceManager.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDocument.h"
 #include "nsIMutableArray.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIPresShell.h"
 #include "nsITableCellLayout.h"
 #include "nsFrameSelection.h"
 #include "nsError.h"
 #include "nsArrayUtils.h"
 #include "nsComponentManagerUtils.h"
+#include "nsNameSpaceManager.h"
 #include "nsTableCellFrame.h"
 #include "nsTableOuterFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableCellAccessible
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -24,19 +24,19 @@
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 #include "nsIMutableArray.h"
 #include "nsIFrame.h"
 #include "nsIScrollableFrame.h"
-#include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIServiceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsTextFormatter.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsEventMap.h"
 #include "nsArrayUtils.h"
 #include "mozilla/Preferences.h"
 
 #include "oleacc.h"
--- a/accessible/src/windows/msaa/XULMenuAccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/XULMenuAccessibleWrap.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "XULMenuAccessibleWrap.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULMenuAccessibleWrap
 ////////////////////////////////////////////////////////////////////////////////
 
 XULMenuitemAccessibleWrap::
--- a/accessible/src/windows/sdn/sdnAccessible.cpp
+++ b/accessible/src/windows/sdn/sdnAccessible.cpp
@@ -9,16 +9,17 @@
 
 #include "DocAccessibleWrap.h"
 
 #include "nsAttrName.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleTypes.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMCSSStyleDeclaration.h"
+#include "nsNameSpaceManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWinUtils.h"
 
 #include "nsAutoPtr.h"
 
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
@@ -171,18 +172,17 @@ sdnAccessible::get_attributesForNames(un
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (!mNode->IsElement())
     return S_FALSE;
 
   nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mNode));
-  nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
-    do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
+  nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance();
 
   int32_t index = 0;
   for (index = 0; index < aMaxAttribs; index++) {
     aAttribValues[index] = nullptr;
     if (aAttribNames[index]) {
       nsAutoString attributeValue, nameSpaceURI;
       nsAutoString attributeName(nsDependentString(
         static_cast<const wchar_t*>(aAttribNames[index])));
--- a/accessible/src/xul/XULElementAccessibles.cpp
+++ b/accessible/src/xul/XULElementAccessibles.cpp
@@ -17,17 +17,17 @@
 #include "TextUpdater.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 #include "nsIAccessibleRelation.h"
 #include "nsIDOMXULDescriptionElement.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsTextBoxFrame.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULLabelAccessible
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -20,19 +20,19 @@
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMXULButtonElement.h"
 #include "nsIDOMXULCheckboxElement.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULTextboxElement.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
-#include "nsINameSpaceManager.h"
 #include "nsITextControlFrame.h"
 #include "nsMenuPopupFrame.h"
+#include "nsNameSpaceManager.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULButtonAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/addon-sdk/source/lib/sdk/console/plain-text.js
+++ b/addon-sdk/source/lib/sdk/console/plain-text.js
@@ -56,19 +56,16 @@ function PlainTextConsole(print) {
   // As we freeze the console object, we can't modify this property afterward
   Object.defineProperty(console, "maxLogLevel", {
     get: function() {
       return logLevel;
     }
   });
 
   // We defined the `__exposedProps__` in our console chrome object.
-  // Although it seems redundant, because we use `createObjectIn` too, in
-  // worker.js, we are following what `ConsoleAPI` does. See:
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#132
   //
   // Meanwhile we're investigating with the platform team if `__exposedProps__`
   // are needed, or are just a left-over.
 
   console.__exposedProps__ = Object.keys(ConsoleAPI.prototype).reduce(function(exposed, prop) {
     exposed[prop] = "r";
     return exposed;
   }, {});
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -194,21 +194,17 @@ const WorkerSandbox = Class({
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
       let win = getUnsafeWindow(window);
 
-      // export our chrome console to content window, using the same approach
-      // of `ConsoleAPI`:
-      // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#150
-      //
-      // and described here:
+      // export our chrome console to content window, as described here:
       // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
       let con = Cu.createObjectIn(win);
 
       let genPropDesc = function genPropDesc(fun) {
         return { enumerable: true, configurable: true, writable: true,
           value: console[fun] };
       }
 
--- a/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
+++ b/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
@@ -273,21 +273,17 @@ const WorkerSandbox = EventEmitter.compo
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
       let win = window.wrappedJSObject ? window.wrappedJSObject : window;
 
-      // export our chrome console to content window, using the same approach
-      // of `ConsoleAPI`:
-      // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#150
-      //
-      // and described here:
+      // export our chrome console to content window as described here:
       // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
       let con = Cu.createObjectIn(win);
 
       let genPropDesc = function genPropDesc(fun) {
         return { enumerable: true, configurable: true, writable: true,
           value: console[fun] };
       }
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -121,16 +121,31 @@ var shell = {
     // Bail if there isn't a valid crashID.
     if (!this.CrashSubmit || !crashID && !this.CrashSubmit.pendingIDs().length) {
       return;
     }
 
     // purge the queue.
     this.CrashSubmit.pruneSavedDumps();
 
+    // check for environment affecting crash reporting
+    let env = Cc["@mozilla.org/process/environment;1"]
+                .getService(Ci.nsIEnvironment);
+    let shutdown = env.get("MOZ_CRASHREPORTER_SHUTDOWN");
+    if (shutdown) {
+      let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
+                         .getService(Ci.nsIAppStartup);
+      appStartup.quit(Ci.nsIAppStartup.eForceQuit);
+    }
+
+    let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
+    if (noReport) {
+      return;
+    }
+
     try {
       // Check if we should automatically submit this crash.
       if (Services.prefs.getBoolPref('app.reportCrashes')) {
         this.submitCrash(crashID);
       } else {
         this.deleteCrash(crashID);
       }
     } catch (e) {
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "remote": "", 
         "branch": "", 
         "revision": ""
     }, 
-    "revision": "8911c19f1f8d6fabd76c3892b62e6b87da9ff76e", 
+    "revision": "46003c7d6ba57fee31542ad22656334a34d4d8ef", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
@@ -91,17 +91,17 @@
   <project name="platform/prebuilt" path="prebuilt" revision="248d92592df169569c387a91db56b1fedd6e5d29"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="395aff045276107a285daf0392d0884a98b9f538"/>
   <project name="platform/system/core" path="system/core" revision="70229cf62037cfb70ec2257f7c78b5550628fb53"/>
   <project name="platform/system/extras" path="system/extras" revision="a5dd0ea60ce973c2c762b41835a43316fb4ff9e3"/>
   <project name="platform/system/media" path="system/media" revision="fbb3d9b4c5bf59071424e820e872e3f64f0a244a"/>
   <project name="platform/system/netd" path="system/netd" revision="2e226e6e636ca0a8cc4c51093e46f4baba1ffcce"/>
   <project name="platform/system/vold" path="system/vold" revision="8ac5eef8ea3a456b96d52ce2091bf6d814782d8c"/>
   <!-- hamachi specific things -->
-  <project name="quic/lf/b2g/build" path="device/qcom/b2g_common" revision="43829d56fab6ad16883f1ac9f55ffb9aa5863543"/>
+  <project name="quic/lf/b2g/build" path="device/qcom/b2g_common" revision="6d90df25658d59dfcfd34f83e74e75c3af3901af"/>
   <project name="quic/lf/b2g/external/jsmin" path="external/jsmin" revision="cec896f0affaa0226c02605ad28d42df1bc0e393"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="d13aaf080177b7c48f243d51827db5c7a7873cd0"/>
   <project name="platform/vendor/qcom/msm7627a" path="device/qcom/msm7627a" revision="f06bcacc6f13cec895dc5d4c2385c076396194ec"/>
   <project name="android-device-hamachi" path="device/qcom/hamachi" remote="b2g" revision="9071ac8f0830979fe4a96ce47c7443d8adf0929d"/>
   <project name="kernel/msm" path="kernel" revision="a6578b9cacf9079f2dcf5bfe77c31b1be18809e3"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="19933e5d182a4799c6217b19a18562193a419298"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="5a58382180c70d0c446badc9c9837918ab69ec60"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="20d83ab382a1f813702421e76c2f9f994585990e"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9422aca1931ba6c68784f9e80bb1b6a7fcfd92e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3e9005ca10de70f6a55cf52c87a8b96937acd481"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="990ddd9f96fd4bac333d1178e56985d09bb26173"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -334,17 +334,17 @@
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xul.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
-@BINPATH@/components/ConsoleAPI.js
+@BINPATH@/components/ConsoleAPIStorage.js
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/PhoneNumberService.js
 @BINPATH@/components/PhoneNumberService.manifest
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -213,17 +213,19 @@
           <hbox id="UITourTooltipButtons" flex="1" align="center"/>
         </vbox>
         <vbox align="start">
           <toolbarbutton id="UITourTooltipClose" class="close-icon"
                          tooltiptext="&uiTour.infoPanel.close;"/>
         </vbox>
       </hbox>
     </panel>
+    <!-- type="default" forces frames to be created so that the panel's size can be determined --> 
     <panel id="UITourHighlightContainer"
+           type="default"
            hidden="true"
            noautofocus="true"
            noautohide="true"
            flip="none"
            consumeoutsideclicks="false"
            mousethrough="always">
       <box id="UITourHighlight"></box>
     </panel>
--- a/browser/devtools/webconsole/test/browser_console_consolejsm_output.js
+++ b/browser/devtools/webconsole/test/browser_console_consolejsm_output.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Test that Console.jsm outputs messages to the Browser Console, bug 851231.
 
 function test()
 {
-  let storage = Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", {}).ConsoleAPIStorage;
+  let storage = Cc["@mozilla.org/consoleAPI-storage;1"].getService(Ci.nsIConsoleAPIStorage);
   storage.clearEvents();
 
   let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
   console.log("bug861338-log-cached");
 
   HUDService.toggleBrowserConsole().then(consoleOpened);
   let hud = null;
 
--- a/browser/devtools/webconsole/test/browser_console_private_browsing.js
+++ b/browser/devtools/webconsole/test/browser_console_private_browsing.js
@@ -6,17 +6,18 @@
 // Bug 874061: test for how the browser and web consoles display messages coming
 // from private windows. See bug for description of expected behavior.
 
 function test()
 {
   const TEST_URI = "data:text/html;charset=utf8,<p>hello world! bug 874061" +
                    "<button onclick='console.log(\"foobar bug 874061\");" +
                    "fooBazBaz.yummy()'>click</button>";
-  let ConsoleAPIStorage = Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", {}).ConsoleAPIStorage;
+  let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+                            .getService(Ci.nsIConsoleAPIStorage);
   let privateWindow, privateBrowser, privateTab, privateContent;
   let hud, expectedMessages, nonPrivateMessage;
 
   // This test is slightly more involved: it opens the web console twice,
   // a new private window once, and the browser console twice. We can get
   // a timeout with debug builds on slower machines.
   requestLongerTimeout(2);
   start();
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js
@@ -28,19 +28,19 @@ function test() {
     let node = [...result.matched][0];
     ok(node, "found trace log node");
 
     let obj = node._messageObject;
     ok(obj, "console.trace message object");
 
     // The expected stack trace object.
     let stacktrace = [
-      { filename: TEST_URI, lineNumber: 9, functionName: "window.foobar585956c", language: 2 },
-      { filename: TEST_URI, lineNumber: 14, functionName: "foobar585956b", language: 2 },
-      { filename: TEST_URI, lineNumber: 18, functionName: "foobar585956a", language: 2 },
-      { filename: TEST_URI, lineNumber: 21, functionName: null, language: 2 }
+      { filename: TEST_URI, functionName: "window.foobar585956c", language: 2, lineNumber: 9 },
+      { filename: TEST_URI, functionName: "foobar585956b", language: 2, lineNumber: 14 },
+      { filename: TEST_URI, functionName: "foobar585956a", language: 2, lineNumber: 18 },
+      { filename: TEST_URI, functionName: "", language: 2, lineNumber: 21 }
     ];
 
     ok(obj._stacktrace, "found stacktrace object");
     is(obj._stacktrace.toSource(), stacktrace.toSource(), "stacktrace is correct");
     isnot(node.textContent.indexOf("bug-585956"), -1, "found file name");
   }
 }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -341,17 +341,17 @@
 @BINPATH@/components/xul.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
 @BINPATH@/components/telemetry.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
-@BINPATH@/components/ConsoleAPI.js
+@BINPATH@/components/ConsoleAPIStorage.js
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/browser/components/BrowserFeeds.manifest
 @BINPATH@/browser/components/FeedConverter.js
 @BINPATH@/browser/components/FeedWriter.js
 @BINPATH@/browser/components/fuelApplication.manifest
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -1,16 +1,16 @@
 # 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/.
 
 # Setup for build cache
 
 bucket=
-if test -f "$topsrcdir/sccache/sccache.py"; then
+if test -z "$SCCACHE_DISABLE" -a -f "$topsrcdir/sccache/sccache.py"; then
     case `hostname` in
     try*spot*.use1.mozilla.com|try*ec2*.use1.mozilla.com)
         bucket=mozilla-releng-s3-cache-us-east-1-try
         ;;
     try*spot*.usw2.mozilla.com|try*ec2*.usw2.mozilla.com)
         bucket=mozilla-releng-s3-cache-us-west-2-try
         ;;
     esac
--- a/content/base/public/moz.build
+++ b/content/base/public/moz.build
@@ -49,23 +49,23 @@ EXPORTS += [
     'nsIContent.h',
     'nsIContentInlines.h',
     'nsIContentIterator.h',
     'nsIContentSerializer.h',
     'nsIDocument.h',
     'nsIDocumentInlines.h',
     'nsIDocumentObserver.h',
     'nsIMutationObserver.h',
-    'nsINameSpaceManager.h',
     'nsINode.h',
     'nsINodeInfo.h',
     'nsINodeList.h',
     'nsIScriptElement.h',
     'nsIStyleSheetLinkingElement.h',
     'nsLineBreaker.h',
+    'nsNameSpaceManager.h',
     'nsReferencedElement.h',
     'nsTreeSanitizer.h',
     'nsViewportInfo.h',
     'nsXMLNameSpaceMap.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'DirectionalityUtils.h',
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -63,17 +63,17 @@ class nsIDragSession;
 class nsIEditor;
 class nsIFragmentContentSink;
 class nsIFrame;
 class nsIImageLoadingContent;
 class nsIInterfaceRequestor;
 class nsIIOService;
 class nsIJSRuntimeService;
 class nsILineBreaker;
-class nsINameSpaceManager;
+class nsNameSpaceManager;
 class nsINodeInfo;
 class nsIObserver;
 class nsIParser;
 class nsIParserService;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRunnable;
 class nsIScriptContext;
@@ -446,17 +446,17 @@ public:
   static nsIDocument* GetDocumentFromContext();
 
   // Check if a node is in the document prolog, i.e. before the document
   // element.
   static bool InProlog(nsINode *aNode);
 
   static nsIParserService* GetParserService();
 
-  static nsINameSpaceManager* NameSpaceManager()
+  static nsNameSpaceManager* NameSpaceManager()
   {
     return sNameSpaceManager;
   }
 
   static nsIIOService* GetIOService()
   {
     return sIOService;
   }
@@ -2150,17 +2150,17 @@ private:
                                       const nsString* aClasses);
 
   static nsIXPConnect *sXPConnect;
 
   static nsIScriptSecurityManager *sSecurityManager;
 
   static nsIParserService *sParserService;
 
-  static nsINameSpaceManager *sNameSpaceManager;
+  static nsNameSpaceManager *sNameSpaceManager;
 
   static nsIIOService *sIOService;
 
   static bool sImgLoaderInitialized;
   static void InitImgLoader();
 
   // The following four members are initialized lazily
   static imgLoader* sImgLoader;
--- a/content/base/public/nsINodeInfo.h
+++ b/content/base/public/nsINodeInfo.h
@@ -21,18 +21,18 @@
  * -- jst@netscape.com
  */
 
 #ifndef nsINodeInfo_h___
 #define nsINodeInfo_h___
 
 #include "nsCOMPtr.h"            // for member
 #include "nsIAtom.h"             // for member (in nsCOMPtr)
-#include "nsINameSpaceManager.h" // for kNameSpaceID_*
 #include "nsISupports.h"         // for base class
+#include "nsNameSpaceManager.h"  // for kNameSpaceID_*
 
 #ifdef MOZILLA_INTERNAL_API
 #include "nsDOMString.h"
 #endif
 
 class nsIDocument;
 class nsIURI;
 class nsIPrincipal;
rename from content/base/public/nsINameSpaceManager.h
rename to content/base/public/nsNameSpaceManager.h
--- a/content/base/public/nsINameSpaceManager.h
+++ b/content/base/public/nsNameSpaceManager.h
@@ -1,17 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef nsINameSpaceManager_h___
-#define nsINameSpaceManager_h___
+#ifndef nsNameSpaceManager_h___
+#define nsNameSpaceManager_h___
 
-#include "nsISupports.h"
+#include "nsDataHashtable.h"
+#include "nsTArray.h"
+
+#include "mozilla/StaticPtr.h"
 
 class nsIAtom;
 class nsAString;
 
 #define kNameSpaceID_Unknown -1
 // 0 is special at C++, so use a static const int32_t for
 // kNameSpaceID_None to keep if from being cast to pointers
 // Note that the XBL cache assumes (and asserts) that it can treat a
@@ -23,50 +26,86 @@ static const int32_t kNameSpaceID_None =
 #define kNameSpaceID_XLink    4
 #define kNameSpaceID_XSLT     5
 #define kNameSpaceID_XBL      6
 #define kNameSpaceID_MathML   7
 #define kNameSpaceID_RDF      8
 #define kNameSpaceID_XUL      9
 #define kNameSpaceID_SVG      10
 #define kNameSpaceID_LastBuiltin          10 // last 'built-in' namespace
- 
-#define NS_NAMESPACEMANAGER_CONTRACTID "@mozilla.org/content/namespacemanager;1"
+
+class nsNameSpaceKey : public PLDHashEntryHdr
+{
+public:
+  typedef const nsAString* KeyType;
+  typedef const nsAString* KeyTypePointer;
+
+  nsNameSpaceKey(KeyTypePointer aKey) : mKey(aKey)
+  {
+  }
+  nsNameSpaceKey(const nsNameSpaceKey& toCopy) : mKey(toCopy.mKey)
+  {
+  }
 
-#define NS_INAMESPACEMANAGER_IID \
-  { 0xd74e83e6, 0xf932, 0x4289, \
-    { 0xac, 0x95, 0x9e, 0x10, 0x24, 0x30, 0x88, 0xd6 } }
+  KeyType GetKey() const
+  {
+    return mKey;
+  }
+  bool KeyEquals(KeyType aKey) const
+  {
+    return mKey->Equals(*aKey);
+  }
 
+  static KeyTypePointer KeyToPointer(KeyType aKey)
+  {
+    return aKey;
+  }
+  static PLDHashNumber HashKey(KeyTypePointer aKey) {
+    return mozilla::HashString(*aKey);
+  }
+
+  enum {
+    ALLOW_MEMMOVE = true
+  };
+
+private:
+  const nsAString* mKey;
+};
+ 
 /**
  * The Name Space Manager tracks the association between a NameSpace
  * URI and the int32_t runtime id. Mappings between NameSpaces and 
  * NameSpace prefixes are managed by nsINameSpaces.
  *
  * All NameSpace URIs are stored in a global table so that IDs are
  * consistent accross the app. NameSpace IDs are only consistent at runtime
  * ie: they are not guaranteed to be consistent accross app sessions.
  *
- * The nsINameSpaceManager needs to have a live reference for as long as
+ * The nsNameSpaceManager needs to have a live reference for as long as
  * the NameSpace IDs are needed.
  *
  */
 
-class nsINameSpaceManager : public nsISupports
+class nsNameSpaceManager
 {
 public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_INAMESPACEMANAGER_IID)
+  virtual ~nsNameSpaceManager() {}
 
   virtual nsresult RegisterNameSpace(const nsAString& aURI,
-                                     int32_t& aNameSpaceID) = 0;
+                                     int32_t& aNameSpaceID);
+
+  virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
+  virtual int32_t GetNameSpaceID(const nsAString& aURI);
+
+  virtual bool HasElementCreator(int32_t aNameSpaceID);
 
-  virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI) = 0;
-  virtual int32_t GetNameSpaceID(const nsAString& aURI) = 0;
+  static nsNameSpaceManager* GetInstance();
+private:
+  bool Init();
+  nsresult AddNameSpace(const nsAString& aURI, const int32_t aNameSpaceID);
 
-  virtual bool HasElementCreator(int32_t aNameSpaceID) = 0;
+  nsDataHashtable<nsNameSpaceKey,int32_t> mURIToIDTable;
+  nsTArray< nsAutoPtr<nsString> > mURIArray;
+
+  static mozilla::StaticAutoPtr<nsNameSpaceManager> sInstance;
 };
  
-NS_DEFINE_STATIC_IID_ACCESSOR(nsINameSpaceManager, NS_INAMESPACEMANAGER_IID)
-
-nsresult NS_GetNameSpaceManager(nsINameSpaceManager** aInstancePtrResult);
-
-void NS_NameSpaceManagerShutdown();
-
-#endif // nsINameSpaceManager_h___
+#endif // nsNameSpaceManager_h___
--- a/content/base/src/Attr.cpp
+++ b/content/base/src/Attr.cpp
@@ -7,26 +7,26 @@
  * Implementation of DOM Core's nsIDOMAttr node.
  */
 
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/AttrBinding.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "nsContentCreatorFunctions.h"
-#include "nsINameSpaceManager.h"
 #include "nsError.h"
 #include "nsUnicharUtils.h"
 #include "nsDOMString.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsEventDispatcher.h"
 #include "nsGkAtoms.h"
 #include "nsCOMArray.h"
+#include "nsNameSpaceManager.h"
 #include "nsNodeUtils.h"
 #include "nsEventListenerManager.h"
 #include "nsTextNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsWrapperCacheInlines.h"
 
 nsIAttribute::nsIAttribute(nsDOMAttributeMap* aAttrMap,
--- a/content/base/src/DocumentFragment.cpp
+++ b/content/base/src/DocumentFragment.cpp
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Implementation of DOM Core's nsIDOMDocumentFragment.
  */
 
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/Element.h" // for NS_IMPL_ELEMENT_CLONE
-#include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsNodeInfoManager.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsDOMString.h"
 #include "nsContentUtils.h" // for NS_INTERFACE_MAP_ENTRY_TEAROFF
 #include "mozilla/dom/DocumentFragmentBinding.h"
 #include "nsPIDOMWindow.h"
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -36,17 +36,17 @@
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsEventStateManager.h"
 #include "nsIDOMEvent.h"
 #include "nsDOMCID.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsDOMCSSAttrDeclaration.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsContentList.h"
 #include "nsDOMTokenList.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsError.h"
 #include "nsDOMString.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/ContentEvents.h"
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -39,17 +39,17 @@
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsEventStateManager.h"
 #include "nsIDOMEvent.h"
 #include "nsDOMCID.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsDOMCSSAttrDeclaration.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsContentList.h"
 #include "nsDOMTokenList.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsError.h"
 #include "nsDOMString.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/InternalMutationEvent.h"
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -17,18 +17,18 @@
 #include "nsISupports.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsIHTMLCollection.h"
 #include "nsIDOMNodeList.h"
 #include "nsINodeList.h"
 #include "nsStubMutationObserver.h"
 #include "nsIAtom.h"
-#include "nsINameSpaceManager.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsNameSpaceManager.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "mozilla/HashFunctions.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 }
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -191,17 +191,17 @@ using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
 nsIParserService *nsContentUtils::sParserService = nullptr;
-nsINameSpaceManager *nsContentUtils::sNameSpaceManager;
+nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
 imgLoader *nsContentUtils::sImgLoader;
 imgLoader *nsContentUtils::sPrivateImgLoader;
 imgICache *nsContentUtils::sImgCache;
 imgICache *nsContentUtils::sPrivateImgCache;
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
@@ -366,30 +366,30 @@ nsresult
 nsContentUtils::Init()
 {
   if (sInitialized) {
     NS_WARNING("Init() called twice");
 
     return NS_OK;
   }
 
-  nsresult rv = NS_GetNameSpaceManager(&sNameSpaceManager);
-  NS_ENSURE_SUCCESS(rv, rv);
+  sNameSpaceManager = nsNameSpaceManager::GetInstance();
+  NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
 
   sXPConnect = nsXPConnect::XPConnect();
 
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
   // Getting the first context can trigger GC, so do this non-lazily.
   sXPConnect->InitSafeJSContext();
 
-  rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
+  nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
 
   rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1453,17 +1453,16 @@ nsContentUtils::Shutdown()
   uint32_t i;
   for (i = 0; i < PropertiesFile_COUNT; ++i)
     NS_IF_RELEASE(sStringBundles[i]);
 
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
-  NS_IF_RELEASE(sNameSpaceManager);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sImgLoader);
   NS_IF_RELEASE(sPrivateImgLoader);
   NS_IF_RELEASE(sImgCache);
   NS_IF_RELEASE(sPrivateImgCache);
--- a/content/base/src/nsDOMAttributeMap.cpp
+++ b/content/base/src/nsDOMAttributeMap.cpp
@@ -13,17 +13,17 @@
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/MozNamedAttrMapBinding.h"
 #include "nsAttrName.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsNodeInfoManager.h"
 #include "nsUnicharUtils.h"
 #include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -59,17 +59,17 @@
 #include "nsView.h"
 #include "nsAsyncDOMEvent.h"
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 
 #include "nsThreadUtils.h"
 
 #include "nsIDOMChromeWindow.h"
 #include "nsInProcessTabChildGlobal.h"
 
 #include "Layers.h"
 
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -14,17 +14,16 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMDocument.h"
 #include "nsReadableUtils.h"
 #include "mozilla/InternalMutationEvent.h"
-#include "nsINameSpaceManager.h"
 #include "nsIURI.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMText.h"
 #include "nsCOMPtr.h"
 #include "nsDOMString.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsChangeHint.h"
 #include "nsEventDispatcher.h"
--- a/content/base/src/nsHTMLContentSerializer.cpp
+++ b/content/base/src/nsHTMLContentSerializer.cpp
@@ -10,17 +10,17 @@
  * string that could be parsed into more or less the original DOM.
  */
 
 #include "nsHTMLContentSerializer.h"
 
 #include "nsIDOMElement.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -56,30 +56,30 @@
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsILinkHandler.h"
-#include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsIServiceManager.h"
 #include "nsIURL.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIWebNavigation.h"
 #include "nsIWidget.h"
 #include "nsLayoutUtils.h"
+#include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsNodeInfoManager.h"
 #include "nsNodeUtils.h"
 #include "nsPIBoxObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
 #include "nsRuleProcessorData.h"
 #include "nsString.h"
--- a/content/base/src/nsNameSpaceManager.cpp
+++ b/content/base/src/nsNameSpaceManager.cpp
@@ -3,26 +3,26 @@
  * 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/. */
 
 /*
  * A class for managing namespace IDs and mapping back and forth
  * between namespace IDs and namespace URIs.
  */
 
+#include "nsNameSpaceManager.h"
+
 #include "nscore.h"
-#include "nsINameSpaceManager.h"
 #include "nsAutoPtr.h"
 #include "nsINodeInfo.h"
 #include "nsCOMArray.h"
-#include "nsTArray.h"
 #include "nsContentCreatorFunctions.h"
-#include "nsDataHashtable.h"
 #include "nsString.h"
 #include "nsINodeInfo.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/XBLChildrenElement.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define kXMLNSNameSpaceURI "http://www.w3.org/2000/xmlns/"
 #define kXMLNameSpaceURI "http://www.w3.org/XML/1998/namespace"
@@ -30,113 +30,60 @@ using namespace mozilla::dom;
 #define kXLinkNameSpaceURI "http://www.w3.org/1999/xlink"
 #define kXSLTNameSpaceURI "http://www.w3.org/1999/XSL/Transform"
 #define kXBLNameSpaceURI "http://www.mozilla.org/xbl"
 #define kMathMLNameSpaceURI "http://www.w3.org/1998/Math/MathML"
 #define kRDFNameSpaceURI "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 #define kXULNameSpaceURI "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
 #define kSVGNameSpaceURI "http://www.w3.org/2000/svg"
 
-class nsNameSpaceKey : public PLDHashEntryHdr
-{
-public:
-  typedef const nsAString* KeyType;
-  typedef const nsAString* KeyTypePointer;
-
-  nsNameSpaceKey(KeyTypePointer aKey) : mKey(aKey)
-  {
-  }
-  nsNameSpaceKey(const nsNameSpaceKey& toCopy) : mKey(toCopy.mKey)
-  {
-  }
+StaticAutoPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
 
-  KeyType GetKey() const
-  {
-    return mKey;
-  }
-  bool KeyEquals(KeyType aKey) const
-  {
-    return mKey->Equals(*aKey);
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey)
-  {
-    return aKey;
-  }
-  static PLDHashNumber HashKey(KeyTypePointer aKey) {
-    return HashString(*aKey);
+/* static */ nsNameSpaceManager*
+nsNameSpaceManager::GetInstance() {
+  if (!sInstance) {
+    sInstance = new nsNameSpaceManager();
+    if (sInstance->Init()) {
+      ClearOnShutdown(&sInstance);
+    } else {
+      delete sInstance;
+      sInstance = nullptr;
+    }
   }
 
-  enum { 
-    ALLOW_MEMMOVE = true
-  };
-
-private:
-  const nsAString* mKey;
-};
-
-class NameSpaceManagerImpl : public nsINameSpaceManager {
-public:
-  NameSpaceManagerImpl()
-    : mURIToIDTable(32)
-  {
-  }
-  virtual ~NameSpaceManagerImpl()
-  {
-  }
-
-  NS_DECL_ISUPPORTS
+  return sInstance;
+}
 
-  nsresult Init();
-
-  nsresult RegisterNameSpace(const nsAString& aURI,  int32_t& aNameSpaceID);
-
-  nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
-  int32_t GetNameSpaceID(const nsAString& aURI);
-
-  bool HasElementCreator(int32_t aNameSpaceID);
-
-private:
-  nsresult AddNameSpace(const nsAString& aURI, const int32_t aNameSpaceID);
-
-  nsDataHashtable<nsNameSpaceKey,int32_t> mURIToIDTable;
-  nsTArray< nsAutoPtr<nsString> > mURIArray;
-};
-
-static NameSpaceManagerImpl* sNameSpaceManager = nullptr;
-
-NS_IMPL_ISUPPORTS1(NameSpaceManagerImpl, nsINameSpaceManager)
-
-nsresult NameSpaceManagerImpl::Init()
+bool nsNameSpaceManager::Init()
 {
   nsresult rv;
 #define REGISTER_NAMESPACE(uri, id) \
   rv = AddNameSpace(NS_LITERAL_STRING(uri), id); \
-  NS_ENSURE_SUCCESS(rv, rv)
+  NS_ENSURE_SUCCESS(rv, false)
 
   // Need to be ordered according to ID.
   REGISTER_NAMESPACE(kXMLNSNameSpaceURI, kNameSpaceID_XMLNS);
   REGISTER_NAMESPACE(kXMLNameSpaceURI, kNameSpaceID_XML);
   REGISTER_NAMESPACE(kXHTMLNameSpaceURI, kNameSpaceID_XHTML);
   REGISTER_NAMESPACE(kXLinkNameSpaceURI, kNameSpaceID_XLink);
   REGISTER_NAMESPACE(kXSLTNameSpaceURI, kNameSpaceID_XSLT);
   REGISTER_NAMESPACE(kXBLNameSpaceURI, kNameSpaceID_XBL);
   REGISTER_NAMESPACE(kMathMLNameSpaceURI, kNameSpaceID_MathML);
   REGISTER_NAMESPACE(kRDFNameSpaceURI, kNameSpaceID_RDF);
   REGISTER_NAMESPACE(kXULNameSpaceURI, kNameSpaceID_XUL);
   REGISTER_NAMESPACE(kSVGNameSpaceURI, kNameSpaceID_SVG);
 
 #undef REGISTER_NAMESPACE
 
-  return NS_OK;
+  return true;
 }
 
 nsresult
-NameSpaceManagerImpl::RegisterNameSpace(const nsAString& aURI, 
-                                        int32_t& aNameSpaceID)
+nsNameSpaceManager::RegisterNameSpace(const nsAString& aURI,
+                                      int32_t& aNameSpaceID)
 {
   if (aURI.IsEmpty()) {
     aNameSpaceID = kNameSpaceID_None; // xmlns="", see bug 75700 for details
 
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
@@ -150,34 +97,34 @@ NameSpaceManagerImpl::RegisterNameSpace(
   }
 
   NS_POSTCONDITION(aNameSpaceID >= -1, "Bogus namespace ID");
   
   return rv;
 }
 
 nsresult
-NameSpaceManagerImpl::GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI)
+nsNameSpaceManager::GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI)
 {
   NS_PRECONDITION(aNameSpaceID >= 0, "Bogus namespace ID");
   
   int32_t index = aNameSpaceID - 1; // id is index + 1
   if (index < 0 || index >= int32_t(mURIArray.Length())) {
     aURI.Truncate();
 
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   aURI = *mURIArray.ElementAt(index);
 
   return NS_OK;
 }
 
 int32_t
-NameSpaceManagerImpl::GetNameSpaceID(const nsAString& aURI)
+nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI)
 {
   if (aURI.IsEmpty()) {
     return kNameSpaceID_None; // xmlns="", see bug 75700 for details
   }
 
   int32_t nameSpaceID;
 
   if (mURIToIDTable.Get(&aURI, &nameSpaceID)) {
@@ -211,29 +158,29 @@ NS_NewElement(Element** aResult,
     NS_ADDREF(*aResult = new XBLChildrenElement(aNodeInfo));
     return NS_OK;
   }
 
   return NS_NewXMLElement(aResult, aNodeInfo);
 }
 
 bool
-NameSpaceManagerImpl::HasElementCreator(int32_t aNameSpaceID)
+nsNameSpaceManager::HasElementCreator(int32_t aNameSpaceID)
 {
   return aNameSpaceID == kNameSpaceID_XHTML ||
 #ifdef MOZ_XUL
          aNameSpaceID == kNameSpaceID_XUL ||
 #endif
          aNameSpaceID == kNameSpaceID_MathML ||
          aNameSpaceID == kNameSpaceID_SVG ||
          false;
 }
 
-nsresult NameSpaceManagerImpl::AddNameSpace(const nsAString& aURI,
-                                            const int32_t aNameSpaceID)
+nsresult nsNameSpaceManager::AddNameSpace(const nsAString& aURI,
+                                          const int32_t aNameSpaceID)
 {
   if (aNameSpaceID < 0) {
     // We've wrapped...  Can't do anything else here; just bail.
     return NS_ERROR_OUT_OF_MEMORY;
   }
   
   NS_ASSERTION(aNameSpaceID - 1 == (int32_t) mURIArray.Length(),
                "BAD! AddNameSpace not called in right order!");
@@ -243,37 +190,8 @@ nsresult NameSpaceManagerImpl::AddNameSp
     delete uri;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mURIToIDTable.Put(uri, aNameSpaceID);
 
   return NS_OK;
 }
-
-nsresult
-NS_GetNameSpaceManager(nsINameSpaceManager** aInstancePtrResult)
-{
-  NS_ENSURE_ARG_POINTER(aInstancePtrResult);
-
-  if (!sNameSpaceManager) {
-    nsCOMPtr<NameSpaceManagerImpl> manager = new NameSpaceManagerImpl();
-    if (manager) {
-      nsresult rv = manager->Init();
-      if (NS_SUCCEEDED(rv)) {
-        manager.swap(sNameSpaceManager);
-      }
-    }
-  }
-
-  *aInstancePtrResult = sNameSpaceManager;
-  NS_ENSURE_TRUE(sNameSpaceManager, NS_ERROR_OUT_OF_MEMORY);
-
-  NS_ADDREF(*aInstancePtrResult);
-
-  return NS_OK;
-}
-
-void
-NS_NameSpaceManagerShutdown()
-{
-  NS_IF_RELEASE(sNameSpaceManager);
-}
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -8,17 +8,17 @@
  * nsIDocumentEncoder to convert a DOM into plaintext in a nice way
  * (eg for copy/paste as plaintext).
  */
 
 #include "nsPlainTextSerializer.h"
 #include "nsLWBrkCIID.h"
 #include "nsIServiceManager.h"
 #include "nsGkAtoms.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 
--- a/content/base/src/nsXHTMLContentSerializer.cpp
+++ b/content/base/src/nsXHTMLContentSerializer.cpp
@@ -10,17 +10,17 @@
  * string that could be parsed into more or less the original DOM.
  */
 
 #include "nsXHTMLContentSerializer.h"
 
 #include "nsIDOMElement.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
--- a/content/base/src/nsXMLContentSerializer.cpp
+++ b/content/base/src/nsXMLContentSerializer.cpp
@@ -13,17 +13,17 @@
 
 #include "nsGkAtoms.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDocumentEncoder.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsString.h"
 #include "prprf.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsAttrName.h"
 #include "nsILineBreaker.h"
--- a/content/base/src/nsXMLNameSpaceMap.cpp
+++ b/content/base/src/nsXMLNameSpaceMap.cpp
@@ -3,19 +3,19 @@
  * 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/. */
 
 /*
  * A class for keeping track of prefix-to-namespace-id mappings
  */
 
 #include "nsXMLNameSpaceMap.h"
-#include "nsINameSpaceManager.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
+#include "nsNameSpaceManager.h"
 
 template <>
 class nsDefaultComparator <nsNameSpaceEntry, nsIAtom*> {
   public:
     bool Equals(const nsNameSpaceEntry& aEntry, nsIAtom* const& aPrefix) const {
       return aEntry.prefix == aPrefix;
     }
 };
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -39,17 +39,17 @@
 #include "nsIScrollableFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsRange.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIDocShell.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsError.h"
 #include "nsScriptLoader.h"
 #include "nsRuleData.h"
 #include "nsIPrincipal.h"
 
 #include "nsPresState.h"
 #include "nsILayoutHistoryState.h"
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -4,17 +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 nsGenericHTMLElement_h___
 #define nsGenericHTMLElement_h___
 
 #include "mozilla/Attributes.h"
 #include "nsMappedAttributeElement.h"
 #include "nsIDOMHTMLElement.h"
-#include "nsINameSpaceManager.h"  // for kNameSpaceID_None
+#include "nsNameSpaceManager.h"  // for kNameSpaceID_None
 #include "nsIFormControl.h"
 #include "nsGkAtoms.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIDOMHTMLMenuElement.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/ValidityState.h"
 #include "mozilla/dom/ElementInlines.h"
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -47,24 +47,24 @@
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsStubDocumentObserver.h"
 #include "nsIHTMLDocument.h"
-#include "nsINameSpaceManager.h"
 #include "nsIDOMHTMLMapElement.h"
 #include "nsICookieService.h"
 #include "nsTArray.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsTextFragment.h"
 #include "nsIScriptGlobalObject.h"
+#include "nsNameSpaceManager.h"
 
 #include "nsIParserService.h"
 
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsITimer.h"
 #include "nsError.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIScriptContext.h"
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -51,17 +51,17 @@
 #include "nsICookieService.h"
 
 #include "nsIServiceManager.h"
 #include "nsIConsoleService.h"
 #include "nsIComponentManager.h"
 #include "nsParserCIID.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMHTMLHeadElement.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/css/Loader.h"
 #include "nsIHttpChannel.h"
 #include "nsIFile.h"
 #include "nsEventListenerManager.h"
 #include "nsFrameSelection.h"
 #include "nsISelectionPrivate.h"//for toStringwithformat code
 
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -366,16 +366,24 @@ MediaSource::GetParentObject() const
 }
 
 JSObject*
 MediaSource::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return MediaSourceBinding::Wrap(aCx, aScope, this);
 }
 
+void
+MediaSource::NotifyEvicted(double aStart, double aEnd)
+{
+  // Cycle through all SourceBuffers and tell them to evict data in
+  // the given range.
+  mSourceBuffers->Evict(aStart, aEnd);
+}
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaSource, nsDOMEventTargetHelper,
                                      mSourceBuffers, mActiveSourceBuffers)
 
 NS_IMPL_ADDREF_INHERITED(MediaSource, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaSource, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -80,16 +80,21 @@ public:
   void SetReadyState(MediaSourceReadyState aState);
 
  // Used by SourceBuffer to call CreateSubDecoder.
   MediaSourceDecoder* GetDecoder()
   {
     return mDecoder;
   }
 
+  // Called by SourceBuffers to notify this MediaSource that data has
+  // been evicted from the buffered data. The start and end times
+  // that were evicted are provided.
+  void NotifyEvicted(double aStart, double aEnd);
+
 private:
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   void DurationChange(double aNewDuration, ErrorResult& aRv);
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "mozilla/mozalloc.h"
 #include "nsISupports.h"
 #include "prlog.h"
 #include "MediaSource.h"
 #include "SubBufferDecoder.h"
 #include "SourceBufferResource.h"
+#include "VideoUtils.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaSourceLog;
 #define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
 #else
 #define LOG(type, msg)
 #endif
 
@@ -233,9 +234,17 @@ MediaSourceReader::ReadMetadata(MediaInf
       decoder->SetAudioReader(reader);
     }
   }
   *aInfo = mInfo;
 
   return NS_OK;
 }
 
+double
+MediaSourceDecoder::GetMediaSourceDuration()
+{
+  return mMediaSource ?
+           mMediaSource->Duration() :
+           mDuration / static_cast<double>(USECS_PER_S);
+}
+
 } // namespace mozilla
--- a/content/media/mediasource/MediaSourceDecoder.h
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -74,16 +74,20 @@ public:
     return mVideoReader;
   }
 
   MediaDecoderReader* GetAudioReader()
   {
     return mAudioReader;
   }
 
+  // Returns the duration in seconds as provided by the attached MediaSource.
+  // If no MediaSource is attached, returns the duration tracked by the decoder.
+  double GetMediaSourceDuration();
+
 private:
   dom::MediaSource* mMediaSource;
 
   nsTArray<nsRefPtr<SubBufferDecoder> > mDecoders;
   nsTArray<MediaDecoderReader*> mReaders; // Readers owned by Decoders.
 
   MediaDecoderReader* mVideoReader;
   MediaDecoderReader* mAudioReader;
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -91,16 +91,33 @@ SubBufferDecoder::SetTransportSeekable(b
 }
 
 layers::ImageContainer*
 SubBufferDecoder::GetImageContainer()
 {
   return mParentDecoder->GetImageContainer();
 }
 
+int64_t
+SubBufferDecoder::ConvertToByteOffset(double aTime)
+{
+  // Uses a conversion based on (aTime/duration) * length.  For the
+  // purposes of eviction this should be adequate since we have the
+  // byte threshold as well to ensure data actually gets evicted and
+  // we ensure we don't evict before the current playable point.
+  double duration = mParentDecoder->GetMediaSourceDuration();
+  if (duration <= 0.0 || IsNaN(duration)) {
+    return -1;
+  }
+  int64_t length = GetResource()->GetLength();
+  MOZ_ASSERT(length > 0);
+  int64_t offset = (aTime / duration) * length;
+  return offset;
+}
+
 namespace dom {
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
@@ -325,17 +342,66 @@ SourceBuffer::AppendData(const uint8_t* 
   LOG(PR_LOG_DEBUG, ("%p Append(ArrayBuffer=%u)", this, aLength));
   StartUpdating();
   // XXX: For future reference: NDA call must run on the main thread.
   mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
                               aLength,
                               mDecoder->GetResource()->GetLength());
   // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
   mDecoder->GetResource()->AppendData(aData, aLength);
+
+  // Eviction uses a byte threshold. If the buffer is greater than the
+  // number of bytes then data is evicted. The time range for this
+  // eviction is reported back to the media source. It will then
+  // evict data before that range across all SourceBuffer's it knows
+  // about.
+  const int evict_threshold = 1000000;
+  bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
+  if (evicted) {
+    double start = 0.0;
+    double end = 0.0;
+    GetBufferedStartEndTime(&start, &end);
+
+    // We notify that we've evicted from the time range 0 through to
+    // the current start point.
+    mMediaSource->NotifyEvicted(0.0, start);
+  }
   StopUpdating();
+
+  // Schedule the state machine thread to ensure playback starts
+  // if required when data is appended.
+  mMediaSource->GetDecoder()->ScheduleStateMachineThread();
+}
+
+void
+SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
+{
+  nsRefPtr<TimeRanges> ranges = new TimeRanges();
+  mDecoder->GetBuffered(ranges);
+  ranges->Normalize();
+  int length = ranges->Length();
+  ErrorResult rv;
+
+  if (aStart) {
+    *aStart = length > 0 ? ranges->Start(0, rv) : 0.0;
+  }
+
+  if (aEnd) {
+    *aEnd = length > 0 ? ranges->End(length - 1, rv) : 0.0;
+  }
+}
+
+void
+SourceBuffer::Evict(double aStart, double aEnd)
+{
+  // Need to map time to byte offset then evict
+  int64_t end = mDecoder->ConvertToByteOffset(aEnd);
+  if (end > 0) {
+    mDecoder->GetResource()->EvictBefore(end);
+  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(SourceBuffer, nsDOMEventTargetHelper, mMediaSource)
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
--- a/content/media/mediasource/SourceBuffer.h
+++ b/content/media/mediasource/SourceBuffer.h
@@ -100,29 +100,36 @@ public:
   void Detach();
   bool IsAttached() const
   {
     return mMediaSource != nullptr;
   }
 
   void Ended();
 
+  // Evict data in the source buffer in the given time range.
+  void Evict(double aStart, double aEnd);
+
 private:
   friend class AsyncEventRunner<SourceBuffer>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
 
+  // Provide the minimum start time and maximum end time that is available
+  // in the data buffered by this SourceBuffer.
+  void GetBufferedStartEndTime(double* aStart, double* aEnd);
+
   nsRefPtr<MediaSource> mMediaSource;
 
   nsRefPtr<SubBufferDecoder> mDecoder;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
--- a/content/media/mediasource/SourceBufferList.cpp
+++ b/content/media/mediasource/SourceBufferList.cpp
@@ -99,16 +99,24 @@ SourceBufferList::Remove(double aStart, 
     mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
     if (aRv.Failed()) {
       return;
     }
   }
 }
 
 void
+SourceBufferList::Evict(double aStart, double aEnd)
+{
+  for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
+    mSourceBuffers[i]->Evict(aStart, aEnd);
+  }
+}
+
+void
 SourceBufferList::Ended()
 {
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
     mSourceBuffers[i]->Ended();
   }
 }
 
 void
--- a/content/media/mediasource/SourceBufferList.h
+++ b/content/media/mediasource/SourceBufferList.h
@@ -68,16 +68,19 @@ public:
 
   // Calls Remove(aStart, aEnd) on each SourceBuffer in the list.  Aborts on
   // first error, with result returned in aRv.
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
 
   // Mark all SourceBuffers input buffers as ended.
   void Ended();
 
+  // Evicts data for the given time range from each SourceBuffer in the list.
+  void Evict(double aStart, double aEnd);
+
 private:
   friend class AsyncEventRunner<SourceBufferList>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   nsRefPtr<MediaSource> mMediaSource;
   nsTArray<nsRefPtr<SourceBuffer> > mSourceBuffers;
 };
--- a/content/media/mediasource/SourceBufferResource.cpp
+++ b/content/media/mediasource/SourceBufferResource.cpp
@@ -41,17 +41,19 @@ SourceBufferResource::Close()
 }
 
 nsresult
 SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   bool blockingRead = !!aBytes;
 
-  while (blockingRead && !mEnded && mOffset + aCount > GetLength()) {
+  while (blockingRead &&
+         !mEnded &&
+         mOffset + aCount > static_cast<uint64_t>(GetLength())) {
     LOG(PR_LOG_DEBUG, ("%p SBR::Read waiting for data", this));
     mon.Wait();
   }
 
   uint32_t available = GetLength() - mOffset;
   uint32_t count = std::min(aCount, available);
   if (!PR_GetEnv("MOZ_QUIET")) {
     LOG(PR_LOG_DEBUG, ("%p SBR::Read aCount=%u length=%u offset=%u "
@@ -60,17 +62,17 @@ SourceBufferResource::Read(char* aBuffer
                        blockingRead, mEnded));
   }
   if (available == 0) {
     LOG(PR_LOG_DEBUG, ("%p SBR::Read EOF", this));
     *aBytes = 0;
     return NS_OK;
   }
 
-  memcpy(aBuffer, &mInputBuffer[mOffset], count);
+  mInputBuffer.CopyData(mOffset, count, aBuffer);
   *aBytes = count;
   mOffset += count;
   return NS_OK;
 }
 
 nsresult
 SourceBufferResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
@@ -119,21 +121,36 @@ SourceBufferResource::ReadFromCache(char
   ReentrantMonitorAutoEnter mon(mMonitor);
   nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   if (NS_FAILED(rv)) {
     return rv;
   }
   return Read(aBuffer, aCount, nullptr);
 }
 
+bool
+SourceBufferResource::EvictData(uint32_t aThreshold)
+{
+  return mInputBuffer.Evict(mOffset, aThreshold);
+}
+
+void
+SourceBufferResource::EvictBefore(uint64_t aOffset)
+{
+  // If aOffset is past the current playback offset we don't evict.
+  if (aOffset < mOffset) {
+    mInputBuffer.Evict(aOffset, 0);
+  }
+}
+
 void
 SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
-  mInputBuffer.AppendElements(aData, aLength);
+  mInputBuffer.PushBack(new ResourceItem(aData, aLength));
   mon.NotifyAll();
 }
 
 void
 SourceBufferResource::Ended()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   mEnded = true;
--- a/content/media/mediasource/SourceBufferResource.h
+++ b/content/media/mediasource/SourceBufferResource.h
@@ -2,41 +2,186 @@
 /* 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/. */
 
 #ifndef MOZILLA_SOURCEBUFFERRESOURCE_H_
 #define MOZILLA_SOURCEBUFFERRESOURCE_H_
 
+#include <algorithm>
 #include "MediaCache.h"
 #include "MediaResource.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsIPrincipal.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
+#include "nsDeque.h"
 #include "nscore.h"
 
 class nsIStreamListener;
 
 namespace mozilla {
 
 class MediaDecoder;
 
 namespace dom {
 
 class SourceBuffer;
 
 }  // namespace dom
 
 class SourceBufferResource MOZ_FINAL : public MediaResource
 {
+private:
+  // A SourceBufferResource has a queue containing the data
+  // that is appended to it. The queue holds instances of
+  // ResourceItem which is an array of the bytes. Appending
+  // data to the SourceBufferResource pushes this onto the
+  // queue. As items are played they are taken off the front
+  // of the queue.
+  // Data is evicted once it reaches a size threshold. This
+  // pops the items off the front of the queue and deletes it.
+  // If an eviction happens then the MediaSource is notified
+  // (done in SourceBuffer::AppendData) which then requests
+  // all SourceBuffers to evict data up to approximately
+  // the same timepoint.
+  struct ResourceItem {
+    ResourceItem(uint8_t const* aData, uint32_t aSize) {
+      mData.AppendElements(aData, aSize);
+    }
+    nsTArray<uint8_t> mData;
+  };
+
+  class ResourceQueueDeallocator : public nsDequeFunctor {
+    virtual void* operator() (void* anObject) {
+      delete static_cast<ResourceItem*>(anObject);
+      return nullptr;
+    }
+  };
+
+  class ResourceQueue : private nsDeque {
+  private:
+    // Logical offset into the resource of the first element
+    // in the queue.
+    uint64_t mOffset;
+
+  public:
+    ResourceQueue() :
+      nsDeque(new ResourceQueueDeallocator()),
+      mOffset(0)
+    {
+    }
+
+    // Clears all items from the queue
+    inline void Clear() {
+      return nsDeque::Erase();
+    }
+
+    // Returns the number of items in the queue
+    inline uint32_t GetSize() {
+      return nsDeque::GetSize();
+    }
+
+    // Returns the logical byte offset of the start of the data.
+    inline uint64_t GetOffset() {
+      return mOffset;
+    }
+
+    inline ResourceItem* ResourceAt(uint32_t aIndex) {
+      return static_cast<ResourceItem*>(nsDeque::ObjectAt(aIndex));
+    }
+
+    // Returns the length of all items in the queue plus the offset.
+    // This is the logical length of the resource.
+    inline uint64_t GetLength() {
+      uint64_t s = mOffset;
+      for (uint32_t i = 0; i < GetSize(); ++i) {
+        ResourceItem* item = ResourceAt(i);
+        s += item->mData.Length();
+      }
+      return s;
+    }
+
+    // Returns the index of the resource that contains the given
+    // logical offset. aResourceOffset will contain the offset into
+    // the resource at the given index returned if it is not null.  If
+    // no such resource exists, returns GetSize() and aOffset is
+    // untouched.
+    inline uint32_t GetAtOffset(uint64_t aOffset, uint32_t *aResourceOffset) {
+      uint64_t offset = mOffset;
+      for (uint32_t i = 0; i < GetSize(); ++i) {
+        ResourceItem* item = ResourceAt(i);
+        // If the item contains the start of the offset we want to
+        // break out of the loop.
+        if (item->mData.Length() + offset > aOffset) {
+          if (aResourceOffset) {
+            *aResourceOffset = aOffset - offset;
+          }
+          return i;
+        }
+        offset += item->mData.Length();
+      }
+      return GetSize();
+    }
+
+    // Copies aCount bytes from aOffset in the queue into aDest.
+    inline void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
+      uint32_t offset = 0;
+      uint32_t start = GetAtOffset(aOffset, &offset);
+      uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, GetSize());
+      for (uint32_t i = start; i < end; ++i) {
+        ResourceItem* item = ResourceAt(i);
+        uint32_t bytes = std::min(aCount, item->mData.Length() - offset);
+        if (bytes != 0) {
+          memcpy(aDest, &item->mData[offset], bytes);
+          offset = 0;
+          aCount -= bytes;
+          aDest += bytes;
+        }
+      }
+    }
+
+    inline void PushBack(ResourceItem* aItem) {
+      nsDeque::Push(aItem);
+    }
+
+    inline void PushFront(ResourceItem* aItem) {
+      nsDeque::PushFront(aItem);
+    }
+
+    inline ResourceItem* PopBack() {
+      return static_cast<ResourceItem*>(nsDeque::Pop());
+    }
+
+    inline ResourceItem* PopFront() {
+      return static_cast<ResourceItem*>(nsDeque::PopFront());
+    }
+
+    // Evict data in queue if the total queue size is greater than
+    // aThreshold past the offset. Returns true if some data was
+    // actually evicted.
+    inline bool Evict(uint64_t aOffset, uint32_t aThreshold) {
+      bool evicted = false;
+      while (GetLength() - mOffset > aThreshold) {
+        ResourceItem* item = ResourceAt(0);
+        if (item->mData.Length() + mOffset > aOffset) {
+          break;
+        }
+        mOffset += item->mData.Length();
+        delete PopFront();
+        evicted = true;
+      }
+      return evicted;
+    }
+  };
+
 public:
   SourceBufferResource(nsIPrincipal* aPrincipal,
                        const nsACString& aType);
   ~SourceBufferResource();
 
   virtual nsresult Close() MOZ_OVERRIDE;
   virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE {}
   virtual void Resume() MOZ_OVERRIDE {}
@@ -57,48 +202,57 @@ public:
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE;
   virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE;
   virtual void StartSeekingForMetadata() MOZ_OVERRIDE { }
   virtual void EndSeekingForMetadata() MOZ_OVERRIDE {}
   virtual int64_t Tell() MOZ_OVERRIDE { return mOffset; }
   virtual void Pin() MOZ_OVERRIDE {}
   virtual void Unpin() MOZ_OVERRIDE {}
   virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
-  virtual int64_t GetLength() MOZ_OVERRIDE { return mInputBuffer.Length(); }
+  virtual int64_t GetLength() MOZ_OVERRIDE { return mInputBuffer.GetLength(); }
   virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { return aOffset; }
   virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { return GetLength(); }
-  virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE { return true; }
+  virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE { return false; }
   virtual bool IsSuspendedByCache() MOZ_OVERRIDE { return false; }
   virtual bool IsSuspended() MOZ_OVERRIDE { return false; }
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) MOZ_OVERRIDE;
   virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
   virtual nsresult Open(nsIStreamListener** aStreamListener) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
 
   virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) MOZ_OVERRIDE
   {
-    aRanges.AppendElement(MediaByteRange(0, GetLength()));
+    aRanges.AppendElement(MediaByteRange(mInputBuffer.GetOffset(),
+                                         mInputBuffer.GetLength()));
     return NS_OK;
   }
 
   virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; }
 
   // Used by SourceBuffer.
   void AppendData(const uint8_t* aData, uint32_t aLength);
   void Ended();
+  // Remove data from resource if it holds more than the threshold
+  // number of bytes. Returns true if some data was evicted.
+  bool EvictData(uint32_t aThreshold);
+
+  // Remove data from resource before the given offset.
+  void EvictBefore(uint64_t aOffset);
 
 private:
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsAutoCString mType;
 
   // Provides synchronization between SourceBuffers and InputAdapters.
   // Protects all of the member variables below.  Read() will await a
   // Notify() (from Seek, AppendData, Ended, or Close) when insufficient
   // data is available in mData.
   ReentrantMonitor mMonitor;
-  nsTArray<uint8_t> mInputBuffer;
 
-  int64_t mOffset;
+  // The buffer holding resource data is a queue of ResourceItem's.
+  ResourceQueue mInputBuffer;
+
+  uint64_t mOffset;
   bool mClosed;
   bool mEnded;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_SOURCEBUFFERRESOURCE_H_ */
--- a/content/media/mediasource/SubBufferDecoder.h
+++ b/content/media/mediasource/SubBufferDecoder.h
@@ -49,16 +49,20 @@ public:
   }
 
   nsresult GetBuffered(dom::TimeRanges* aBuffered)
   {
     // XXX: Need mStartTime (from StateMachine) instead of passing 0.
     return mReader->GetBuffered(aBuffered, 0);
   }
 
+  // Given a time convert it into an approximate byte offset from the
+  // cached data. Returns -1 if no such value is computable.
+  int64_t ConvertToByteOffset(double aTime);
+
 private:
   MediaSourceDecoder* mParentDecoder;
   nsAutoPtr<MediaDecoderReader> mReader;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_SUBBUFFERDECODER_H_ */
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -10,17 +10,17 @@
 #include <cutils/properties.h>
 #include <stagefright/foundation/ADebug.h>
 #include <stagefright/foundation/AMessage.h>
 #include <stagefright/MediaExtractor.h>
 #include <stagefright/MetaData.h>
 #include <stagefright/OMXClient.h>
 #include <stagefright/OMXCodec.h>
 #include <OMX.h>
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 #include <ui/Fence.h>
 #endif
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Types.h"
 #include "mozilla/Monitor.h"
 #include "nsMimeTypes.h"
 #include "MPAPI.h"
@@ -1055,17 +1055,17 @@ void OmxDecoder::ReleaseAllPendingVideoB
     }
     mPendingVideoBuffers.clear();
   }
   // Free all pending video buffers without holding mPendingVideoBuffersLock.
   int size = releasingVideoBuffers.size();
   for (int i = 0; i < size; i++) {
     MediaBuffer *buffer;
     buffer = releasingVideoBuffers[i].mMediaBuffer;
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
     android::sp<Fence> fence;
     int fenceFd = -1;
     fence = releasingVideoBuffers[i].mReleaseFenceHandle.mFence;
     if (fence.get() && fence->isValid()) {
       fenceFd = fence->dup();
     }
     MOZ_ASSERT(buffer->refcount() == 1);
     // This code expect MediaBuffer's ref count is 1.
--- a/content/xml/document/src/XMLDocument.cpp
+++ b/content/xml/document/src/XMLDocument.cpp
@@ -15,17 +15,16 @@
 #include "nsIDocShell.h"
 #include "nsIMarkupDocumentViewer.h"
 #include "nsHTMLParts.h"
 #include "nsIComponentManager.h"
 #include "nsIDOMElement.h"
 #include "nsIBaseWindow.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocumentType.h"
-#include "nsINameSpaceManager.h"
 #include "nsCOMPtr.h"
 #include "nsXPIDLString.h"
 #include "nsIHttpChannel.h"
 #include "nsIURI.h"
 #include "nsIServiceManager.h"
 #include "nsNetUtil.h"
 #include "nsError.h"
 #include "nsIScriptSecurityManager.h"
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -20,17 +20,17 @@
 #include "DocumentType.h"
 #include "nsHTMLParts.h"
 #include "nsCRT.h"
 #include "nsCSSStyleSheet.h"
 #include "mozilla/css/Loader.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsIScriptContext.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIContentViewer.h"
 #include "prtime.h"
 #include "prlog.h"
 #include "prmem.h"
 #include "nsRect.h"
 #include "nsIWebNavigation.h"
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -33,17 +33,17 @@
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsEventStateManager.h"
 #include "nsFocusManager.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsIJSRuntimeService.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFService.h"
 #include "nsIScriptContext.h"
--- a/content/xul/document/src/nsXULContentSink.cpp
+++ b/content/xul/document/src/nsXULContentSink.cpp
@@ -20,22 +20,22 @@
 #include "nsForwardReference.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsIContentSink.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIFormControl.h"
-#include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIServiceManager.h"
 #include "nsIURL.h"
+#include "nsNameSpaceManager.h"
 #include "nsParserBase.h"
 #include "nsViewManager.h"
 #include "nsIXULDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsLayoutCID.h"
 #include "nsNetUtil.h"
 #include "nsRDFCID.h"
 #include "nsXPIDLString.h"
--- a/content/xul/templates/src/nsXULContentUtils.cpp
+++ b/content/xul/templates/src/nsXULContentUtils.cpp
@@ -29,22 +29,22 @@
 
 #include "nsCOMPtr.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIRDFNode.h"
-#include "nsINameSpaceManager.h"
 #include "nsIRDFService.h"
 #include "nsIServiceManager.h"
 #include "nsIURL.h"
 #include "nsXULContentUtils.h"
 #include "nsLayoutCID.h"
+#include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsRDFCID.h"
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsGkAtoms.h"
 #include "prlog.h"
 #include "prtime.h"
 #include "rdf.h"
--- a/content/xul/templates/src/nsXULContentUtils.h
+++ b/content/xul/templates/src/nsXULContentUtils.h
@@ -19,17 +19,16 @@ class nsIContent;
 class nsIDocument;
 class nsIDOMNodeList;
 class nsIRDFNode;
 class nsCString;
 class nsString;
 class nsIRDFResource;
 class nsIRDFLiteral;
 class nsIRDFService;
-class nsINameSpaceManager;
 class nsIDateTimeFormat;
 class nsICollation;
 
 // errors to pass to LogTemplateError
 #define ERROR_TEMPLATE_INVALID_QUERYPROCESSOR                           \
         "querytype attribute doesn't specify a valid query processor"
 #define ERROR_TEMPLATE_INVALID_QUERYSET                                 \
         "unexpected <queryset> element"
--- a/content/xul/templates/src/nsXULSortService.cpp
+++ b/content/xul/templates/src/nsXULSortService.cpp
@@ -19,19 +19,19 @@
 /*
   This file provides the implementation for the sort service manager.
  */
 
 #include "nsCOMPtr.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
-#include "nsINameSpaceManager.h"
 #include "nsIServiceManager.h"
 #include "nsGkAtoms.h"
+#include "nsNameSpaceManager.h"
 #include "nsXULContentUtils.h"
 #include "nsString.h"
 #include "nsQuickSort.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsXULSortService.h"
 #include "nsIDOMXULElement.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsTemplateMatch.h"
--- a/content/xul/templates/src/nsXULTemplateBuilder.cpp
+++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp
@@ -23,33 +23,33 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMXMLDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDocument.h"
 #include "nsBindingManager.h"
 #include "nsIDOMNodeList.h"
-#include "nsINameSpaceManager.h"
 #include "nsIObserverService.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFInferDataSource.h"
 #include "nsIRDFContainerUtils.h"
 #include "nsIXULDocument.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsIXULBuilderListener.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsIRDFService.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIServiceManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIMutableArray.h"
 #include "nsIURL.h"
 #include "nsIXPConnect.h"
 #include "nsContentCID.h"
+#include "nsNameSpaceManager.h"
 #include "nsRDFCID.h"
 #include "nsXULContentUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsXPIDLString.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsGkAtoms.h"
 #include "nsXULElement.h"
--- a/content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp
+++ b/content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp
@@ -8,17 +8,17 @@
 #include "nsIDOMNode.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFObserver.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsIRDFInferDataSource.h"
 #include "nsIRDFService.h"
 #include "nsRDFCID.h"
 #include "nsIServiceManager.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMDocument.h"
 #include "nsAttrName.h"
 #include "rdf.h"
 #include "nsArrayUtils.h"
 #include "nsIURI.h"
 
 #include "nsContentTestNode.h"
--- a/content/xul/templates/src/nsXULTreeBuilder.cpp
+++ b/content/xul/templates/src/nsXULTreeBuilder.cpp
@@ -22,17 +22,17 @@
 #include "nsTemplateRule.h"
 #include "nsTemplateMatch.h"
 #include "nsGkAtoms.h"
 #include "nsXULContentUtils.h"
 #include "nsXULTemplateBuilder.h"
 #include "nsIXULSortService.h"
 #include "nsTArray.h"
 #include "nsUnicharUtils.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsDOMClassInfoID.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsTreeContentView.h"
 
 // For security check
 #include "nsIDocument.h"
 
 /**
new file mode 100644
--- /dev/null
+++ b/dom/base/Console.cpp
@@ -0,0 +1,1399 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/Console.h"
+#include "mozilla/dom/ConsoleBinding.h"
+
+#include "mozilla/dom/Exceptions.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDocument.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsGlobalWindow.h"
+#include "nsJSUtils.h"
+#include "nsPerformance.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "xpcprivate.h"
+
+#include "nsIConsoleAPIStorage.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILoadContext.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIWebNavigation.h"
+
+// The maximum allowed number of concurrent timers per page.
+#define MAX_PAGE_TIMERS 10000
+
+// The maximum allowed number of concurrent counters per page.
+#define MAX_PAGE_COUNTERS 10000
+
+// The maximum stacktrace depth when populating the stacktrace array used for
+// console.trace().
+#define DEFAULT_MAX_STACKTRACE_DEPTH 200
+
+// The console API methods are async and their action is executed later. This
+// delay tells how much later.
+#define CALL_DELAY 15 // milliseconds
+
+// This constant tells how many messages to process in a single timer execution.
+#define MESSAGES_IN_INTERVAL 1500
+
+// This tag is used in the Structured Clone Algorithm to move js values from
+// worker thread to main thread
+#define CONSOLE_TAG JS_SCTAG_USER_MIN
+
+using namespace mozilla::dom::exceptions;
+using namespace mozilla::dom::workers;
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Console API in workers uses the Structured Clone Algorithm to move any value
+ * from the worker thread to the main-thread. Some object cannot be moved and,
+ * in these cases, we convert them to strings.
+ * It's not the best, but at least we are able to show something.
+ */
+
+// This method is called by the Structured Clone Algorithm when some data has
+// to be read.
+static JSObject*
+ConsoleStructuredCloneCallbacksRead(JSContext* aCx,
+                                    JSStructuredCloneReader* /* unused */,
+                                    uint32_t aTag, uint32_t aData,
+                                    void* aClosure)
+{
+  AssertIsOnMainThread();
+
+  if (aTag != CONSOLE_TAG) {
+    return nullptr;
+  }
+
+  nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure);
+  MOZ_ASSERT(strings->Length() <= aData);
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!xpc::StringToJsval(aCx, strings->ElementAt(aData), &value)) {
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> obj(aCx);
+  if (!JS_ValueToObject(aCx, value, &obj)) {
+    return nullptr;
+  }
+
+  return obj;
+}
+
+// This method is called by the Structured Clone Algorithm when some data has
+// to be written.
+static bool
+ConsoleStructuredCloneCallbacksWrite(JSContext* aCx,
+                                     JSStructuredCloneWriter* aWriter,
+                                     JS::Handle<JSObject*> aObj,
+                                     void* aClosure)
+{
+  JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
+  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+  if (!jsString) {
+    return false;
+  }
+
+  nsDependentJSString string;
+  if (!string.init(aCx, jsString)) {
+    return false;
+  }
+
+  nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure);
+
+  if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG, strings->Length())) {
+    return false;
+  }
+
+  strings->AppendElement(string);
+
+  return true;
+}
+
+static void
+ConsoleStructuredCloneCallbacksError(JSContext* /* aCx */,
+                                     uint32_t /* aErrorId */)
+{
+  NS_WARNING("Failed to clone data for the Console API in workers.");
+}
+
+JSStructuredCloneCallbacks gConsoleCallbacks = {
+  ConsoleStructuredCloneCallbacksRead,
+  ConsoleStructuredCloneCallbacksWrite,
+  ConsoleStructuredCloneCallbacksError
+};
+
+class ConsoleCallData
+{
+public:
+  ConsoleCallData()
+    : mMethodName(Console::MethodLog)
+    , mPrivate(false)
+    , mTimeStamp(JS_Now())
+    , mMonotonicTimer(0)
+  {
+  }
+
+  void
+  Initialize(JSContext* aCx, Console::MethodName aName,
+             const nsAString& aString, const Sequence<JS::Value>& aArguments)
+  {
+    mGlobal = JS::CurrentGlobalOrNull(aCx);
+    mMethodName = aName;
+    mMethodString = aString;
+
+    for (uint32_t i = 0; i < aArguments.Length(); ++i) {
+      mArguments.AppendElement(aArguments[i]);
+    }
+  }
+
+  JS::Heap<JSObject*> mGlobal;
+
+  Console::MethodName mMethodName;
+  bool mPrivate;
+  int64_t mTimeStamp;
+  DOMHighResTimeStamp mMonotonicTimer;
+
+  nsString mMethodString;
+  nsTArray<JS::Heap<JS::Value>> mArguments;
+  Sequence<ConsoleStackEntry> mStack;
+};
+
+// This class is used to clear any exception at the end of this method.
+class ClearException
+{
+public:
+  ClearException(JSContext* aCx)
+    : mCx(aCx)
+  {
+  }
+
+  ~ClearException()
+  {
+    JS_ClearPendingException(mCx);
+  }
+
+private:
+  JSContext* mCx;
+};
+
+class ConsoleRunnable : public nsRunnable
+{
+public:
+  ConsoleRunnable()
+    : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+  }
+
+  virtual
+  ~ConsoleRunnable()
+  {
+  }
+
+  bool
+  Dispatch()
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    JSContext* cx = mWorkerPrivate->GetJSContext();
+
+    if (!PreDispatch(cx)) {
+      return false;
+    }
+
+    AutoSyncLoopHolder syncLoop(mWorkerPrivate);
+    mSyncLoopTarget = syncLoop.EventTarget();
+
+    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
+      JS_ReportError(cx,
+                     "Failed to dispatch to main thread for the Console API!");
+      return false;
+    }
+
+    return syncLoop.Run();
+  }
+
+private:
+  NS_IMETHOD Run()
+  {
+    AssertIsOnMainThread();
+
+    RunConsole();
+
+    nsRefPtr<MainThreadStopSyncLoopRunnable> response =
+      new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
+                                         mSyncLoopTarget.forget(),
+                                         true);
+    if (!response->Dispatch(nullptr)) {
+      NS_WARNING("Failed to dispatch response!");
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  virtual bool
+  PreDispatch(JSContext* aCx) = 0;
+
+  virtual void
+  RunConsole() = 0;
+
+  WorkerPrivate* mWorkerPrivate;
+
+private:
+  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
+};
+
+// This runnable appends a CallData object into the Console queue running on
+// the main-thread.
+class ConsoleCallDataRunnable MOZ_FINAL : public ConsoleRunnable
+{
+public:
+  ConsoleCallDataRunnable(const ConsoleCallData& aCallData)
+    : mCallData(aCallData)
+  {
+  }
+
+private:
+  bool
+  PreDispatch(JSContext* aCx) MOZ_OVERRIDE
+  {
+    ClearException ce(aCx);
+    JSAutoCompartment ac(aCx, mCallData.mGlobal);
+
+    JS::Rooted<JSObject*> arguments(aCx,
+      JS_NewArrayObject(aCx, mCallData.mArguments.Length()));
+    if (!arguments) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < mCallData.mArguments.Length(); ++i) {
+      if (!JS_DefineElement(aCx, arguments, i, mCallData.mArguments[i],
+                            nullptr, nullptr, JSPROP_ENUMERATE)) {
+        return false;
+      }
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
+
+    if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mStrings)) {
+      return false;
+    }
+
+    mCallData.mArguments.Clear();
+    mCallData.mGlobal = nullptr;
+    return true;
+  }
+
+  void
+  RunConsole() MOZ_OVERRIDE
+  {
+    // Walk up to our containing page
+    WorkerPrivate* wp = mWorkerPrivate;
+    while (wp->GetParent()) {
+      wp = wp->GetParent();
+    }
+
+    AutoPushJSContext cx(wp->ParentJSContext());
+    ClearException ce(cx);
+
+    nsPIDOMWindow* window = wp->GetWindow();
+    NS_ENSURE_TRUE_VOID(window);
+
+    nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
+    NS_ENSURE_TRUE_VOID(win);
+
+    ErrorResult error;
+    nsRefPtr<Console> console = win->GetConsole(error);
+    if (error.Failed()) {
+      NS_WARNING("Failed to get console from the window.");
+      return;
+    }
+
+    JS::Rooted<JS::Value> argumentsValue(cx);
+    if (!mArguments.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
+      return;
+    }
+
+    MOZ_ASSERT(argumentsValue.isObject());
+    JS::Rooted<JSObject*> argumentsObj(cx, &argumentsValue.toObject());
+    MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
+
+    uint32_t length;
+    if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
+      return;
+    }
+
+    for (uint32_t i = 0; i < length; ++i) {
+      JS::Rooted<JS::Value> value(cx);
+
+      if (!JS_GetElement(cx, argumentsObj, i, &value)) {
+        return;
+      }
+
+      mCallData.mArguments.AppendElement(value);
+    }
+
+    MOZ_ASSERT(mCallData.mArguments.Length() == length);
+
+    mCallData.mGlobal = JS::CurrentGlobalOrNull(cx);
+    console->AppendCallData(mCallData);
+  }
+
+private:
+  ConsoleCallData mCallData;
+
+  JSAutoStructuredCloneBuffer mArguments;
+  nsTArray<nsString> mStrings;
+};
+
+// This runnable calls ProfileMethod() on the console on the main-thread.
+class ConsoleProfileRunnable MOZ_FINAL : public ConsoleRunnable
+{
+public:
+  ConsoleProfileRunnable(const nsAString& aAction,
+                         const Sequence<JS::Value>& aArguments)
+    : mAction(aAction)
+    , mArguments(aArguments)
+  {
+  }
+
+private:
+  bool
+  PreDispatch(JSContext* aCx) MOZ_OVERRIDE
+  {
+    ClearException ce(aCx);
+
+    JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      return false;
+    }
+
+    JSAutoCompartment ac(aCx, global);
+
+    JS::Rooted<JSObject*> arguments(aCx,
+      JS_NewArrayObject(aCx, mArguments.Length()));
+    if (!arguments) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < mArguments.Length(); ++i) {
+      if (!JS_DefineElement(aCx, arguments, i, mArguments[i], nullptr, nullptr,
+                            JSPROP_ENUMERATE)) {
+        return false;
+      }
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
+
+    if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mStrings)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  void
+  RunConsole() MOZ_OVERRIDE
+  {
+    // Walk up to our containing page
+    WorkerPrivate* wp = mWorkerPrivate;
+    while (wp->GetParent()) {
+      wp = wp->GetParent();
+    }
+
+    AutoPushJSContext cx(wp->ParentJSContext());
+    ClearException ce(cx);
+
+    JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+    NS_ENSURE_TRUE_VOID(global);
+    JSAutoCompartment ac(cx, global);
+
+    nsPIDOMWindow* window = wp->GetWindow();
+    NS_ENSURE_TRUE_VOID(window);
+
+    nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
+    NS_ENSURE_TRUE_VOID(win);
+
+    ErrorResult error;
+    nsRefPtr<Console> console = win->GetConsole(error);
+    if (error.Failed()) {
+      NS_WARNING("Failed to get console from the window.");
+      return;
+    }
+
+    JS::Rooted<JS::Value> argumentsValue(cx);
+    if (!mBuffer.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
+      return;
+    }
+
+    MOZ_ASSERT(argumentsValue.isObject());
+    JS::Rooted<JSObject*> argumentsObj(cx, &argumentsValue.toObject());
+    MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
+
+    uint32_t length;
+    if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
+      return;
+    }
+
+    Sequence<JS::Value> arguments;
+
+    for (uint32_t i = 0; i < length; ++i) {
+      JS::Rooted<JS::Value> value(cx);
+
+      if (!JS_GetElement(cx, argumentsObj, i, &value)) {
+        return;
+      }
+
+      arguments.AppendElement(value);
+    }
+
+    console->ProfileMethod(cx, mAction, arguments, error);
+    if (error.Failed()) {
+      NS_WARNING("Failed to call call profile() method to the ConsoleAPI.");
+    }
+  }
+
+private:
+  nsString mAction;
+  Sequence<JS::Value> mArguments;
+
+  JSAutoStructuredCloneBuffer mBuffer;
+  nsTArray<nsString> mStrings;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTimer)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStorage)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+
+  tmp->mQueuedCalls.Clear();
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorage)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+
+  for (uint32_t i = 0; i < tmp->mQueuedCalls.Length(); ++i) {
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mQueuedCalls[i].mGlobal);
+
+    for (uint32_t j = 0; j < tmp->mQueuedCalls[i].mArguments.Length(); ++j) {
+      NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mQueuedCalls[i].mArguments[j]);
+    }
+  }
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
+NS_INTERFACE_MAP_END
+
+Console::Console(nsPIDOMWindow* aWindow)
+  : mWindow(aWindow)
+  , mOuterID(0)
+  , mInnerID(0)
+{
+  if (mWindow) {
+    MOZ_ASSERT(mWindow->IsInnerWindow());
+    mInnerID = mWindow->WindowID();
+
+    nsPIDOMWindow* outerWindow = mWindow->GetOuterWindow();
+    MOZ_ASSERT(outerWindow);
+    mOuterID = outerWindow->WindowID();
+  }
+
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->AddObserver(this, "inner-window-destroyed", false);
+    }
+  }
+
+  SetIsDOMBinding();
+  mozilla::HoldJSObjects(this);
+}
+
+Console::~Console()
+{
+  mozilla::DropJSObjects(this);
+}
+
+NS_IMETHODIMP
+Console::Observe(nsISupports* aSubject, const char* aTopic,
+                 const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (strcmp(aTopic, "inner-window-destroyed")) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+  NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+  uint64_t innerID;
+  nsresult rv = wrapper->GetData(&innerID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (innerID == mInnerID) {
+    nsCOMPtr<nsIObserverService> obs =
+      do_GetService("@mozilla.org/observer-service;1");
+    if (obs) {
+      obs->RemoveObserver(this, "inner-window-destroyed");
+    }
+
+    mQueuedCalls.Clear();
+    mTimerRegistry.Clear();
+
+    if (mTimer) {
+      mTimer->Cancel();
+      mTimer = nullptr;
+    }
+  }
+
+  return NS_OK;
+}
+
+JSObject*
+Console::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return ConsoleBinding::Wrap(aCx, aScope, this);
+}
+
+#define METHOD(name, string)                                          \
+  void                                                                \
+  Console::name(JSContext* aCx, const Sequence<JS::Value>& aData)     \
+  {                                                                   \
+    Method(aCx, Method##name, NS_LITERAL_STRING(string), aData);      \
+  }
+
+METHOD(Log, "log")
+METHOD(Info, "info")
+METHOD(Warn, "warn")
+METHOD(Error, "error")
+METHOD(Exception, "exception")
+METHOD(Debug, "debug")
+
+void
+Console::Trace(JSContext* aCx)
+{
+  const Sequence<JS::Value> data;
+  Method(aCx, MethodTrace, NS_LITERAL_STRING("trace"), data);
+}
+
+// Displays an interactive listing of all the properties of an object.
+METHOD(Dir, "dir");
+
+METHOD(Group, "group")
+METHOD(GroupCollapsed, "groupCollapsed")
+METHOD(GroupEnd, "groupEnd")
+
+void
+Console::Time(JSContext* aCx, const JS::Handle<JS::Value> aTime)
+{
+  Sequence<JS::Value> data;
+  SequenceRooter<JS::Value> rooter(aCx, &data);
+
+  if (!aTime.isUndefined()) {
+    data.AppendElement(aTime);
+  }
+
+  Method(aCx, MethodTime, NS_LITERAL_STRING("time"), data);
+}
+
+void
+Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
+{
+  Sequence<JS::Value> data;
+  SequenceRooter<JS::Value> rooter(aCx, &data);
+
+  if (!aTime.isUndefined()) {
+    data.AppendElement(aTime);
+  }
+
+  Method(aCx, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"), data);
+}
+
+void
+Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
+                 ErrorResult& aRv)
+{
+  ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData, aRv);
+}
+
+void
+Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
+                    ErrorResult& aRv)
+{
+  ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData, aRv);
+}
+
+void
+Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
+                       const Sequence<JS::Value>& aData,
+                       ErrorResult& aRv)
+{
+  if (!NS_IsMainThread()) {
+    // Here we are in a worker thread.
+    nsRefPtr<ConsoleProfileRunnable> runnable =
+      new ConsoleProfileRunnable(aAction, aData);
+    runnable->Dispatch();
+    return;
+  }
+
+  RootedDictionary<ConsoleProfileEvent> event(aCx);
+  event.mAction = aAction;
+
+  event.mArguments.Construct();
+  Sequence<JS::Value>& sequence = event.mArguments.Value();
+
+  for (uint32_t i = 0; i < aData.Length(); ++i) {
+    sequence.AppendElement(aData[i]);
+  }
+
+  JS::Rooted<JS::Value> eventValue(aCx);
+  if (!event.ToObject(aCx, JS::NullPtr(), &eventValue)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
+  MOZ_ASSERT(eventObj);
+
+  if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
+                         nullptr, nullptr, JSPROP_ENUMERATE)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsXPConnect*  xpc = nsXPConnect::XPConnect();
+  nsCOMPtr<nsISupports> wrapper;
+  const nsIID& iid = NS_GET_IID(nsISupports);
+
+  if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (obs) {
+    obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
+  }
+}
+
+void
+Console::Assert(JSContext* aCx, bool aCondition,
+                const Sequence<JS::Value>& aData)
+{
+  if (!aCondition) {
+    Method(aCx, MethodAssert, NS_LITERAL_STRING("assert"), aData);
+  }
+}
+
+METHOD(Count, "count")
+
+void
+Console::__noSuchMethod__()
+{
+  // Nothing to do.
+}
+
+// Queue a call to a console method. See the CALL_DELAY constant.
+void
+Console::Method(JSContext* aCx, MethodName aMethodName,
+                const nsAString& aMethodString,
+                const Sequence<JS::Value>& aData)
+{
+  // This RAII class removes the last element of the mQueuedCalls if something
+  // goes wrong.
+  class RAII {
+  public:
+    RAII(nsTArray<ConsoleCallData>& aArray)
+      : mArray(aArray)
+      , mUnfinished(true)
+    {
+    }
+
+    ~RAII()
+    {
+      if (mUnfinished) {
+        mArray.RemoveElementAt(mArray.Length() - 1);
+      }
+    }
+
+    void
+    Finished()
+    {
+      mUnfinished = false;
+    }
+
+  private:
+    nsTArray<ConsoleCallData>& mArray;
+    bool mUnfinished;
+  };
+
+  ConsoleCallData& callData = *mQueuedCalls.AppendElement();
+  callData.Initialize(aCx, aMethodName, aMethodString, aData);
+  RAII raii(mQueuedCalls);
+
+  if (mWindow) {
+    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
+    if (!webNav) {
+      Throw(aCx, NS_ERROR_FAILURE);
+      return;
+    }
+
+    nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+    MOZ_ASSERT(loadContext);
+
+    loadContext->GetUsePrivateBrowsing(&callData.mPrivate);
+  }
+
+  uint32_t maxDepth = aMethodName == MethodTrace ?
+                       DEFAULT_MAX_STACKTRACE_DEPTH : 1;
+  nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
+
+  if (!stack) {
+    Throw(aCx, NS_ERROR_FAILURE);
+    return;
+  }
+
+  // nsIStackFrame is not thread-safe so we take what we need and we store in
+  // an array of ConsoleStackEntry objects.
+  do {
+    uint32_t language;
+    nsresult rv = stack->GetLanguage(&language);
+    if (NS_FAILED(rv)) {
+      Throw(aCx, rv);
+      return;
+    }
+
+    if (language == nsIProgrammingLanguage::JAVASCRIPT ||
+        language == nsIProgrammingLanguage::JAVASCRIPT2) {
+      ConsoleStackEntry& data = *callData.mStack.AppendElement();
+
+      nsCString string;
+      rv = stack->GetFilename(string);
+      if (NS_FAILED(rv)) {
+        Throw(aCx, rv);
+        return;
+      }
+
+      CopyUTF8toUTF16(string, data.mFilename);
+
+      int32_t lineNumber;
+      rv = stack->GetLineNumber(&lineNumber);
+      if (NS_FAILED(rv)) {
+        Throw(aCx, rv);
+        return;
+      }
+
+      data.mLineNumber = lineNumber;
+
+      rv = stack->GetName(string);
+      if (NS_FAILED(rv)) {
+        Throw(aCx, rv);
+        return;
+      }
+
+      CopyUTF8toUTF16(string, data.mFunctionName);
+
+      data.mLanguage = language;
+    }
+
+    nsCOMPtr<nsIStackFrame> caller;
+    rv = stack->GetCaller(getter_AddRefs(caller));
+    if (NS_FAILED(rv)) {
+      Throw(aCx, rv);
+      return;
+    }
+
+    stack.swap(caller);
+  } while (stack);
+
+  // Monotonic timer for 'time' and 'timeEnd'
+  if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd) && mWindow) {
+    nsGlobalWindow *win = static_cast<nsGlobalWindow*>(mWindow.get());
+    MOZ_ASSERT(win);
+
+    ErrorResult rv;
+    nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
+    if (rv.Failed()) {
+      Throw(aCx, rv.ErrorCode());
+      return;
+    }
+
+    callData.mMonotonicTimer = performance->Now();
+  }
+
+  if (!NS_IsMainThread()) {
+    // Here we are in a worker thread.
+    nsRefPtr<ConsoleCallDataRunnable> runnable =
+      new ConsoleCallDataRunnable(callData);
+    runnable->Dispatch();
+    return;
+  }
+
+  // The operation is completed. RAII class has to be disabled.
+  raii.Finished();
+
+  if (!mTimer) {
+    mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    mTimer->InitWithCallback(this, CALL_DELAY,
+                             nsITimer::TYPE_REPEATING_SLACK);
+  }
+}
+
+void
+Console::AppendCallData(const ConsoleCallData& aCallData)
+{
+  mQueuedCalls.AppendElement(aCallData);
+
+  if (!mTimer) {
+    mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    mTimer->InitWithCallback(this, CALL_DELAY,
+                             nsITimer::TYPE_REPEATING_SLACK);
+  }
+}
+
+// Timer callback used to process each of the queued calls.
+NS_IMETHODIMP
+Console::Notify(nsITimer *timer)
+{
+  MOZ_ASSERT(!mQueuedCalls.IsEmpty());
+
+  uint32_t i = 0;
+  for (; i < MESSAGES_IN_INTERVAL && i < mQueuedCalls.Length();
+       ++i) {
+    ProcessCallData(mQueuedCalls[i]);
+  }
+
+  mQueuedCalls.RemoveElementsAt(0, i);
+
+  if (mQueuedCalls.IsEmpty()) {
+    mTimer->Cancel();
+    mTimer = nullptr;
+  }
+
+  return NS_OK;
+}
+
+void
+Console::ProcessCallData(ConsoleCallData& aData)
+{
+  ConsoleStackEntry frame;
+  if (!aData.mStack.IsEmpty()) {
+    frame = aData.mStack[0];
+  }
+
+  AutoSafeJSContext cx;
+  ClearException ce(cx);
+  RootedDictionary<ConsoleEvent> event(cx);
+
+  JSAutoCompartment ac(cx, aData.mGlobal);
+
+  event.mID.Construct();
+  event.mInnerID.Construct();
+  if (mWindow) {
+    event.mID.Value().SetAsUnsignedLong() = mOuterID;
+    event.mInnerID.Value().SetAsUnsignedLong() = mInnerID;
+  } else {
+    // If we are in a JSM, the window doesn't exist.
+    event.mID.Value().SetAsString() = NS_LITERAL_STRING("jsm");
+    event.mInnerID.Value().SetAsString() = frame.mFilename;
+  }
+
+  event.mLevel = aData.mMethodString;
+  event.mFilename = frame.mFilename;
+  event.mLineNumber = frame.mLineNumber;
+  event.mFunctionName = frame.mFunctionName;
+  event.mTimeStamp = aData.mTimeStamp;
+  event.mPrivate = aData.mPrivate;
+
+  switch (aData.mMethodName) {
+    case MethodLog:
+    case MethodInfo:
+    case MethodWarn:
+    case MethodError:
+    case MethodException:
+    case MethodDebug:
+    case MethodAssert:
+      event.mArguments.Construct();
+      ProcessArguments(cx, aData.mArguments, event.mArguments.Value());
+      break;
+
+    default:
+      event.mArguments.Construct();
+      ArgumentsToValueList(aData.mArguments, event.mArguments.Value());
+  }
+
+  if (aData.mMethodName == MethodTrace) {
+    event.mStacktrace.Construct();
+    event.mStacktrace.Value().SwapElements(aData.mStack);
+  }
+
+  else if (aData.mMethodName == MethodGroup ||
+           aData.mMethodName == MethodGroupCollapsed ||
+           aData.mMethodName == MethodGroupEnd) {
+    ComposeGroupName(cx, aData.mArguments, event.mGroupName);
+  }
+
+  else if (aData.mMethodName == MethodTime && !aData.mArguments.IsEmpty()) {
+    event.mTimer = StartTimer(cx, aData.mArguments[0], aData.mMonotonicTimer);
+  }
+
+  else if (aData.mMethodName == MethodTimeEnd && !aData.mArguments.IsEmpty()) {
+    event.mTimer = StopTimer(cx, aData.mArguments[0], aData.mMonotonicTimer);
+  }
+
+  else if (aData.mMethodName == MethodCount) {
+    event.mCounter = IncreaseCounter(cx, frame, aData.mArguments);
+  }
+
+  JS::Rooted<JS::Value> eventValue(cx);
+  if (!event.ToObject(cx, JS::NullPtr(), &eventValue)) {
+    Throw(cx, NS_ERROR_FAILURE);
+    return;
+  }
+
+  JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
+  MOZ_ASSERT(eventObj);
+
+  if (!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventValue,
+                         nullptr, nullptr, JSPROP_ENUMERATE)) {
+    return;
+  }
+
+  if (!mStorage) {
+    mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
+  }
+
+  if (!mStorage) {
+    NS_WARNING("Failed to get the ConsoleAPIStorage service.");
+    return;
+  }
+
+  nsAutoString innerID;
+  innerID.AppendInt(mInnerID);
+
+  if (NS_FAILED(mStorage->RecordEvent(innerID, eventValue))) {
+    NS_WARNING("Failed to record a console event.");
+  }
+
+  nsXPConnect*  xpc = nsXPConnect::XPConnect();
+  nsCOMPtr<nsISupports> wrapper;
+  const nsIID& iid = NS_GET_IID(nsISupports);
+
+  if (NS_FAILED(xpc->WrapJS(cx, eventObj, iid, getter_AddRefs(wrapper)))) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (obs) {
+    nsAutoString outerID;
+    outerID.AppendInt(mOuterID);
+
+    obs->NotifyObservers(wrapper, "console-api-log-event", outerID.get());
+  }
+}
+
+void
+Console::ProcessArguments(JSContext* aCx,
+                          const nsTArray<JS::Heap<JS::Value>>& aData,
+                          Sequence<JS::Value>& aSequence)
+{
+  if (aData.IsEmpty()) {
+    return;
+  }
+
+  if (aData.Length() == 1 || !aData[0].isString()) {
+    ArgumentsToValueList(aData, aSequence);
+    return;
+  }
+
+  SequenceRooter<JS::Value> rooter(aCx, &aSequence);
+
+  JS::Rooted<JS::Value> format(aCx, aData[0]);
+  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, format));
+  if (!jsString) {
+    return;
+  }
+
+  nsDependentJSString string;
+  if (!string.init(aCx, jsString)) {
+    return;
+  }
+
+  nsString::const_iterator start, end;
+  string.BeginReading(start);
+  string.EndReading(end);
+
+  nsString output;
+  uint32_t index = 1;
+
+  while (start != end) {
+    if (*start != '%') {
+      output.Append(*start);
+      ++start;
+      continue;
+    }
+
+    ++start;
+
+    if (*start == '%') {
+      output.Append(*start);
+      ++start;
+      continue;
+    }
+
+    nsAutoString tmp;
+    tmp.Append('%');
+
+    int32_t integer = -1;
+    int32_t mantissa = -1;
+
+    // Let's parse %<number>.<number> for %d and %f
+    if (*start >= '0' && *start <= '9') {
+      integer = 0;
+
+      do {
+        integer = integer * 10 + *start - '0';
+        tmp.Append(*start);
+        ++start;
+      } while (*start >= '0' && *start <= '9');
+    }
+
+    if (*start == '.') {
+      tmp.Append(*start);
+      ++start;
+
+      // '.' must be followed by a number.
+      if (*start < '0' || *start > '9') {
+        output.Append(tmp);
+        continue;
+      }
+
+      mantissa = 0;
+
+      do {
+        mantissa = mantissa * 10 + *start - '0';
+        tmp.Append(*start);
+        ++start;
+      } while (*start >= '0' && *start <= '9');
+    }
+
+    char ch = *start;
+    tmp.Append(ch);
+    ++start;
+
+    switch (ch) {
+      case 'o':
+      {
+        if (!output.IsEmpty()) {
+          JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
+                                                             output.get(),
+                                                             output.Length()));
+          if (!str) {
+            return;
+          }
+
+          aSequence.AppendElement(JS::StringValue(str));
+          output.Truncate();
+        }
+
+        JS::Rooted<JS::Value> v(aCx);
+        if (index < aData.Length()) {
+          v = aData[index++];
+        }
+
+        aSequence.AppendElement(v);
+        break;
+      }
+
+      case 's':
+        if (index < aData.Length()) {
+          JS::Rooted<JS::Value> value(aCx, aData[index++]);
+          JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+          if (!jsString) {
+            return;
+          }
+
+          nsDependentJSString v;
+          if (!v.init(aCx, jsString)) {
+            return;
+          }
+
+          output.Append(v);
+        }
+        break;
+
+      case 'd':
+      case 'i':
+        if (index < aData.Length()) {
+          JS::Rooted<JS::Value> value(aCx, aData[index++]);
+
+          int32_t v;
+          if (!JS::ToInt32(aCx, value, &v)) {
+            return;
+          }
+
+          nsCString format;
+          MakeFormatString(format, integer, mantissa, 'd');
+          output.AppendPrintf(format.get(), v);
+        }
+        break;
+
+      case 'f':
+        if (index < aData.Length()) {
+          JS::Rooted<JS::Value> value(aCx, aData[index++]);
+
+          double v;
+          if (!JS::ToNumber(aCx, value, &v)) {
+            return;
+          }
+
+          nsCString format;
+          MakeFormatString(format, integer, mantissa, 'f');
+          output.AppendPrintf(format.get(), v);
+        }
+        break;
+
+      default:
+        output.Append(tmp);
+        break;
+    }
+  }
+
+  if (!output.IsEmpty()) {
+    JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx, output.get(),
+                                                       output.Length()));
+    if (!str) {
+      return;
+    }
+
+    aSequence.AppendElement(JS::StringValue(str));
+  }
+
+  // The rest of the array, if unused by the format string.
+  for (; index < aData.Length(); ++index) {
+    aSequence.AppendElement(aData[index]);
+  }
+}
+
+void
+Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
+                          int32_t aMantissa, char aCh)
+{
+  aFormat.Append("%");
+  if (aInteger >= 0) {
+    aFormat.AppendInt(aInteger);
+  }
+
+  if (aMantissa >= 0) {
+    aFormat.Append(".");
+    aFormat.AppendInt(aMantissa);
+  }
+
+  aFormat.Append(aCh);
+}
+
+void
+Console::ComposeGroupName(JSContext* aCx,
+                          const nsTArray<JS::Heap<JS::Value>>& aData,
+                          nsAString& aName)
+{
+  for (uint32_t i = 0; i < aData.Length(); ++i) {
+    if (i != 0) {
+      aName.AppendASCII(" ");
+    }
+
+    JS::Rooted<JS::Value> value(aCx, aData[i]);
+    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+    if (!jsString) {
+      return;
+    }
+
+    nsDependentJSString string;
+    if (!string.init(aCx, jsString)) {
+      return;
+    }
+
+    aName.Append(string);
+  }
+}
+
+JS::Value
+Console::StartTimer(JSContext* aCx, const JS::Value& aName,
+                    DOMHighResTimeStamp aTimestamp)
+{
+  if (mTimerRegistry.Count() >= MAX_PAGE_TIMERS) {
+    RootedDictionary<ConsoleTimerError> error(aCx);
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!error.ToObject(aCx, JS::NullPtr(), &value)) {
+      return JS::UndefinedValue();
+    }
+
+    return value;
+  }
+
+  RootedDictionary<ConsoleTimerStart> timer(aCx);
+
+  JS::Rooted<JS::Value> name(aCx, aName);
+  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
+  if (!jsString) {
+    return JS::UndefinedValue();
+  }
+
+  nsDependentJSString key;
+  if (!key.init(aCx, jsString)) {
+    return JS::UndefinedValue();
+  }
+
+  timer.mName = key;
+
+  DOMHighResTimeStamp entry;
+  if (!mTimerRegistry.Get(key, &entry)) {
+    mTimerRegistry.Put(key, aTimestamp);
+  } else {
+    aTimestamp = entry;
+  }
+
+  timer.mStarted = aTimestamp;
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!timer.ToObject(aCx, JS::NullPtr(), &value)) {
+    return JS::UndefinedValue();
+  }
+
+  return value;
+}
+
+JS::Value
+Console::StopTimer(JSContext* aCx, const JS::Value& aName,
+                   DOMHighResTimeStamp aTimestamp)
+{
+  JS::Rooted<JS::Value> name(aCx, aName);
+  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
+  if (!jsString) {
+    return JS::UndefinedValue();
+  }
+
+  nsDependentJSString key;
+  if (!key.init(aCx, jsString)) {
+    return JS::UndefinedValue();
+  }
+
+  DOMHighResTimeStamp entry;
+  if (!mTimerRegistry.Get(key, &entry)) {
+    return JS::UndefinedValue();
+  }
+
+  mTimerRegistry.Remove(key);
+
+  RootedDictionary<ConsoleTimerEnd> timer(aCx);
+  timer.mName = key;
+  timer.mDuration = aTimestamp - entry;
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!timer.ToObject(aCx, JS::NullPtr(), &value)) {
+    return JS::UndefinedValue();
+  }
+
+  return value;
+}
+
+void
+Console::ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
+                              Sequence<JS::Value>& aSequence)
+{
+  for (uint32_t i = 0; i < aData.Length(); ++i) {
+    aSequence.AppendElement(aData[i]);
+  }
+}
+
+JS::Value
+Console::IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
+                          const nsTArray<JS::Heap<JS::Value>>& aArguments)
+{
+  ClearException ce(aCx);
+
+  nsAutoString key;
+  nsAutoString label;
+
+  if (!aArguments.IsEmpty()) {
+    JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
+    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
+
+    nsDependentJSString string;
+    if (jsString && string.init(aCx, jsString)) {
+      label = string;
+      key = string;
+    }
+  }
+
+  if (key.IsEmpty()) {
+    key.Append(aFrame.mFilename);
+    key.Append(NS_LITERAL_STRING(":"));
+    key.AppendInt(aFrame.mLineNumber);
+  }
+
+  uint32_t count = 0;
+  if (!mCounterRegistry.Get(key, &count)) {
+    if (mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
+      RootedDictionary<ConsoleCounterError> error(aCx);
+
+      JS::Rooted<JS::Value> value(aCx);
+      if (!error.ToObject(aCx, JS::NullPtr(), &value)) {
+        return JS::UndefinedValue();
+      }
+
+      return value;
+    }
+  }
+
+  ++count;
+  mCounterRegistry.Put(key, count);
+
+  RootedDictionary<ConsoleCounter> data(aCx);
+  data.mLabel = label;
+  data.mCount = count;
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!data.ToObject(aCx, JS::NullPtr(), &value)) {
+    return JS::UndefinedValue();
+  }
+
+  return value;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/Console.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Console_h
+#define mozilla_dom_Console_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/UnionConversions.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsWrapperCache.h"
+
+class nsIConsoleAPIStorage;
+
+namespace mozilla {
+namespace dom {
+
+class ConsoleCallData;
+class ConsoleStackEntry;
+
+class Console MOZ_FINAL : public nsITimerCallback
+                        , public nsIObserver
+                        , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console,
+                                                         nsITimerCallback)
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSIOBSERVER
+
+  Console(nsPIDOMWindow* aWindow);
+  ~Console();
+
+  // WebIDL methods
+  nsISupports* GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  void
+  Log(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Info(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Warn(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Error(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Exception(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Debug(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Trace(JSContext* aCx);
+
+  void
+  Dir(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Group(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  GroupCollapsed(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  GroupEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  Time(JSContext* aCx, const JS::Handle<JS::Value> aTime);
+
+  void
+  TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime);
+
+  void
+  Profile(JSContext* aCx, const Sequence<JS::Value>& aData,
+          ErrorResult& aRv);
+
+  void
+  ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData,
+             ErrorResult& aRv);
+
+  void
+  Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
+
+  void
+  Count(JSContext* aCx, const Sequence<JS::Value>& aData);
+
+  void
+  __noSuchMethod__();
+
+private:
+  enum MethodName
+  {
+    MethodLog,
+    MethodInfo,
+    MethodWarn,
+    MethodError,
+    MethodException,
+    MethodDebug,
+    MethodTrace,
+    MethodDir,
+    MethodGroup,
+    MethodGroupCollapsed,
+    MethodGroupEnd,
+    MethodTime,
+    MethodTimeEnd,
+    MethodAssert,
+    MethodCount
+  };
+
+  void
+  Method(JSContext* aCx, MethodName aName, const nsAString& aString,
+         const Sequence<JS::Value>& aData);
+
+  void
+  AppendCallData(const ConsoleCallData& aData);
+
+  void
+  ProcessCallData(ConsoleCallData& aData);
+
+  // If the first JS::Value of the array is a string, this method uses it to
+  // format a string. The supported sequences are:
+  //   %s    - string
+  //   %d,%i - integer
+  //   %f    - double
+  //   %o    - a JS object.
+  // The output is an array where any object is a separated item, the rest is
+  // unified in a format string.
+  // Example if the input is:
+  //   "string: %s, integer: %d, object: %o, double: %d", 's', 1, window, 0.9
+  // The output will be:
+  //   [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
+  void
+  ProcessArguments(JSContext* aCx, const nsTArray<JS::Heap<JS::Value>>& aData,
+                   Sequence<JS::Value>& aSequence);
+
+  void
+  MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,
+                   char aCh);
+
+  // Stringify and Concat all the JS::Value in a single string using ' ' as
+  // separator.
+  void
+  ComposeGroupName(JSContext* aCx, const nsTArray<JS::Heap<JS::Value>>& aData,
+                   nsAString& aName);
+
+  JS::Value
+  StartTimer(JSContext* aCx, const JS::Value& aName,
+             DOMHighResTimeStamp aTimestamp);
+
+  JS::Value
+  StopTimer(JSContext* aCx, const JS::Value& aName,
+            DOMHighResTimeStamp aTimestamp);
+
+  // The method populates a Sequence from an array of JS::Value.
+  void
+  ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
+                       Sequence<JS::Value>& aSequence);
+
+  void
+  ProfileMethod(JSContext* aCx, const nsAString& aAction,
+                const Sequence<JS::Value>& aData,
+                ErrorResult& aRv);
+
+  JS::Value
+  IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
+                   const nsTArray<JS::Heap<JS::Value>>& aArguments);
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsIConsoleAPIStorage> mStorage;
+
+  nsTArray<ConsoleCallData> mQueuedCalls;
+  nsDataHashtable<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry;
+  nsDataHashtable<nsStringHashKey, uint32_t> mCounterRegistry;
+
+  uint64_t mOuterID;
+  uint64_t mInnerID;
+
+  friend class ConsoleCallData;
+  friend class ConsoleCallDataRunnable;
+  friend class ConsoleProfileRunnable;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif /* mozilla_dom_Console_h */
deleted file mode 100644
--- a/dom/base/ConsoleAPI.js
+++ /dev/null
@@ -1,560 +0,0 @@
-/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
-/* vim: set ft=javascript ts=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/. */
-
-let Cu = Components.utils;
-let Ci = Components.interfaces;
-let Cc = Components.classes;
-
-// The maximum allowed number of concurrent timers per page.
-const MAX_PAGE_TIMERS = 10000;
-
-// The maximum allowed number of concurrent counters per page.
-const MAX_PAGE_COUNTERS = 10000;
-
-// The regular expression used to parse %s/%d and other placeholders for
-// variables in strings that need to be interpolated.
-const ARGUMENT_PATTERN = /%\d*\.?\d*([osdif])\b/g;
-
-// The maximum stacktrace depth when populating the stacktrace array used for
-// console.trace().
-const DEFAULT_MAX_STACKTRACE_DEPTH = 200;
-
-// The console API methods are async and their action is executed later. This
-// delay tells how much later.
-const CALL_DELAY = 15; // milliseconds
-
-// This constant tells how many messages to process in a single timer execution.
-const MESSAGES_IN_INTERVAL = 1500;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-/**
- * The window.console API implementation. One instance is lazily created for
- * every inner window, when the window.console object is accessed.
- */
-function ConsoleAPI() {}
-ConsoleAPI.prototype = {
-
-  classID: Components.ID("{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
-
-  _timerInitialized: false,
-  _queuedCalls: null,
-  _window: null,
-  _innerID: null,
-  _outerID: null,
-  _windowDestroyed: false,
-  _timer: null,
-
-  // nsIDOMGlobalPropertyInitializer
-  init: function CA_init(aWindow) {
-    Services.obs.addObserver(this, "inner-window-destroyed", true);
-
-    try {
-      let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-
-      this._outerID = windowUtils.outerWindowID;
-      this._innerID = windowUtils.currentInnerWindowID;
-    }
-    catch (ex) {
-      Cu.reportError(ex);
-    }
-
-    let self = this;
-    let chromeObject = {
-      // window.console API
-      log: function CA_log() {
-        self.queueCall("log", arguments);
-      },
-      info: function CA_info() {
-        self.queueCall("info", arguments);
-      },
-      warn: function CA_warn() {
-        self.queueCall("warn", arguments);
-      },
-      error: function CA_error() {
-        self.queueCall("error", arguments);
-      },
-      exception: function CA_exception() {
-        self.queueCall("exception", arguments);
-      },
-      debug: function CA_debug() {
-        self.queueCall("debug", arguments);
-      },
-      trace: function CA_trace() {
-        self.queueCall("trace", arguments);
-      },
-      // Displays an interactive listing of all the properties of an object.
-      dir: function CA_dir() {
-        self.queueCall("dir", arguments);
-      },
-      group: function CA_group() {
-        self.queueCall("group", arguments);
-      },
-      groupCollapsed: function CA_groupCollapsed() {
-        self.queueCall("groupCollapsed", arguments);
-      },
-      groupEnd: function CA_groupEnd() {
-        self.queueCall("groupEnd", arguments);
-      },
-      time: function CA_time() {
-        self.queueCall("time", arguments);
-      },
-      timeEnd: function CA_timeEnd() {
-        self.queueCall("timeEnd", arguments);
-      },
-      profile: function CA_profile() {
-        // Send a notification picked up by the profiler if installed.
-        // This must happen right away otherwise we will miss samples
-        let consoleEvent = {
-          action: "profile",
-          arguments: arguments
-        };
-        consoleEvent.wrappedJSObject = consoleEvent;
-        Services.obs.notifyObservers(consoleEvent, "console-api-profiler",
-                                     null);  
-      },
-      profileEnd: function CA_profileEnd() {
-        // Send a notification picked up by the profiler if installed.
-        // This must happen right away otherwise we will miss samples
-        let consoleEvent = {
-          action: "profileEnd",
-          arguments: arguments
-        };
-        consoleEvent.wrappedJSObject = consoleEvent;
-        Services.obs.notifyObservers(consoleEvent, "console-api-profiler",
-                                     null);  
-      },
-      assert: function CA_assert() {
-        let args = Array.prototype.slice.call(arguments);
-        if(!args.shift()) {
-          self.queueCall("assert", args);
-        }
-      },
-      count: function CA_count() {
-        self.queueCall("count", arguments);
-      },
-      __exposedProps__: {
-        log: "r",
-        info: "r",
-        warn: "r",
-        error: "r",
-        exception: "r",
-        debug: "r",
-        trace: "r",
-        dir: "r",
-        group: "r",
-        groupCollapsed: "r",
-        groupEnd: "r",
-        time: "r",
-        timeEnd: "r",
-        profile: "r",
-        profileEnd: "r",
-        assert: "r",
-        count: "r"
-      }
-    };
-
-    // We need to return an actual content object here, instead of a wrapped
-    // chrome object. This allows things like console.log.bind() to work.
-    let contentObj = Cu.createObjectIn(aWindow);
-    function genPropDesc(fun) {
-      return { enumerable: true, configurable: true, writable: true,
-               value: chromeObject[fun].bind(chromeObject) };
-    }
-    const properties = {
-      log: genPropDesc('log'),
-      info: genPropDesc('info'),
-      warn: genPropDesc('warn'),
-      error: genPropDesc('error'),
-      exception: genPropDesc('exception'),
-      debug: genPropDesc('debug'),
-      trace: genPropDesc('trace'),
-      dir: genPropDesc('dir'),
-      group: genPropDesc('group'),
-      groupCollapsed: genPropDesc('groupCollapsed'),
-      groupEnd: genPropDesc('groupEnd'),
-      time: genPropDesc('time'),
-      timeEnd: genPropDesc('timeEnd'),
-      profile: genPropDesc('profile'),
-      profileEnd: genPropDesc('profileEnd'),
-      assert: genPropDesc('assert'),
-      count: genPropDesc('count'),
-      __noSuchMethod__: { enumerable: true, configurable: true, writable: true,
-                          value: function() {} },
-      __mozillaConsole__: { value: true }
-    };
-
-    Object.defineProperties(contentObj, properties);
-    Cu.makeObjectPropsNormal(contentObj);
-
-    this._queuedCalls = [];
-    this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this._window = Cu.getWeakReference(aWindow);
-    this.timerRegistry = new Map();
-    this.counterRegistry = new Map();
-
-    return contentObj;
-  },
-
-  observe: function CA_observe(aSubject, aTopic, aData)
-  {
-    if (aTopic == "inner-window-destroyed") {
-      let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-      if (innerWindowID == this._innerID) {
-        Services.obs.removeObserver(this, "inner-window-destroyed");
-        this._windowDestroyed = true;
-        if (!this._timerInitialized) {
-          this.timerRegistry.clear();
-        }
-      }
-    }
-  },
-
-  /**
-   * Queue a call to a console method. See the CALL_DELAY constant.
-   * This method is the entry point for the console.* for workers.
-   *
-   * @param string aMethod
-   *        The console method the code has invoked.
-   * @param object aArguments
-   *        The arguments passed to the console method.
-   * @param array aStack
-   *        The stack of the console method. Used by console.* for workers.
-   */
-  queueCall: function CA_queueCall(aMethod, aArguments, aStack = null)
-  {
-    let window = this._window.get();
-    let metaForCall = {
-      private: PrivateBrowsingUtils.isWindowPrivate(window),
-      timeStamp: Date.now(),
-      stack: (aStack ? aStack : this.getStackTrace(aMethod != "trace" ? 1 : null)),
-    };
-
-    if (aMethod == "time" || aMethod == "timeEnd") {
-      metaForCall.monotonicTimer = window.performance.now();
-    }
-
-    this._queuedCalls.push([aMethod, aArguments, metaForCall]);
-
-    if (!this._timerInitialized) {
-      this._timer.initWithCallback(this._timerCallback.bind(this), CALL_DELAY,
-                                   Ci.nsITimer.TYPE_REPEATING_SLACK);
-      this._timerInitialized = true;
-    }
-  },
-
-  /**
-   * Timer callback used to process each of the queued calls.
-   * @private
-   */
-  _timerCallback: function CA__timerCallback()
-  {
-    this._queuedCalls.splice(0, MESSAGES_IN_INTERVAL)
-      .forEach(this._processQueuedCall, this);
-
-    if (!this._queuedCalls.length) {
-      this._timerInitialized = false;
-      this._timer.cancel();
-
-      if (this._windowDestroyed) {
-        ConsoleAPIStorage.clearEvents(this._innerID);
-        this.timerRegistry.clear();
-      }
-    }
-  },
-
-  /**
-   * Process a queued call to a console method.
-   *
-   * @private
-   * @param array aCall
-   *        Array that holds information about the queued call.
-   */
-  _processQueuedCall: function CA__processQueuedItem(aCall)
-  {
-    let [method, args, meta] = aCall;
-
-    let frame;
-    if (meta.stack.length) {
-      frame = meta.stack[0];
-    } else {
-      frame = {
-        filename: "",
-        lineNumber: 0,
-        functionName: "",
-      };
-    }
-
-    let consoleEvent = {
-      ID: this._outerID,
-      innerID: this._innerID,
-      level: method,
-      filename: frame.filename,
-      lineNumber: frame.lineNumber,
-      functionName: frame.functionName,
-      timeStamp: meta.timeStamp,
-      arguments: args,
-      private: meta.private,
-    };
-
-    switch (method) {
-      case "log":
-      case "info":
-      case "warn":
-      case "error":
-      case "exception":
-      case "debug":
-      case "assert":
-        consoleEvent.arguments = this.processArguments(args);
-        break;
-      case "trace":
-        consoleEvent.stacktrace = meta.stack;
-        break;
-      case "group":
-      case "groupCollapsed":
-      case "groupEnd":
-        try {
-          consoleEvent.groupName = Array.prototype.join.call(args, " ");
-        }
-        catch (ex) {
-          Cu.reportError(ex);
-          Cu.reportError(ex.stack);
-          return;
-        }
-        break;
-      case "dir":
-        break;
-      case "time":
-        consoleEvent.timer = this.startTimer(args[0], meta.monotonicTimer);
-        break;
-      case "timeEnd":
-        consoleEvent.timer = this.stopTimer(args[0], meta.monotonicTimer);
-        break;
-      case "count":
-        consoleEvent.counter = this.increaseCounter(frame, args[0]);
-        break;
-      default:
-        // unknown console API method!
-        return;
-    }
-
-    this.notifyObservers(method, consoleEvent);
-  },
-
-  /**
-   * Notify all observers of any console API call.
-   *
-   * @param string aLevel
-   *        The message level.
-   * @param object aConsoleEvent
-   *        The console event object to send to observers for the given console
-   *        API call.
-   */
-  notifyObservers: function CA_notifyObservers(aLevel, aConsoleEvent)
-  {
-    aConsoleEvent.wrappedJSObject = aConsoleEvent;
-    ConsoleAPIStorage.recordEvent(this._innerID, aConsoleEvent);
-    Services.obs.notifyObservers(aConsoleEvent, "console-api-log-event",
-                                 this._outerID);
-  },
-
-  /**
-   * Process the console API call arguments in order to perform printf-like
-   * string substitution.
-   *
-   * TODO: object substitution should take into account width and precision
-   * qualifiers (bug 685813).
-   *
-   * @param mixed aArguments
-   *        The arguments given to the console API call.
-   **/
-  processArguments: function CA_processArguments(aArguments) {
-    if (aArguments.length < 2 || typeof aArguments[0] != "string") {
-      return aArguments;
-    }
-
-    let args = Array.prototype.slice.call(aArguments);
-    let format = args.shift();
-    let splitter = "%" + format.length + Date.now() + "%";
-    let objects = [];
-
-    // Format specification regular expression.
-    let processed = format.replace(ARGUMENT_PATTERN, function CA_PA_substitute(match, submatch) {
-      switch (submatch) {
-        case "o":
-          objects.push(args.shift());
-          return splitter;
-        case "s":
-          return String(args.shift());
-        case "d":
-        case "i":
-          return parseInt(args.shift());
-        case "f":
-          return parseFloat(args.shift());
-        default:
-          return submatch;
-      };
-    });
-
-    let result = [];
-    let processedArray = processed.split(splitter);
-    processedArray.forEach(function(aValue, aIndex) {
-      if (aValue !== "") {
-        result.push(aValue);
-      }
-      if (objects[aIndex]) {
-        result.push(objects[aIndex]);
-      }
-    });
-
-    return result.concat(args);
-  },
-
-  /**
-   * Build the stacktrace array for the console.trace() call.
-   *
-   * @param number [aMaxDepth=DEFAULT_MAX_STACKTRACE_DEPTH]
-   *        Optional maximum stacktrace depth.
-   * @return array
-   *         Each element is a stack frame that holds the following properties:
-   *         filename, lineNumber, functionName and language.
-   */
-  getStackTrace: function CA_getStackTrace(aMaxDepth) {
-    if (!aMaxDepth) {
-      aMaxDepth = DEFAULT_MAX_STACKTRACE_DEPTH;
-    }
-
-    let stack = [];
-    let frame = Components.stack.caller.caller;
-    while (frame = frame.caller) {
-      if (frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT ||
-          frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT2) {
-        stack.push({
-          filename: frame.filename,
-          lineNumber: frame.lineNumber,
-          functionName: frame.name,
-          language: frame.language,
-        });
-        if (stack.length == aMaxDepth) {
-          break;
-        }
-      }
-    }
-
-    return stack;
-  },
-
-  /*
-   * A registry of started timers.
-   * @type Map
-   */
-  timerRegistry: null,
-
-  /**
-   * Create a new timer by recording the current time under the specified name.
-   *
-   * @param string aName
-   *        The name of the timer.
-   * @param number aTimestamp
-   *        A monotonic strictly-increasing timing value that tells when the
-   *        timer was started.
-   * @return object
-   *        The name property holds the timer name and the started property
-   *        holds the time the timer was started. In case of error, it returns
-   *        an object with the single property "error" that contains the key
-   *        for retrieving the localized error message.
-   **/
-  startTimer: function CA_startTimer(aName, aTimestamp) {
-    if (!aName) {
-      return;
-    }
-    if (this.timerRegistry.size > MAX_PAGE_TIMERS - 1) {
-      return { error: "maxTimersExceeded" };
-    }
-    let key = aName.toString();
-    if (!this.timerRegistry.has(key)) {
-      this.timerRegistry.set(key, aTimestamp);
-    }
-    return { name: aName, started: this.timerRegistry.get(key) };
-  },
-
-  /**
-   * Stop the timer with the specified name and retrieve the elapsed time.
-   *
-   * @param string aName
-   *        The name of the timer.
-   * @param number aTimestamp
-   *        A monotonic strictly-increasing timing value that tells when the
-   *        timer was stopped.
-   * @return object
-   *        The name property holds the timer name and the duration property
-   *        holds the number of milliseconds since the timer was started.
-   **/
-  stopTimer: function CA_stopTimer(aName, aTimestamp) {
-    if (!aName) {
-      return;
-    }
-    let key = aName.toString();
-    if (!this.timerRegistry.has(key)) {
-      return;
-    }
-    let duration = aTimestamp - this.timerRegistry.get(key);
-    this.timerRegistry.delete(key);
-    return { name: aName, duration: duration };
-  },
-
-  /*
-   * A registry of counsole.count() counters.
-   * @type Map
-   */
-  counterRegistry: null,
-
-  /**
-   * Increases the given counter by one or creates a new counter if the label
-   * is not known so far.
-   *
-   * @param object aFrame
-   *        The current stack frame to extract the filename and linenumber
-   *        from the console.count() invocation.
-   * @param string aLabel
-   *        The label of the counter. If no label is provided, the script url
-   *        and line number is used for associating the counters
-   * @return object
-   *        The label property holds the counters label and the count property
-   *        holds the current count.
-   **/
-  increaseCounter: function CA_increaseCounter(aFrame, aLabel) {
-    let key = null, label = null;
-    try {
-      label = key = aLabel ? aLabel + "" : "";
-    } catch (ex) { }
-    if (!key) {
-      key = aFrame.filename + ":" + aFrame.lineNumber;
-    }
-    let counter = null;
-    if (!this.counterRegistry.has(key)) {
-      if (this.counterRegistry.size > MAX_PAGE_COUNTERS - 1) {
-        return { error: "maxCountersExceeded" };
-      }
-      counter = { label: label, count: 1 };
-      this.counterRegistry.set(key, counter);
-    } else {
-      counter = this.counterRegistry.get(key);
-      counter.count += 1;
-    }
-    return { label: counter.label, count: counter.count };
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ConsoleAPI]);
--- a/dom/base/ConsoleAPI.manifest
+++ b/dom/base/ConsoleAPI.manifest
@@ -1,3 +1,2 @@
-component {b49c18f8-3379-4fc0-8c90-d7772c1a9ff3} ConsoleAPI.js
-contract @mozilla.org/console-api;1 {b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}
-category JavaScript-global-property console @mozilla.org/console-api;1
+component {96cf7855-dfa9-4c6d-8276-f9705b4890f2} ConsoleAPIStorage.js
+contract @mozilla.org/consoleAPI-storage;1 {96cf7855-dfa9-4c6d-8276-f9705b4890f2}
rename from dom/base/ConsoleAPIStorage.jsm
rename to dom/base/ConsoleAPIStorage.js
--- a/dom/base/ConsoleAPIStorage.jsm
+++ b/dom/base/ConsoleAPIStorage.js
@@ -8,19 +8,19 @@ let Cu = Components.utils;
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const STORAGE_MAX_EVENTS = 200;
 
-this.EXPORTED_SYMBOLS = ["ConsoleAPIStorage"];
+var _consoleStorage = new Map();
 
-var _consoleStorage = new Map();
+const CONSOLEAPISTORAGE_CID = Components.ID('{96cf7855-dfa9-4c6d-8276-f9705b4890f2}');
 
 /**
  * The ConsoleAPIStorage is meant to cache window.console API calls for later
  * reuse by other components when needed. For example, the Web Console code can
  * display the cached messages when it opens for the active tab.
  *
  * ConsoleAPI messages are stored as they come from the ConsoleAPI code, with
  * all their properties. They are kept around until the inner window object that
@@ -33,30 +33,41 @@ var _consoleStorage = new Map();
  *    // Get the cached events array for the window you want (use the inner
  *    // window ID).
  *    let events = ConsoleAPIStorage.getEvents(innerWindowID);
  *    events.forEach(function(event) { ... });
  *
  *    // Clear the events for the given inner window ID.
  *    ConsoleAPIStorage.clearEvents(innerWindowID);
  */
-this.ConsoleAPIStorage = {
+function ConsoleAPIStorageService() {
+  this.init();
+}
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+ConsoleAPIStorageService.prototype = {
+  classID : CONSOLEAPISTORAGE_CID,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleAPIStorage,
+                                         Ci.nsIObserver]),
+  classInfo: XPCOMUtils.generateCI({
+    classID: CONSOLEAPISTORAGE_CID,
+    contractID: '@mozilla.org/consoleAPI-storage;1',
+    interfaces: [Ci.nsIConsoleAPIStorage, Ci.nsIObserver],
+    flags: Ci.nsIClassInfo.SINGLETON
+  }),
 
   observe: function CS_observe(aSubject, aTopic, aData)
   {
     if (aTopic == "xpcom-shutdown") {
       Services.obs.removeObserver(this, "xpcom-shutdown");
       Services.obs.removeObserver(this, "inner-window-destroyed");
       Services.obs.removeObserver(this, "memory-pressure");
     }
     else if (aTopic == "inner-window-destroyed") {
       let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-      this.clearEvents(innerWindowID);
+      this.clearEvents(innerWindowID + "");
     }
     else if (aTopic == "memory-pressure") {
       this.clearEvents();
     }
   },
 
   /** @private */
   init: function CS_init()
@@ -135,9 +146,9 @@ this.ConsoleAPIStorage = {
     }
     else {
       _consoleStorage.clear();
       Services.obs.notifyObservers(null, "console-storage-reset", null);
     }
   },
 };
 
-ConsoleAPIStorage.init();
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ConsoleAPIStorageService]);
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
+    'nsIConsoleAPIStorage.idl',
     'nsIDOMDOMCursor.idl',
     'nsIDOMDOMRequest.idl',
     'nsIEntropyCollector.idl',
     'nsIScriptChannel.idl',
     'nsISiteSpecificUserAgent.idl',
 ]
 
 XPIDL_MODULE = 'dom'
@@ -44,16 +45,17 @@ EXPORTS += [
     'nsStructuredCloneContainer.h',
     'nsWindowMemoryReporter.h',
     'nsWrapperCache.h',
     'nsWrapperCacheInlines.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BarProps.h',
+    'Console.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMRequest.h',
     'MessageChannel.h',
     'MessagePort.h',
     'MessagePortList.h',
     'Navigator.h',
@@ -62,16 +64,17 @@ EXPORTS.mozilla.dom += [
     'StructuredCloneTags.h',
     'URL.h',
     'URLSearchParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'BarProps.cpp',
     'CompositionStringSynthesizer.cpp',
+    'Console.cpp',
     'Crypto.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMRequest.cpp',
     'MessageChannel.cpp',
     'MessagePortList.cpp',
     'Navigator.cpp',
@@ -112,24 +115,23 @@ SOURCES += [
     'nsGlobalWindow.cpp',
     # This file forces NSPR logging.
     'nsJSEnvironment.cpp',
     # nsPluginArray.cpp includes npapi.h indirectly, and that includes a lot of system headers
     'nsPluginArray.cpp',
 ]
 
 EXTRA_COMPONENTS += [
-    'ConsoleAPI.js',
     'ConsoleAPI.manifest',
+    'ConsoleAPIStorage.js',
     'SiteSpecificUserAgent.js',
     'SiteSpecificUserAgent.manifest',
 ]
 
 EXTRA_JS_MODULES += [
-    'ConsoleAPIStorage.jsm',
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
     'ObjectWrapper.jsm',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
@@ -138,16 +140,17 @@ LOCAL_INCLUDES += [
     '../battery',
     '../bluetooth',
     '../events',
     '../media',
     '../network/src',
     '../src/geolocation',
     '../src/storage',
     '../time',
+    '../workers',
     '../xbl',
     '/content/base/src',
     '/content/html/document/src',
     '/content/xul/document/src',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
     '/widget/shared',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -202,16 +202,17 @@
 #include "nsHTMLDocument.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
 #include "TimeChangeObserver.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
+#include "mozilla/dom/Console.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
@@ -1434,16 +1435,18 @@ nsGlobalWindow::CleanUp()
   mScrollbars = nullptr;
   mLocation = nullptr;
   mHistory = nullptr;
   mFrames = nullptr;
   mWindowUtils = nullptr;
   mApplicationCache = nullptr;
   mIndexedDB = nullptr;
 
+  mConsole = nullptr;
+
   mPerformance = nullptr;
 
 #ifdef MOZ_WEBSPEECH
   mSpeechSynthesis = nullptr;
 #endif
 
   ClearControllers();
 
@@ -1748,16 +1751,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
@@ -1805,16 +1809,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindow::RiskyUnlink()
 {
   NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
 }
@@ -13355,16 +13360,69 @@ nsGlobalWindow::SetHasAudioAvailableEven
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mDoc) {
     mDoc->NotifyAudioAvailableListener();
   }
 }
 
+NS_IMETHODIMP
+nsGlobalWindow::GetConsole(JSContext* aCx,
+                           JS::MutableHandle<JS::Value> aConsole)
+{
+  ErrorResult rv;
+  nsRefPtr<Console> console = GetConsole(rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+
+  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
+  if (!mJSObject) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (!JS_WrapObject(aCx, &thisObj) ||
+      !WrapNewBindingObject(aCx, thisObj, console, aConsole)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
+{
+  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
+  if (!mJSObject) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (!JS_WrapObject(aCx, &thisObj) ||
+      !JS_DefineProperty(aCx, thisObj, "console", aValue,
+                         JS_PropertyStub, JS_StrictPropertyStub,
+                         JSPROP_ENUMERATE)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+Console*
+nsGlobalWindow::GetConsole(ErrorResult& aRv)
+{
+  FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr);
+
+  if (!mConsole) {
+    mConsole = new Console(this);
+  }
+
+  return mConsole;
+}
+
 #ifdef MOZ_B2G
 void
 nsGlobalWindow::EnableNetworkEvent(uint32_t aType)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -101,16 +101,17 @@ struct nsIntSize;
 struct nsRect;
 
 class nsWindowSizes;
 
 namespace mozilla {
 class Selection;
 namespace dom {
 class BarProp;
+class Console;
 class Function;
 class Gamepad;
 class MediaQueryList;
 class Navigator;
 class SpeechSynthesis;
 class WakeLock;
 namespace indexedDB {
 class IDBFactory;
@@ -815,16 +816,18 @@ public:
   mozilla::dom::Element* GetFrameElement(mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> Open(const nsAString& aUrl,
                                       const nsAString& aName,
                                       const nsAString& aOptions,
                                       mozilla::ErrorResult& aError);
   mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
   nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
 
+  mozilla::dom::Console* GetConsole(mozilla::ErrorResult& aRv);
+
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
                       mozilla::ErrorResult& aError);
 
 public:
   void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
@@ -1449,16 +1452,17 @@ protected:
   nsRefPtr<mozilla::dom::BarProp> mPersonalbar;
   nsRefPtr<mozilla::dom::BarProp> mStatusbar;
   nsRefPtr<mozilla::dom::BarProp> mScrollbars;
   nsRefPtr<nsDOMWindowUtils>    mWindowUtils;
   nsString                      mStatus;
   nsString                      mDefaultStatus;
   nsGlobalWindowObserver*       mObserver; // Inner windows only.
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
+  nsRefPtr<mozilla::dom::Console> mConsole;
 
   nsCOMPtr<nsIDOMStorage>      mLocalStorage;
   nsCOMPtr<nsIDOMStorage>      mSessionStorage;
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
 
   // These member variable are used only on inner windows.
   nsRefPtr<nsEventListenerManager> mListenerManager;
new file mode 100644
--- /dev/null
+++ b/dom/base/nsIConsoleAPIStorage.idl
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(6701600a-17ca-417e-98f9-4ceb175dd15d)]
+interface nsIConsoleAPIStorage : nsISupports
+{
+  /**
+   * Get the events array by inner window ID or all events from all windows.
+   *
+   * @param string [aId]
+   *        Optional, the inner window ID for which you want to get the array of
+   *        cached events.
+   * @returns array
+   *          The array of cached events for the given window. If no |aId| is
+   *          given this function returns all of the cached events, from any
+   *          window.
+   */
+  jsval getEvents([optional] in DOMString aId);
+
+  /**
+   * Record an event associated with the given window ID.
+   *
+   * @param string aId
+   *        The ID of the inner window for which the event occurred or "jsm" for
+   *        messages logged from JavaScript modules..
+   * @param object aEvent
+   *        A JavaScript object you want to store.
+   */
+  void recordEvent(in DOMString aId, in jsval aEvent);
+
+  /**
+   * Clear storage data for the given window.
+   *
+   * @param string [aId]
+   *        Optional, the inner window ID for which you want to clear the
+   *        messages. If this is not specified all of the cached messages are
+   *        cleared, from all window objects.
+   */
+  void clearEvents([optional] in DOMString aId);
+};
--- a/dom/base/test/chrome.ini
+++ b/dom/base/test/chrome.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 support-files =
   file_url.jsm
 
 [test_bug715041.xul]
 [test_bug715041_removal.xul]
 [test_domrequesthelper.xul]
 [test_url.xul]
+[test_console.xul]
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -5,16 +5,17 @@ support-files =
   iframe_messageChannel_post.html
   file_empty.html
   iframe_postMessage_solidus.html
 
 [test_Image_constructor.html]
 [test_appname_override.html]
 [test_bug913761.html]
 [test_clearTimeoutIntervalNoArg.html]
+[test_consoleEmptyStack.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_document.all_unqualified.html]
 [test_domcursor.html]
 [test_domrequest.html]
 [test_e4x_for_each.html]
 [test_error.html]
 [test_gsp-qualified.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_console.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test for URL API"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <iframe id="iframe" />
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+  ok("console" in window, "Console exists");
+  window.console.log(42);
+  window.console = 42;
+  is(window.console, 42, "Console is replacable");
+
+  var frame = document.getElementById("iframe");
+  ok(frame, "Frame must exist");
+  frame.src="http://mochi.test:8888/tests/dom/base/test/file_empty.html";
+  frame.onload = function() {
+    ok("console" in frame.contentWindow, "Console exists in the iframe");
+    frame.contentWindow.console.log(42);
+    frame.contentWindow.console = 42;
+    is(frame.contentWindow.console, 42, "Console is replacable in the iframe");
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  ]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_consoleEmptyStack.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Test for empty stack in console</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+window.setTimeout(console.log.bind(console), 0, "xyz");
+
+window.addEventListener("fake", console.log.bind(console, "xyz"));
+
+window.addEventListener("fake", function() {
+  ok(true, "Still alive");
+  SimpleTest.finish();
+});
+
+window.dispatchEvent(new Event("fake"));
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -240,16 +240,20 @@ DOMInterfaces = {
 'CommandEvent': {
     'nativeType': 'mozilla::dom::CommandEvent',
 },
 
 'CompositionEvent': {
     'nativeType': 'mozilla::dom::CompositionEvent',
 },
 
+'Console': {
+    'implicitJSContext': [ 'trace', 'time', 'timeEnd' ],
+},
+
 'ConvolverNode': {
     'implicitJSContext': [ 'buffer' ],
     'resultNotAddRefed': [ 'buffer' ],
 },
 
 'Coordinates': {
     'headerFile': 'nsGeoPosition.h'
 },
@@ -1510,22 +1514,17 @@ DOMInterfaces = {
 
 'WorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
     'concrete': False,
     'implicitJSContext': [
         'close', 'importScripts',
     ],
-},
-
-'WorkerConsole': {
-    'headerFile': 'mozilla/dom/workers/bindings/Console.h',
-    'workers': True,
-    'implicitJSContext': [ 'trace', 'time', 'timeEnd' ],
+    'binaryNames': { 'console': 'getConsole', },
 },
 
 'WorkerLocation': {
     'headerFile': 'mozilla/dom/workers/bindings/Location.h',
     'workers': True,
 },
 
 'WorkerNavigator': {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -11770,17 +11770,17 @@ struct PrototypeTraits;
                                              config.getCallbacks(),
                                              config)
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'], unions)
 
         curr = CGWrapper(curr, post='\n')
 
-        headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h", "XPCWrapper.h"])
+        headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
         curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
 
         # Add include guards.
         curr = CGIncludeGuard('UnionConversions', curr)
 
         # Done.
         return curr
 
--- a/dom/datastore/DataStore.jsm
+++ b/dom/datastore/DataStore.jsm
@@ -292,19 +292,19 @@ this.DataStore.prototype = {
 
   sendNotification: function(aId, aOperation, aRevisionId) {
     debug("SendNotification");
     if (aOperation == REVISION_VOID) {
       aOperation = "cleared";
     }
 
     cpmm.sendAsyncMessage("DataStore:Changed",
-                          { store: this.name, owner: this.owner,
+                          { store: this.name, owner: this._owner,
                             message: { revisionId: aRevisionId, id: aId,
-                                       operation: aOperation } } );
+                                       operation: aOperation, owner: this._owner } } );
   },
 
   receiveMessage: function(aMessage) {
     debug("receiveMessage");
 
     if (aMessage.name != "DataStore:Changed:Return:OK") {
       debug("Wrong message: " + aMessage.name);
       return;
--- a/dom/datastore/tests/file_changes2.html
+++ b/dom/datastore/tests/file_changes2.html
@@ -25,16 +25,21 @@
   }
 
   function finish() {
     alert('DONE');
   }
 
   function eventListener(obj) {
     ok(obj, "OnChangeListener is called with data");
+    ok("revisionId" in obj, "the event contains a revisionId");
+    ok("id" in obj, "the event contains a id");
+    ok("operation" in obj, "the event contains a operation");
+    ok("owner" in obj, "the event contains a owner");
+    is(obj.owner, 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_changes.html', 'Owner matches');
     finish();
   }
 
   navigator.getDataStores('foo').then(function(stores) {
     is(stores.length, 1, "getDataStores('foo') returns 1 element");
     is(stores[0].name, 'foo', 'The dataStore.name is foo');
 
     stores[0].onchange = eventListener;
--- a/dom/events/nsEventListenerManager.cpp
+++ b/dom/events/nsEventListenerManager.cpp
@@ -15,17 +15,17 @@
 #include "nsISupports.h"
 #include "nsDOMEvent.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMEventListener.h"
 #include "nsGkAtoms.h"
 #include "nsPIDOMWindow.h"
 #include "nsIJSEventListener.h"
 #include "nsIScriptGlobalObject.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIContent.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsIDocument.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "nsIXPConnect.h"
 #include "nsDOMCID.h"
--- a/dom/events/nsEventStateManager.cpp
+++ b/dom/events/nsEventStateManager.cpp
@@ -26,17 +26,17 @@
 #include "nsIPresShell.h"
 #include "nsDOMEvent.h"
 #include "nsGkAtoms.h"
 #include "nsIFormControl.h"
 #include "nsIComboboxControlFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMXULControlElement.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIBaseWindow.h"
 #include "nsISelection.h"
 #include "nsITextControlElement.h"
 #include "nsFrameSelection.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIWebNavigation.h"
 #include "nsIContentViewer.h"
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -19,17 +19,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(97b6784b-ab12-4f79-8422-d7868a4cc7dc)]
+[scriptable, uuid(8c115ab3-cf96-492c-850c-3b18056b45e2)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -499,16 +499,21 @@ interface nsIDOMWindow : nsISupports
   [implicit_jscontext] attribute jsval ondevicemotion;
   [implicit_jscontext] attribute jsval ondeviceorientation;
   [implicit_jscontext] attribute jsval ondeviceproximity;
   [implicit_jscontext] attribute jsval onuserproximity;
   [implicit_jscontext] attribute jsval ondevicelight;
 
   [implicit_jscontext] attribute jsval onmouseenter;
   [implicit_jscontext] attribute jsval onmouseleave;
+
+  /**
+   * Console API
+   */
+  [implicit_jscontext] attribute jsval console;
 };
 
 [scriptable, uuid(2146c906-57f7-486c-a1b4-8cdb57ef577f)]
 interface nsIDOMWindowPerformance : nsISupports
 {
   /**
    * A namespace to hold performance related data and statistics.
    */
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -27,24 +27,23 @@
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
 
-#if defined(MOZ_CONTENT_SANDBOX)
-#if defined(XP_WIN)
+#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
-#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+#endif
+#if defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #endif
-#endif
 
 #include "mozilla/unused.h"
 
 #include "nsIConsoleListener.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
@@ -654,29 +653,28 @@ ContentChild::AllocPBackgroundChild(Tran
 }
 
 bool
 ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
 {
   ChildPrivileges privs = (aPrivs == PRIVILEGES_DEFAULT) ?
                           GeckoChildProcessHost::DefaultChildPrivileges() :
                           aPrivs;
+#if defined(XP_LINUX)
+  // SetCurrentProcessSandbox includes SetCurrentProcessPrivileges.
+  // But we may want to move the sandbox initialization somewhere else
+  // at some point; see bug 880808.
+  SetCurrentProcessSandbox(privs);
+#else
   // If this fails, we die.
   SetCurrentProcessPrivileges(privs);
+#endif
 
-#if defined(MOZ_CONTENT_SANDBOX)
-#if defined(XP_WIN)
+#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
-#else if defined(XP_UNIX) && !defined(XP_MACOSX)
-  // SetCurrentProcessSandbox should be moved close to process initialization
-  // time if/when possible. SetCurrentProcessPrivileges should probably be
-  // moved as well. Right now this is set ONLY if we receive the
-  // RecvSetProcessPrivileges message. See bug 880808.
-  SetCurrentProcessSandbox();
-#endif
 #endif
   return true;
 }
 
 bool
 ContentChild::RecvSpeakerManagerNotify()
 {
 #ifdef MOZ_WIDGET_GONK
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -161,20 +161,20 @@ function testConsoleGroup(aMessageObject
   if (aMessageObject.level == "groupEnd") {
     startTimeTest();
   }
 }
 
 function startTraceTest() {
   gLevel = "trace";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 6, functionName: "window.foobar585956c", language: 2},
-    {filename: TEST_URI, lineNumber: 11, functionName: "foobar585956b", language: 2},
-    {filename: TEST_URI, lineNumber: 15, functionName: "foobar585956a", language: 2},
-    {filename: TEST_URI, lineNumber: 1, functionName: "onclick", language: 2}
+    {filename: TEST_URI, functionName: "window.foobar585956c", language: 2, lineNumber: 6},
+    {filename: TEST_URI, functionName: "foobar585956b", language: 2, lineNumber: 11},
+    {filename: TEST_URI, functionName: "foobar585956a", language: 2, lineNumber: 15},
+    {filename: TEST_URI, functionName: "onclick", language: 2, lineNumber: 1}
   ];
 
   let button = gWindow.document.getElementById("test-trace");
   ok(button, "found #test-trace button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function startLocationTest() {
@@ -185,17 +185,17 @@ function startLocationTest() {
     } catch (ex) {
       // XXX Bug 906593 - Exceptions in this function currently aren't
       // reported, because of some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "log";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 19, functionName: "foobar646025", arguments: ["omg", "o", "d"]}
+    {filename: TEST_URI, functionName: "foobar646025", arguments: ["omg", "o", "d"], lineNumber: 19}
   ];
 
   let button = gWindow.document.getElementById("test-location");
   ok(button, "found #test-location button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function expect(level) {
@@ -208,25 +208,44 @@ function observeConsoleTest() {
   expect("log", "arg");
   win.console.log("arg");
   yield undefined;
 
   expect("info", "arg", "extra arg");
   win.console.info("arg", "extra arg");
   yield undefined;
 
-  // We don't currently support width and precision qualifiers, but we don't
-  // choke on them either.
-  expect("warn", "Lesson 1: PI is approximately equal to 3.14159");
+  expect("warn", "Lesson 1: PI is approximately equal to 3");
+  win.console.warn("Lesson %d: %s is approximately equal to %1.0f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
+  expect("warn", "Lesson 1: PI is approximately equal to 3.14");
   win.console.warn("Lesson %d: %s is approximately equal to %1.2f",
                    1,
                    "PI",
                    3.14159);
   yield undefined;
 
+  expect("warn", "Lesson 1: PI is approximately equal to 3.141590");
+  win.console.warn("Lesson %d: %s is approximately equal to %f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
+  expect("warn", "Lesson 1: PI is approximately equal to 3.1415900");
+  win.console.warn("Lesson %d: %s is approximately equal to %0.7f",
+                   1,
+                   "PI",
+                   3.14159);
+  yield undefined;
+
   expect("log", "%d, %s, %l");
   win.console.log("%d, %s, %l");
   yield undefined;
 
   expect("log", "%a %b %c");
   win.console.log("%a %b %c");
   yield undefined;
 
--- a/dom/tests/browser/browser_ConsoleStorageAPITests.js
+++ b/dom/tests/browser/browser_ConsoleStorageAPITests.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "http://example.com/browser/dom/tests/browser/test-console-api.html";
 const TEST_URI_NAV = "http://example.com/browser/dom/tests/browser/";
 
-let tempScope = {};
-Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", tempScope);
-let ConsoleAPIStorage = tempScope.ConsoleAPIStorage;
+let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+                          .getService(Ci.nsIConsoleAPIStorage);
 
 var apiCallCount;
 
 var ConsoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init: function CO_init()
   {
--- a/dom/tests/browser/browser_ConsoleStoragePBTest_perwindowpb.js
+++ b/dom/tests/browser/browser_ConsoleStoragePBTest_perwindowpb.js
@@ -7,18 +7,18 @@ function test() {
   let windowsToClose = [];
   let innerID;
   let beforeEvents;
   let afterEvents;
   let storageShouldOccur;
   let consoleObserver;
   let testURI =
     "http://example.com/browser/dom/tests/browser/test-console-api.html";
-  let CSS = {};
-  Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", CSS);
+  let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+                            .getService(Ci.nsIConsoleAPIStorage);
 
   function getInnerWindowId(aWindow) {
     return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .currentInnerWindowID;
   }
 
   function whenNewWindowLoaded(aOptions, aCallback) {
@@ -31,17 +31,17 @@ function test() {
 
   function doTest(aIsPrivateMode, aWindow, aCallback) {
     aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
       aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
       consoleObserver = {
         observe: function(aSubject, aTopic, aData) {
           if (aTopic == "console-api-log-event") {
-            afterEvents = CSS.ConsoleAPIStorage.getEvents(innerID);
+            afterEvents = ConsoleAPIStorage.getEvents(innerID);
             is(beforeEvents.length == afterEvents.length - 1, storageShouldOccur,
               "storage should" + (storageShouldOccur ? "" : " not") + " occur");
 
             executeSoon(function() {
               Services.obs.removeObserver(consoleObserver, "console-api-log-event");
               aCallback();
             });
           }
@@ -51,17 +51,17 @@ function test() {
       aWindow.Services.obs.addObserver(
         consoleObserver, "console-api-log-event", false);
       aWindow.console.log("foo bar baz (private: " + aIsPrivateMode + ")");
     }, true);
 
     // We expect that console API messages are always stored.
     storageShouldOccur = true;
     innerID = getInnerWindowId(aWindow);
-    beforeEvents = CSS.ConsoleAPIStorage.getEvents(innerID);
+    beforeEvents = ConsoleAPIStorage.getEvents(innerID);
     aWindow.gBrowser.selectedBrowser.loadURI(testURI);
   }
 
   function testOnWindow(aOptions, aCallback) {
     whenNewWindowLoaded(aOptions, function(aWin) {
       windowsToClose.push(aWin);
       // execute should only be called when need, like when you are opening
       // web pages on the test. If calling executeSoon() is not necesary, then
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -46,17 +46,17 @@
 
       function testGroups() {
         console.groupCollapsed("a", "group");
         console.group("b", "group");
         console.groupEnd("b", "group");
       }
 
       function nativeCallback() {
-        new Promise(function(resolve, reject) { resolve(42); }).then(console.log);
+        new Promise(function(resolve, reject) { resolve(42); }).then(console.log.bind(console));
       }
     </script>
   </head>
   <body>
     <h1>Console API Test Page</h1>
     <button onclick="test();">Log stuff</button>
     <button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
     <button id="test-location" onclick="foobar646025('omg');">Test location</button>
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Console.webidl
@@ -0,0 +1,87 @@
+/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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/. */
+
+[ChromeOnly]
+interface Console {
+  void log(any... data);
+  void info(any... data);
+  void warn(any... data);
+  void error(any... data);
+  void _exception(any... data);
+  void debug(any... data);
+  void trace();
+  void dir(any... data);
+  void group(any... data);
+  void groupCollapsed(any... data);
+  void groupEnd(any... data);
+  void time(any time);
+  void timeEnd(any time);
+
+  [Throws]
+  void profile(any... data);
+
+  [Throws]
+  void profileEnd(any... data);
+
+  void assert(boolean condition, any... data);
+  void count(any... data);
+
+  void ___noSuchMethod__();
+};
+
+// This is used to propagate console events to the observers.
+dictionary ConsoleEvent {
+  (unsigned long or DOMString) ID;
+  (unsigned long or DOMString) innerID;
+  DOMString level = "";
+  DOMString filename = "";
+  unsigned long lineNumber = 0;
+  DOMString functionName = "";
+  double timeStamp = 0;
+  sequence<any> arguments;
+  boolean private = false;
+  sequence<ConsoleStackEntry> stacktrace;
+  DOMString groupName = "";
+  any timer = null;
+  any counter = null;
+};
+
+// Event for profile operations
+dictionary ConsoleProfileEvent {
+  DOMString action = "";
+  sequence<any> arguments;
+};
+
+// This dictionary is used to manage stack trace data.
+dictionary ConsoleStackEntry {
+  DOMString filename = "";
+  unsigned long lineNumber = 0;
+  DOMString functionName = "";
+  unsigned long language = 0;
+};
+
+dictionary ConsoleTimerStart {
+  DOMString name = "";
+  double started = 0;
+};
+
+dictionary ConsoleTimerEnd {
+  DOMString name = "";
+  double duration = 0;
+};
+
+dictionary ConsoleTimerError {
+  DOMString error = "maxTimersExceeded";
+};
+
+dictionary ConsoleCounter {
+  DOMString label = "";
+  unsigned long count = 0;
+};
+
+dictionary ConsoleCounterError {
+  DOMString error = "maxCountersExceeded";
+};
--- a/dom/webidl/DataStoreChangeEvent.webidl
+++ b/dom/webidl/DataStoreChangeEvent.webidl
@@ -3,17 +3,19 @@
  * 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/.
  */
 
 dictionary DataStoreChangeEventInit : EventInit {
   DOMString revisionId = "";
   DataStoreKey id = 0;
   DOMString operation = "";
+  DOMString owner = "";
 };
 
 [Func="Navigator::HasDataStoreSupport",
  Constructor(DOMString type, optional DataStoreChangeEventInit eventInitDict)]
 interface DataStoreChangeEvent : Event {
   readonly attribute DOMString revisionId;
   readonly attribute DataStoreKey id;
   readonly attribute DOMString operation;
+  readonly attribute DOMString owner;
 };
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -337,16 +337,23 @@ partial interface Window {
 
   [ChromeOnly, Throws] readonly attribute object? __content;
 };
 
 Window implements TouchEventHandlers;
 
 Window implements OnErrorEventHandlerForWindow;
 
+// ConsoleAPI
+partial interface Window {
+  [Replaceable, GetterThrows]
+  readonly attribute Console console;
+};
+
+
 [ChromeOnly] interface ChromeWindow {
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_MAXIMIZED = 1;
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_MINIMIZED = 2;
   [Func="nsGlobalWindow::IsChromeWindow"]
   const unsigned short STATE_NORMAL = 3;
   [Func="nsGlobalWindow::IsChromeWindow"]
deleted file mode 100644
--- a/dom/webidl/WorkerConsole.webidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=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/. */
-
-interface WorkerConsole {
-  void log(any... data);
-  void info(any... data);
-  void warn(any... data);
-  void error(any... data);
-  void _exception(any... data);
-  void debug(any... data);
-  void trace();
-  void dir(any data);
-  void group(any... data);
-  void groupCollapsed(any... data);
-  void groupEnd(any... data);
-  void time(any time);
-  void timeEnd(any time);
-  void profile(any... data);
-  void profileEnd(any... data);
-  void assert(boolean condition, any... data);
-  void ___noSuchMethod__();
-};
-
-// This dictionary is used internally to send the stack trace from the worker to
-// the main thread Console API implementation.
-dictionary WorkerConsoleStack {
-  DOMString filename = "";
-  unsigned long lineNumber = 0;
-  DOMString functionName = "";
-  unsigned long language = 0;
-};
-
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -11,17 +11,17 @@
  * You are granted a license to use, reproduce and create derivative works of
  * this document.
  */
 
 interface WorkerGlobalScope : EventTarget {
   readonly attribute WorkerGlobalScope self;
 
   [Replaceable]
-  readonly attribute WorkerConsole console;
+  readonly attribute Console console;
 
   readonly attribute WorkerLocation location;
 
   void close();
   attribute OnErrorEventHandler onerror;
 
   attribute EventHandler onoffline;
   attribute EventHandler ononline;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -50,16 +50,17 @@ WEBIDL_FILES = [
     'ChannelMergerNode.webidl',
     'ChannelSplitterNode.webidl',
     'CharacterData.webidl',
     'ChildNode.webidl',
     'ClipboardEvent.webidl',
     'CommandEvent.webidl',
     'Comment.webidl',
     'CompositionEvent.webidl',
+    'Console.webidl',
     'Contacts.webidl',
     'ConvolverNode.webidl',
     'Coordinates.webidl',
     'CSS.webidl',
     'CSSPrimitiveValue.webidl',
     'CSSStyleDeclaration.webidl',
     'CSSStyleSheet.webidl',
     'CSSValue.webidl',
@@ -429,17 +430,16 @@ WEBIDL_FILES = [
     'VTTRegion.webidl',
     'VTTRegionList.webidl',
     'WaveShaperNode.webidl',
     'WebComponents.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
     'WifiOptions.webidl',
     'Worker.webidl',
-    'WorkerConsole.webidl',
     'WorkerGlobalScope.webidl',
     'WorkerLocation.webidl',
     'WorkerNavigator.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
deleted file mode 100644
--- a/dom/workers/Console.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_workers_Console_h
-#define mozilla_dom_workers_Console_h
-
-#include "Workers.h"
-#include "WorkerPrivate.h"
-#include "nsWrapperCache.h"
-
-BEGIN_WORKERS_NAMESPACE
-
-class ConsoleProxy;
-class ConsoleStackData;
-
-class WorkerConsole MOZ_FINAL : public nsWrapperCache
-{
-  WorkerConsole();
-
-public:
-
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WorkerConsole)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerConsole)
-
-  static already_AddRefed<WorkerConsole>
-  Create();
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
-
-  nsISupports* GetParentObject() const
-  {
-    return nullptr;
-  }
-
-  ~WorkerConsole();
-
-  ConsoleProxy*
-  GetProxy() const
-  {
-    return mProxy;
-  }
-
-  void
-  SetProxy(ConsoleProxy* aProxy);
-
-  // WebIDL methods
-
-  void
-  Log(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Info(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Warn(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Error(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Exception(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Debug(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Trace(JSContext* aCx);
-
-  void
-  Dir(JSContext* aCx, JS::Handle<JS::Value> aValue);
-
-  void
-  Group(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  GroupCollapsed(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  GroupEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Time(JSContext* aCx, JS::Handle<JS::Value> aTimer);
-
-  void
-  TimeEnd(JSContext* aCx, JS::Handle<JS::Value> aTimer);
-
-  void
-  Profile(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
-
-  void
-  Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
-
-  void
-  __noSuchMethod__();
-
-private:
-  void
-  Method(JSContext* aCx, const char* aMethodName,
-         const Sequence<JS::Value>& aData, uint32_t aMaxStackDepth);
-
-  nsRefPtr<ConsoleProxy> mProxy;
-};
-
-END_WORKERS_NAMESPACE
-
-#endif // mozilla_dom_workers_Console_h
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -6,16 +6,17 @@
 #include "WorkerPrivate.h"
 #include "ChromeWorkerScope.h"
 #include "File.h"
 #include "RuntimeService.h"
 
 #include "jsapi.h"
 #include "js/OldDebugAPI.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ConsoleBinding.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/EventBinding.h"
 #include "mozilla/dom/EventHandlerBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEventBinding.h"
@@ -53,17 +54,18 @@ WorkerPrivate::RegisterBindings(JSContex
   }
 
   // Init other classes we care about.
   if (!file::InitClasses(aCx, aGlobal)) {
     return false;
   }
 
   // Init other paris-bindings.
-  if (!DOMExceptionBinding::GetConstructorObject(aCx, aGlobal) ||
+  if (!ConsoleBinding::GetConstructorObject(aCx, aGlobal) ||
+      !DOMExceptionBinding::GetConstructorObject(aCx, aGlobal) ||
       !EventBinding::GetConstructorObject(aCx, aGlobal) ||
       !FileReaderSyncBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !ImageDataBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessageEventBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
       !PromiseBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -5,22 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerScope.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/Console.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
-#include "Console.h"
 #include "Location.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerPrivate.h"
 
 #define UNWRAP_WORKER_OBJECT(Interface, obj, value)                           \
@@ -72,27 +72,27 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 JSObject*
 WorkerGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   MOZ_CRASH("We should never get here!");
 }
 
-WorkerConsole*
-WorkerGlobalScope::Console()
+already_AddRefed<Console>
+WorkerGlobalScope::GetConsole()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!mConsole) {
-    mConsole = WorkerConsole::Create();
+    mConsole = new Console(nullptr);
     MOZ_ASSERT(mConsole);
   }
 
-  return mConsole;
+  return mConsole.forget();
 }
 
 already_AddRefed<WorkerLocation>
 WorkerGlobalScope::Location()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!mLocation) {
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -7,32 +7,32 @@
 #define mozilla_dom_workerscope_h__
 
 #include "Workers.h"
 #include "nsDOMEventTargetHelper.h"
 
 namespace mozilla {
 namespace dom {
 
+class Console;
 class Function;
 
 } // namespace dom
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerPrivate;
-class WorkerConsole;
 class WorkerLocation;
 class WorkerNavigator;
 
 class WorkerGlobalScope : public nsDOMEventTargetHelper,
                           public nsIGlobalObject
 {
-  nsRefPtr<WorkerConsole> mConsole;
+  nsRefPtr<Console> mConsole;
   nsRefPtr<WorkerLocation> mLocation;
   nsRefPtr<WorkerNavigator> mNavigator;
 
 protected:
   WorkerPrivate* mWorkerPrivate;
 
   WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
@@ -55,18 +55,18 @@ public:
                                                          nsDOMEventTargetHelper)
 
   already_AddRefed<WorkerGlobalScope>
   Self()
   {
     return nsRefPtr<WorkerGlobalScope>(this).forget();
   }
 
-  WorkerConsole*
-  Console();
+  already_AddRefed<Console>
+  GetConsole();
 
   already_AddRefed<WorkerLocation>
   Location();
 
   already_AddRefed<WorkerNavigator>
   Navigator();
 
   already_AddRefed<WorkerNavigator>
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -14,31 +14,29 @@ EXPORTS.mozilla.dom += [
 ]
 
 EXPORTS.mozilla.dom.workers += [
     'Workers.h',
 ]
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
-    'Console.h',
     'FileReaderSync.h',
     'Location.h',
     'MessagePort.h',
     'Navigator.h',
     'SharedWorker.h',
     'URL.h',
     'WorkerFeature.h',
     'XMLHttpRequest.h',
     'XMLHttpRequestUpload.h',
 ]
 
 SOURCES += [
     'ChromeWorkerScope.cpp',
-    'Console.cpp',
     'File.cpp',
     'FileReaderSync.cpp',
     'Location.cpp',
     'MessagePort.cpp',
     'Navigator.cpp',
     'Principal.cpp',
     'RegisterBindings.cpp',
     'RuntimeService.cpp',
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -3,17 +3,17 @@
 /* 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 "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsIInputStream.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsHashtable.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
--- a/dom/xbl/nsXBLContentSink.cpp
+++ b/dom/xbl/nsXBLContentSink.cpp
@@ -5,17 +5,17 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsXBLContentSink.h"
 #include "nsIDocument.h"
 #include "nsBindingManager.h"
 #include "nsIDOMNode.h"
 #include "nsGkAtoms.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIURI.h"
 #include "nsTextFragment.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
 #include "nsXBLProtoImplProperty.h"
 #include "nsXBLProtoImplMethod.h"
 #include "nsXBLProtoImplField.h"
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsIInputStream.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "nsContentCreatorFunctions.h"
--- a/dom/xbl/nsXBLPrototypeBinding.h
+++ b/dom/xbl/nsXBLPrototypeBinding.h
@@ -210,17 +210,17 @@ public:
    *   32-bit count of the number of child nodes
    *     each child node is serialized in the same manner in sequence
    *   the constant XBLBinding_Serialize_NoContent
    */
   nsresult WriteContentNode(nsIObjectOutputStream* aStream, nsIContent* aNode);
 
   /**
    * Read or write a namespace id from or to aStream. If the namespace matches
-   * one of the built-in ones defined in nsINameSpaceManager.h, it will be written as
+   * one of the built-in ones defined in nsNameSpaceManager.h, it will be written as
    * a single byte with that value. Otherwise, XBLBinding_Serialize_CustomNamespace is
    * written out, followed by a string written with writeWStringZ.
    */
   nsresult ReadNamespace(nsIObjectInputStream* aStream, int32_t& aNameSpaceID);
   nsresult WriteNamespace(nsIObjectOutputStream* aStream, int32_t aNameSpaceID);
 
 public:
   nsXBLPrototypeBinding();
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -9,17 +9,17 @@
 #include "nsXBLPrototypeHandler.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsIContent.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsIScriptContext.h"
 #include "nsIDocument.h"
 #include "nsIJSEventListener.h"
 #include "nsIController.h"
 #include "nsIControllers.h"
 #include "nsIDOMXULElement.h"
 #include "nsIURI.h"
 #include "nsIDOMHTMLTextAreaElement.h"
--- a/dom/xbl/nsXBLSerialize.h
+++ b/dom/xbl/nsXBLSerialize.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXBLSerialize_h__
 #define nsXBLSerialize_h__
 
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "js/TypeDecls.h"
 
 typedef uint8_t XBLBindingSerializeDetails;
 
 // A version number to ensure we don't load cached data in a different
 // file format.
 #define XBLBinding_Serialize_Version 0x00000003
 
@@ -52,17 +52,17 @@ typedef uint8_t XBLBindingSerializeDetai
 // Appears at the end of the list of insertion points to indicate that there
 // are no more. 
 #define XBLBinding_Serialize_NoMoreInsertionPoints 0xFFFFFFFF
 
 // When serializing content nodes, a single-byte namespace id is written out
 // first. The special values below can appear in place of a namespace id.
 
 // Indicates that this is not one of the built-in namespaces defined in 
-// nsINameSpaceManager.h. The string form will be serialized immediately
+// nsNameSpaceManager.h. The string form will be serialized immediately
 // following.
 #define XBLBinding_Serialize_CustomNamespace 0xFE
 
 // Flags to indicate a non-element node. Otherwise, it is an element. 
 #define XBLBinding_Serialize_TextNode 0xFB
 #define XBLBinding_Serialize_CDATANode 0xFC
 #define XBLBinding_Serialize_CommentNode 0xFD
 
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -5,17 +5,17 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsNetUtil.h"
 #include "nsXBLService.h"
 #include "nsXBLWindowKeyHandler.h"
 #include "nsIInputStream.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsHashtable.h"
 #include "nsIURI.h"
 #include "nsIDOMElement.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "plstr.h"
 #include "nsIContent.h"
--- a/dom/xslt/xpath/XPathEvaluator.cpp
+++ b/dom/xslt/xpath/XPathEvaluator.cpp
@@ -12,17 +12,17 @@
 #include "nsContentCID.h"
 #include "txExpr.h"
 #include "txExprParser.h"
 #include "nsError.h"
 #include "txURIUtils.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsDOMString.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/XPathEvaluatorBinding.h"
 
 extern nsresult
 TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
                             nsIAtom *aName, nsISupports *aState,
                             FunctionCall **aFunction);
 
--- a/dom/xslt/xpath/txXPathNode.h
+++ b/dom/xslt/xpath/txXPathNode.h
@@ -5,17 +5,17 @@
 
 #ifndef txXPathNode_h__
 #define txXPathNode_h__
 
 #include "nsAutoPtr.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMNode.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsContentUtils.h" // For NameSpaceManager().
 
 typedef nsIDOMNode txXPathNodeType;
 
 class txXPathNode
 {
 public:
     bool operator==(const txXPathNode& aNode) const;
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -7,17 +7,16 @@
 #include "nsIAuthPrompt.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsIExpatSink.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadGroup.h"
-#include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
 #include "nsIParser.h"
 #include "nsCharsetSource.h"
 #include "nsIRequestObserver.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIStreamConverterService.h"
 #include "nsSyncLoadService.h"
--- a/dom/xslt/xslt/txMozillaXMLOutput.cpp
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -17,17 +17,17 @@
 #include "nsIContent.h"
 #include "nsContentCID.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
 #include "nsGkAtoms.h"
 #include "txLog.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMDocumentFragment.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsCSSStyleSheet.h"
 #include "txStringUtils.h"
 #include "txURIUtils.h"
 #include "nsIHTMLDocument.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIDocumentTransformer.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/Element.h"
--- a/editor/composer/src/nsComposeTxtSrvFilter.cpp
+++ b/editor/composer/src/nsComposeTxtSrvFilter.cpp
@@ -2,17 +2,17 @@
 /* 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 "nsComposeTxtSrvFilter.h"
 #include "nsError.h"                    // for NS_OK
 #include "nsIContent.h"                 // for nsIContent
 #include "nsIDOMNode.h"                 // for nsIDOMNode
-#include "nsINameSpaceManager.h"        // for kNameSpaceID_None
+#include "nsNameSpaceManager.h"        // for kNameSpaceID_None
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nscore.h"                     // for NS_IMETHODIMP
 
 nsComposeTxtSrvFilter::nsComposeTxtSrvFilter() :
   mIsForMail(false)
 {
 }
 
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -71,17 +71,17 @@
 #include "nsIDocumentStateListener.h"   // for nsIDocumentStateListener
 #include "nsIEditActionListener.h"      // for nsIEditActionListener
 #include "nsIEditorObserver.h"          // for nsIEditorObserver
 #include "nsIEditorSpellCheck.h"        // for nsIEditorSpellCheck
 #include "nsIFrame.h"                   // for nsIFrame
 #include "nsIHTMLDocument.h"            // for nsIHTMLDocument
 #include "nsIInlineSpellChecker.h"      // for nsIInlineSpellChecker, etc
 #include "nsIMEStateManager.h"          // for nsIMEStateManager
-#include "nsINameSpaceManager.h"        // for kNameSpaceID_None, etc
+#include "nsNameSpaceManager.h"        // for kNameSpaceID_None, etc
 #include "nsINode.h"                    // for nsINode, etc
 #include "nsIObserverService.h"         // for nsIObserverService
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc
 #include "nsIPresShell.h"               // for nsIPresShell
 #include "nsIPrivateTextRange.h"        // for nsIPrivateTextRange, etc
 #include "nsISelection.h"               // for nsISelection, etc
 #include "nsISelectionController.h"     // for nsISelectionController, etc
 #include "nsISelectionDisplay.h"        // for nsISelectionDisplay, etc
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -52,17 +52,17 @@
 #include "nsIDOMRange.h"
 #include "nsIDocument.h"
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsIEditorMailSupport.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIParserUtils.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISelection.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISupportsUtils.h"
 #include "nsITransferable.h"
--- a/editor/libeditor/html/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/html/nsHTMLEditUtils.cpp
@@ -14,17 +14,17 @@
 #include "nsEditor.h"                   // for nsEditor
 #include "nsError.h"                    // for NS_SUCCEEDED
 #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::a, etc
 #include "nsHTMLEditUtils.h"
 #include "nsHTMLTags.h"
 #include "nsIAtom.h"                    // for nsIAtom
 #include "nsIDOMHTMLAnchorElement.h"    // for nsIDOMHTMLAnchorElement
 #include "nsIDOMNode.h"                 // for nsIDOMNode
-#include "nsINameSpaceManager.h"        // for kNameSpaceID_None
+#include "nsNameSpaceManager.h"        // for kNameSpaceID_None
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsString.h"                   // for nsAutoString
 #include "nsTextEditUtils.h"            // for nsTextEditUtils
 
 using namespace mozilla;
 
 ///////////////////////////////////////////////////////////////////////////
 //                  
--- a/editor/libeditor/html/nsHTMLEditorStyle.cpp
+++ b/editor/libeditor/html/nsHTMLEditorStyle.cpp
@@ -28,17 +28,17 @@
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMRange.h"
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsISelection.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupportsImpl.h"
 #include "nsLiteralString.h"
 #include "nsReadableUtils.h"
 #include "nsSelectionState.h"
 #include "nsString.h"
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -32,17 +32,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventTarget.h" 
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIEditorIMESupport.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsIPrivateTextEvent.h"
 #include "nsIPrivateTextRange.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupportsPrimitives.h"
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -25,17 +25,17 @@
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMNodeIterator.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMText.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISelection.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupportsBase.h"
 #include "nsLiteralString.h"
 #include "mozilla/dom/NodeIterator.h"
 #include "nsTextEditRules.h"
--- a/editor/libeditor/text/nsTextEditUtils.cpp
+++ b/editor/libeditor/text/nsTextEditUtils.cpp
@@ -9,17 +9,17 @@
 #include "nsCOMPtr.h"
 #include "nsCaseTreatment.h"
 #include "nsDebug.h"
 #include "nsEditor.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
-#include "nsINameSpaceManager.h"
+#include "nsNameSpaceManager.h"
 #include "nsLiteralString.h"
 #include "nsPlaintextEditor.h"
 #include "nsString.h"
 #include "nsTextEditUtils.h"
 
 using namespace mozilla;
 
 ///////////////////////////////////////////////////////////////////////////
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -182,17 +182,17 @@ UNIFIED_SOURCES += [
     'cairo-user-font.c',
     'cairo-version.c',
     'cairo-wideint.c',
     'cairo.c',
 ]
 
 MSVC_ENABLE_PGO = True
 
-FINAL_LIBRARY = 'gkmedias'
+FINAL_LIBRARY = 'xul'
 
 DEFINES['PACKAGE_VERSION'] = '"moz"'
 DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
 
 for var in ('CAIRO_HAS_PTHREAD', '_GNU_SOURCE'):
     DEFINES[var] = True
 
 for var in ('MOZ_TREE_CAIRO', 'MOZ_TREE_PIXMAN'):
--- a/gfx/cairo/libpixman/src/moz.build
+++ b/gfx/cairo/libpixman/src/moz.build
@@ -55,17 +55,17 @@ SOURCES += [
     'pixman-trap.c',
     'pixman-utils.c',
     'pixman-x86.c',
     'pixman.c',
 ]
 
 MSVC_ENABLE_PGO = True
 
-FINAL_LIBRARY = 'gkmedias'
+FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../../cairo/src',
 ]
 
 
 if CONFIG['MOZ_USE_PTHREADS']:
     DEFINES['HAVE_PTHREAD_SETSPECIFIC'] = True
 
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -956,35 +956,34 @@ BasicLayerManager::PaintLayer(gfxContext
     const nsIntRect& bounds = visibleRegion.GetBounds();
     RefPtr<DrawTarget> untransformedDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
                                                                    SurfaceFormat::B8G8R8A8);
     if (!untransformedDT) {
       return;
     }
 
-    nsRefPtr<gfxContext> groupTarget = new gfxContext(untransformedDT);
-    groupTarget->Translate(gfxPoint(-bounds.x, -bounds.y));
+    nsRefPtr<gfxContext> groupTarget = new gfxContext(untransformedDT,
+                                                      Point(bounds.x, bounds.y));
 
     PaintSelfOrChildren(paintLayerContext, groupTarget);
 
     // Temporary fast fix for bug 725886
     // Revert these changes when 725886 is ready
     NS_ABORT_IF_FALSE(untransformedDT,
                       "We should always allocate an untransformed surface with 3d transforms!");
     gfxRect destRect;
 #ifdef DEBUG
     if (aLayer->GetDebugColorIndex() != 0) {
       gfxRGBA  color((aLayer->GetDebugColorIndex() & 1) ? 1.0 : 0.0,
                      (aLayer->GetDebugColorIndex() & 2) ? 1.0 : 0.0,
                      (aLayer->GetDebugColorIndex() & 4) ? 1.0 : 0.0,
                      1.0);
 
-      nsRefPtr<gfxContext> temp = new gfxContext(untransformedDT);
-      temp->Translate(gfxPoint(-bounds.x, -bounds.y));
+      nsRefPtr<gfxContext> temp = new gfxContext(untransformedDT, Point(bounds.x, bounds.y));
       temp->SetColor(color);
       temp->Paint();
     }
 #endif
     gfx3DMatrix effectiveTransform;
     gfx::To3DMatrix(aLayer->GetEffectiveTransform(), effectiveTransform);
     nsRefPtr<gfxASurface> result =
       Transform3D(untransformedDT->Snapshot(), aTarget, bounds,
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -299,17 +299,17 @@ CompositableParentManager::ReceiveCompos
     default: {
       MOZ_ASSERT(false, "bad type");
     }
   }
 
   return true;
 }
 
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 void
 CompositableParentManager::ReturnTextureDataIfNecessary(CompositableHost* aCompositable,
                                                         EditReplyVector& replyv,
                                                         PCompositableParent* aParent)
 {
   if (!aCompositable || !aCompositable->GetCompositableBackendSpecificData()) {
     return;
   }
--- a/gfx/layers/ipc/FenceUtils.h
+++ b/gfx/layers/ipc/FenceUtils.h
@@ -8,36 +8,36 @@
 #ifndef IPC_FencerUtils_h
 #define IPC_FencerUtils_h
 
 #include "ipc/IPCMessageUtils.h"
 
 /**
  * FenceHandle is used for delivering Fence object via ipc.
  */
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 # include "mozilla/layers/FenceUtilsGonk.h"
 #else
 namespace mozilla {
 namespace layers {
 struct FenceHandle {
   bool operator==(const FenceHandle&) const { return false; }
   bool IsValid() const { return false; }
 };
 } // namespace layers
 } // namespace mozilla
-#endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 
 namespace IPC {
 
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 #else
 template <>
 struct ParamTraits<mozilla::layers::FenceHandle> {
   typedef mozilla::layers::FenceHandle paramType;
   static void Write(Message*, const paramType&) {}
   static bool Read(const Message*, void**, paramType*) { return false; }
 };
-#endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#endif // MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 
 } // namespace IPC
 
 #endif // IPC_FencerUtils_h
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -198,17 +198,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         'opengl/EGLImageHelpers.cpp',
         'opengl/GrallocTextureClient.cpp',
         'opengl/GrallocTextureHost.cpp',
     ]
     SOURCES += [
         'ipc/ShadowLayerUtilsGralloc.cpp',
     ]
 
-    if CONFIG['ANDROID_VERSION'] >= '18':
+    if CONFIG['ANDROID_VERSION'] >= '17':
         EXPORTS.mozilla.layers += [
             'ipc/FenceUtilsGonk.h',
         ]
         SOURCES += [
             'ipc/FenceUtilsGonk.cpp',
         ]
 
 UNIFIED_SOURCES += [
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -213,20 +213,25 @@ GrallocTextureClientOGL::Lock(OpenMode a
   if (!IsValid() || !IsAllocated()) {
     return false;
   }
 
   if (mMappedBuffer) {
     return true;
   }
 
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
   if (mReleaseFenceHandle.IsValid()) {
     android::sp<Fence> fence = mReleaseFenceHandle.mFence;
+#if ANDROID_VERSION == 17
+    fence->waitForever(1000, "GrallocTextureClientOGL::Lock");
+    // 1000 is what Android uses. It is warning timeout ms.
+#else
     fence->waitForever("GrallocTextureClientOGL::Lock");
+#endif
     mReleaseFenceHandle = FenceHandle();
   }
 #endif
 
   uint32_t usage = 0;
   if (aMode & OPEN_READ) {
     usage |= GRALLOC_USAGE_SW_READ_OFTEN;
   }
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -66,17 +66,17 @@ protected:
   CompositorOGL* mCompositor;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   gfx::SurfaceFormat mFormat;
   bool mNeedsReset;
 };
 
 class GrallocTextureHostOGL : public TextureHost
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
                             , public TextureHostOGL
 #endif
 {
   friend class GrallocBufferActor;
 public:
   GrallocTextureHostOGL(TextureFlags aFlags,
                         const NewSurfaceDescriptorGralloc& aDescriptor);
 
@@ -102,17 +102,17 @@ public:
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
   virtual NewTextureSource* GetTextureSources() MOZ_OVERRIDE
   {
     return mTextureSource;
   }
 
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
   virtual TextureHostOGL* AsHostOGL() MOZ_OVERRIDE
   {
     return this;
   }
 #endif
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -196,17 +196,17 @@ CompositableDataGonkOGL::DeleteTextureIf
   if (mTexture) {
     if (gl()->MakeCurrent()) {
       gl()->fDeleteTextures(1, &mTexture);
     }
     mTexture = 0;
   }
 }
 
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 bool
 TextureHostOGL::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
 {
   if (!aReleaseFence.get() || !aReleaseFence->isValid()) {
     return false;
   }
 
   if (!mReleaseFence.get()) {
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -26,17 +26,17 @@
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsISupportsImpl.h"            // for TextureImage::Release, etc
 #include "OGLShaderProgram.h"           // for ShaderProgramType, etc
 #ifdef MOZ_WIDGET_GONK
 #include <ui/GraphicBuffer.h>
-#if ANDROID_VERSION >= 18
+#if ANDROID_VERSION >= 17
 #include <ui/Fence.h>
 #endif
 #endif
 
 class gfxImageSurface;
 class gfxReusableSurfaceWrapper;
 class nsIntRegion;
 struct nsIntPoint;
@@ -120,17 +120,17 @@ public:
 };
 
 /**
  * TextureHostOGL provides the necessary API for platform specific composition.
  */
 class TextureHostOGL
 {
 public:
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 18
+#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
 
   /**
    * Store a fence that will signal when the current buffer is no longer being read.
    * Similar to android's GLConsumer::setReleaseFence()
    */
   virtual bool SetReleaseFence(const android::sp<android::Fence>& aReleaseFence);
 
   /**
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -15,17 +15,17 @@ footer = """
 # left out of UNIFIED_SOURCES for now; that's not C++ anyway, nothing else to unify it with
 if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
     SOURCES += [
         'trunk/src/opts/memset.arm.S',
     ]
 
 MSVC_ENABLE_PGO = True
 
-FINAL_LIBRARY = 'gkmedias'
+FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     'trunk/include/config',
     'trunk/include/core',
     'trunk/include/effects',
     'trunk/include/gpu',
     'trunk/include/images',
     'trunk/include/lazy',
     'trunk/include/pathops',
@@ -62,19 +62,16 @@ if CONFIG['INTEL_ARCHITECTURE'] and CONF
 
 if (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android') or \
    (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa') or \
    (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk') or \
    CONFIG['MOZ_WIDGET_QT'] or \
    CONFIG['MOZ_WIDGET_GTK']:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-    DEFINES['SKIA_DLL'] = 1
-
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
 """
 
 import json
 
 platforms = ['linux', 'mac', 'android', 'win']
 
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -802,17 +802,17 @@ else:
 # left out of UNIFIED_SOURCES for now; that's not C++ anyway, nothing else to unify it with
 if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
     SOURCES += [
         'trunk/src/opts/memset.arm.S',
     ]
 
 MSVC_ENABLE_PGO = True
 
-FINAL_LIBRARY = 'gkmedias'
+FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     'trunk/include/config',
     'trunk/include/core',
     'trunk/include/effects',
     'trunk/include/gpu',
     'trunk/include/images',
     'trunk/include/lazy',
     'trunk/include/pathops',
@@ -845,13 +845,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('and
     DEFINES['SK_USE_POSIX_THREADS'] = 1
 
 if CONFIG['INTEL_ARCHITECTURE'] and CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3']:
     DEFINES['SK_BUILD_SSSE3'] = 1
 
 if (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android') or (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk') or    (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa') or    CONFIG['MOZ_WIDGET_GTK'] or    CONFIG['MOZ_WIDGET_QT']:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-    DEFINES['SKIA_DLL'] = 1
-
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -93,30 +93,31 @@ gfxContext::gfxContext(gfxASurface *surf
     Translate(gfxPoint(0, size.width));
     gfxMatrix matrix(0, -1,
                       1,  0,
                       0,  0);
     Multiply(matrix);
   }
 }
 
-gfxContext::gfxContext(DrawTarget *aTarget)
+gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   : mPathIsRect(false)
   , mTransformChanged(false)
   , mCairo(nullptr)
   , mRefCairo(nullptr)
   , mSurface(nullptr)
   , mFlags(0)
   , mDT(aTarget)
   , mOriginalDT(aTarget)
 {
   MOZ_COUNT_CTOR(gfxContext);
 
   mStateStack.SetLength(1);
   CurrentState().drawTarget = mDT;
+  CurrentState().deviceOffset = aDeviceOffset;
   mDT->SetTransform(Matrix());
 }
 
 /* static */ already_AddRefed<gfxContext>
 gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
 {
   Matrix transform = aTarget->GetTransform();
   nsRefPtr<gfxContext> result = new gfxContext(aTarget);
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -46,17 +46,18 @@ public:
      */
     gfxContext(gfxASurface *surface);
 
     /**
      * Initialize this context from a DrawTarget.
      * Strips any transform from aTarget.
      * aTarget will be flushed in the gfxContext's destructor.
      */
-    gfxContext(mozilla::gfx::DrawTarget *aTarget);
+    gfxContext(mozilla::gfx::DrawTarget *aTarget,
+               const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
 
     ~gfxContext();
 
     /**
      * Create a new gfxContext wrapping aTarget and preserving aTarget's
      * transform. Note that the transform is moved from aTarget to the resulting
      * gfxContext, aTarget will no longer have its transform.
      */
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -72,127 +72,131 @@ JS_FRIEND_API(size_t) MemoryReportingSun
 // on every hash and match! Beware.
 struct InefficientNonFlatteningStringHashPolicy
 {
     typedef JSString *Lookup;
     static HashNumber hash(const Lookup &l);
     static bool match(const JSString *const &k, const Lookup &l);
 };
 
+struct CStringHashPolicy
+{
+    typedef const char *Lookup;
+    static HashNumber hash(const Lookup &l);
+    static bool match(const char *const &k, const Lookup &l);
+};
+
 // This file features many classes with numerous size_t fields, and each such
 // class has one or more methods that need to operate on all of these fields.
 // Writing these individually is error-prone -- it's easy to add a new field
 // without updating all the required methods.  So we define a single macro list
 // in each class to name the fields (and notable characteristics of them), and
 // then use the following macros to transform those lists into the required
 // methods.
 //
 // In some classes, one or more of the macro arguments aren't used.  We use '_'
 // for those.
 //
 #define DECL_SIZE(kind, gc, mSize)                      size_t mSize;
 #define ZERO_SIZE(kind, gc, mSize)                      mSize(0),
 #define COPY_OTHER_SIZE(kind, gc, mSize)                mSize(other.mSize),
 #define ADD_OTHER_SIZE(kind, gc, mSize)                 mSize += other.mSize;
+#define SUB_OTHER_SIZE(kind, gc, mSize)                 MOZ_ASSERT(mSize >= other.mSize); \
+                                                        mSize -= other.mSize;
+#define ADD_SIZE_TO_N(kind, gc, mSize)                  n += mSize;
 #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0;
 #define ADD_TO_TAB_SIZES(kind, gc, mSize)               sizes->add(JS::TabSizes::kind, mSize);
 
 // Used to annotate which size_t fields measure live GC things and which don't.
 enum {
     NotLiveGCThing = false,
     IsLiveGCThing = true
 };
 
-struct ZoneStatsPod
-{
-#define FOR_EACH_SIZE(macro) \
-    macro(Other,   NotLiveGCThing, gcHeapArenaAdmin) \
-    macro(Other,   NotLiveGCThing, unusedGCThings) \
-    macro(Other,   IsLiveGCThing,  lazyScriptsGCHeap) \
-    macro(Other,   NotLiveGCThing, lazyScriptsMallocHeap) \
-    macro(Other,   IsLiveGCThing,  jitCodesGCHeap) \
-    macro(Other,   IsLiveGCThing,  typeObjectsGCHeap) \
-    macro(Other,   NotLiveGCThing, typeObjectsMallocHeap) \
-    macro(Other,   NotLiveGCThing, typePool) \
-    macro(Strings, IsLiveGCThing,  stringsShortGCHeap) \
-    macro(Strings, IsLiveGCThing,  stringsNormalGCHeap) \
-    macro(Strings, NotLiveGCThing, stringsNormalMallocHeap)
-
-    ZoneStatsPod()
-      : FOR_EACH_SIZE(ZERO_SIZE)
-        extra()
-    {}
-
-    void add(const ZoneStatsPod &other) {
-        FOR_EACH_SIZE(ADD_OTHER_SIZE)
-        // Do nothing with |extra|.
-    }
-
-    size_t sizeOfLiveGCThings() const {
-        size_t n = 0;
-        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
-        // Do nothing with |extra|.
-        return n;
-    }
-
-    void addToTabSizes(JS::TabSizes *sizes) const {
-        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
-        // Do nothing with |extra|.
-    }
-
-    FOR_EACH_SIZE(DECL_SIZE)
-    void *extra;    // This field can be used by embedders.
-
-#undef FOR_EACH_SIZE
-};
-
 } // namespace js
 
 namespace JS {
 
-// Data for tracking memory usage of things hanging off objects.
-struct ObjectsExtraSizes
+struct ClassInfo
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(Objects, NotLiveGCThing, mallocHeapSlots) \
-    macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \
-    macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \
-    macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \
-    macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \
-    macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \
-    macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \
-    macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \
-    macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \
-    macro(Objects, NotLiveGCThing, mallocHeapCtypesData)
+    macro(Objects, IsLiveGCThing,  objectsGCHeap) \
+    macro(Objects, NotLiveGCThing, objectsMallocHeapSlots) \
+    macro(Objects, NotLiveGCThing, objectsMallocHeapElementsNonAsmJS) \
+    macro(Objects, NotLiveGCThing, objectsMallocHeapElementsAsmJS) \
+    macro(Objects, NotLiveGCThing, objectsNonHeapElementsAsmJS) \
+    macro(Objects, NotLiveGCThing, objectsNonHeapCodeAsmJS) \
+    macro(Objects, NotLiveGCThing, objectsMallocHeapMisc) \
+    \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapTree) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapDict) \
+    macro(Other,   IsLiveGCThing,  shapesGCHeapBase) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeTables) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapDictTables) \
+    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeKids) \
 
-    ObjectsExtraSizes()
+    ClassInfo()
       : FOR_EACH_SIZE(ZERO_SIZE)
         dummy()
     {}
 
-    void add(const ObjectsExtraSizes &other) {
+    void add(const ClassInfo &other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
     }
 
+    void subtract(const ClassInfo &other) {
+        FOR_EACH_SIZE(SUB_OTHER_SIZE)
+    }
+
+    bool isNotable() const {
+        static const size_t NotabilityThreshold = 16 * 1024;
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N)
+        return n >= NotabilityThreshold;
+    }
+
     size_t sizeOfLiveGCThings() const {
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
         return n;
     }
 
     void addToTabSizes(TabSizes *sizes) const {
         FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
     }
 
     FOR_EACH_SIZE(DECL_SIZE)
     int dummy;  // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
 
 #undef FOR_EACH_SIZE
 };
 
+// Holds data about a notable class (one whose combined object and shape
+// instances use more than a certain amount of memory) so we can report it
+// individually.
+//
+// The only difference between this class and ClassInfo is that this class
+// holds a copy of the filename.
+struct NotableClassInfo : public ClassInfo
+{
+    NotableClassInfo();
+    NotableClassInfo(const char *className, const ClassInfo &info);
+    NotableClassInfo(NotableClassInfo &&info);
+    NotableClassInfo &operator=(NotableClassInfo &&info);
+
+    ~NotableClassInfo() {
+        js_free(className_);
+    }
+
+    char *className_;
+
+  private:
+    NotableClassInfo(const NotableClassInfo& info) MOZ_DELETE;
+};
+
 // Data for tracking JIT-code memory usage.
 struct CodeSizes
 {
 #define FOR_EACH_SIZE(macro) \
     macro(_, _, ion) \
     macro(_, _, baseline) \
     macro(_, _, regexp) \
     macro(_, _, other) \
@@ -236,238 +240,372 @@ struct GCSizes
 
 // This class holds information about the memory taken up by identical copies of
 // a particular string.  Multiple JSStrings may have their sizes aggregated
 // together into one StringInfo object.  Note that two strings with identical
 // chars will not be aggregated together if one is a short string and the other
 // is not.
 struct StringInfo
 {
+#define FOR_EACH_SIZE(macro) \
+    macro(Strings, IsLiveGCThing,  gcHeap) \
+    macro(Strings, NotLiveGCThing, mallocHeap) \
+
     StringInfo()
-      : numCopies(0),
-        isShort(0),
-        gcHeap(0),
-        mallocHeap(0)
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        numCopies(0)
     {}
 
-    StringInfo(bool isShort, size_t gcSize, size_t mallocSize)
-      : numCopies(1),
-        isShort(isShort),
-        gcHeap(gcSize),
-        mallocHeap(mallocSize)
-    {}
+    void add(const StringInfo &other) {
+        FOR_EACH_SIZE(ADD_OTHER_SIZE);
+        numCopies++;
+    }
 
-    void add(bool isShort, size_t gcSize, size_t mallocSize) {
-        numCopies++;
-        MOZ_ASSERT(isShort == this->isShort);
-        gcHeap += gcSize;
-        mallocHeap += mallocSize;
+    void subtract(const StringInfo &other) {
+        FOR_EACH_SIZE(SUB_OTHER_SIZE);
+        numCopies--;
+    }
+
+    bool isNotable() const {
+        static const size_t NotabilityThreshold = 16 * 1024;
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N)
+        return n >= NotabilityThreshold;
     }
 
-    void add(const StringInfo& info) {
-        numCopies += info.numCopies;
-        MOZ_ASSERT(info.isShort == isShort);
-        gcHeap += info.gcHeap;
-        mallocHeap += info.mallocHeap;
+    size_t sizeOfLiveGCThings() const {
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
+        return n;
+    }
+
+    void addToTabSizes(TabSizes *sizes) const {
+        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
     }
 
-    uint32_t numCopies:31;  // How many copies of the string have we seen?
-    uint32_t isShort:1;     // Is it a short string?
+    FOR_EACH_SIZE(DECL_SIZE)
+    uint32_t numCopies;     // How many copies of the string have we seen?
 
-    // These are all totals across all copies of the string we've seen.
-    size_t gcHeap;
-    size_t mallocHeap;
+#undef FOR_EACH_SIZE
 };
 
-// Holds data about a notable string (one which uses more than
-// NotableStringInfo::notableSize() bytes of memory), so we can report it
-// individually.
+// Holds data about a notable string (one which, counting all duplicates, uses
+// more than a certain amount of memory) so we can report it individually.
 //
-// Essentially the only difference between this class and StringInfo is that
-// NotableStringInfo holds a copy of the string's chars.
+// The only difference between this class and StringInfo is that
+// NotableStringInfo holds a copy of some or all of the string's chars.
 struct NotableStringInfo : public StringInfo
 {
+    static const size_t MAX_SAVED_CHARS = 1024;
+
     NotableStringInfo();
     NotableStringInfo(JSString *str, const StringInfo &info);
     NotableStringInfo(NotableStringInfo &&info);
     NotableStringInfo &operator=(NotableStringInfo &&info);
 
     ~NotableStringInfo() {
         js_free(buffer);
     }
 
-    // A string needs to take up this many bytes of storage before we consider
-    // it to be "notable".
-    static size_t notableSize() {
-        return js::MemoryReportingSundriesThreshold();
-    }
-
     char *buffer;
     size_t length;
 
   private:
     NotableStringInfo(const NotableStringInfo& info) MOZ_DELETE;
 };
 
+// This class holds information about the memory taken up by script sources
+// from a particular file.
+struct ScriptSourceInfo
+{
+#define FOR_EACH_SIZE(macro) \
+    macro(_, _, compressed) \
+    macro(_, _, uncompressed) \
+    macro(_, _, misc)
+
+    ScriptSourceInfo()
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        numScripts(0)
+    {}
+
+    void add(const ScriptSourceInfo &other) {
+        FOR_EACH_SIZE(ADD_OTHER_SIZE)
+        numScripts++;
+    }
+
+    void subtract(const ScriptSourceInfo &other) {
+        FOR_EACH_SIZE(SUB_OTHER_SIZE)
+        numScripts--;
+    }
+
+    bool isNotable() const {
+        static const size_t NotabilityThreshold = 16 * 1024;
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N)
+        return n >= NotabilityThreshold;
+    }
+
+    FOR_EACH_SIZE(DECL_SIZE)
+    uint32_t numScripts;    // How many ScriptSources come from this file? (It
+                            // can be more than one in XML files that have
+                            // multiple scripts in CDATA sections.)
+#undef FOR_EACH_SIZE
+};
+
+// Holds data about a notable script source file (one whose combined
+// script sources use more than a certain amount of memory) so we can report it
+// individually.
+//
+// The only difference between this class and ScriptSourceInfo is that this
+// class holds a copy of the filename.
+struct NotableScriptSourceInfo : public ScriptSourceInfo
+{
+    NotableScriptSourceInfo();
+    NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info);
+    NotableScriptSourceInfo(NotableScriptSourceInfo &&info);
+    NotableScriptSourceInfo &operator=(NotableScriptSourceInfo &&info);
+
+    ~NotableScriptSourceInfo() {
+        js_free(filename_);
+    }
+
+    char *filename_;
+
+  private:
+    NotableScriptSourceInfo(const NotableScriptSourceInfo& info) MOZ_DELETE;
+};
+
 // These measurements relate directly to the JSRuntime, and not to zones and
 // compartments within it.
 struct RuntimeSizes
 {
 #define FOR_EACH_SIZE(macro) \
     macro(_, _, object) \
     macro(_, _, atomsTable) \
     macro(_, _, contexts) \
     macro(_, _, dtoa) \
     macro(_, _, temporary) \
     macro(_, _, regexpData) \
     macro(_, _, interpreterStack) \
     macro(_, _, mathCache) \
     macro(_, _, sourceDataCache) \
     macro(_, _, scriptData) \
-    macro(_, _, scriptSources)
 
     RuntimeSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
+        scriptSourceInfo(),
         code(),
-        gc()
-    {}
+        gc(),
+        notableScriptSources()
+    {
+        allScriptSources = js_new<ScriptSourcesHashMap>();
+        if (!allScriptSources || !allScriptSources->init())
+            MOZ_CRASH("oom");
+    }
+
+    ~RuntimeSizes() {
+        // |allScriptSources| is usually deleted and set to nullptr before this
+        // destructor runs. But there are failure cases due to OOMs that may
+        // prevent that, so it doesn't hurt to try again here.
+        js_delete(allScriptSources);
+    }
 
+    // The script source measurements in |scriptSourceInfo| are initially for
+    // all script sources.  At the end, if the measurement granularity is
+    // FineGrained, we subtract the measurements of the notable script sources
+    // and move them into |notableScriptSources|.
     FOR_EACH_SIZE(DECL_SIZE)
-    CodeSizes code;
-    GCSizes   gc;
+    ScriptSourceInfo    scriptSourceInfo;
+    CodeSizes           code;
+    GCSizes             gc;
+
+    typedef js::HashMap<const char*, ScriptSourceInfo,
+                        js::CStringHashPolicy,
+                        js::SystemAllocPolicy> ScriptSourcesHashMap;
+
+    // |allScriptSources| is only used transiently.  During the reporting phase
+    // it is filled with info about every script source in the runtime.  It's
+    // then used to fill in |notableScriptSources| (which actually gets
+    // reported), and immediately discarded afterwards.
+    ScriptSourcesHashMap *allScriptSources;
+    js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
 
 #undef FOR_EACH_SIZE
 };
 
-struct ZoneStats : js::ZoneStatsPod
+struct ZoneStats
 {
+#define FOR_EACH_SIZE(macro) \
+    macro(Other,   NotLiveGCThing, gcHeapArenaAdmin) \
+    macro(Other,   NotLiveGCThing, unusedGCThings) \
+    macro(Other,   IsLiveGCThing,  lazyScriptsGCHeap) \
+    macro(Other,   NotLiveGCThing, lazyScriptsMallocHeap) \
+    macro(Other,   IsLiveGCThing,  jitCodesGCHeap) \
+    macro(Other,   IsLiveGCThing,  typeObjectsGCHeap) \
+    macro(Other,   NotLiveGCThing, typeObjectsMallocHeap) \
+    macro(Other,   NotLiveGCThing, typePool) \
+
     ZoneStats()
-      : strings(nullptr)
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        stringInfo(),
+        extra(),
+        allStrings(nullptr),
+        notableStrings(),
+        isTotals(true)
     {}
 
     ZoneStats(ZoneStats &&other)
-      : ZoneStatsPod(mozilla::Move(other)),
-        strings(other.strings),
-        notableStrings(mozilla::Move(other.notableStrings))
+      : FOR_EACH_SIZE(COPY_OTHER_SIZE)
+        stringInfo(mozilla::Move(other.stringInfo)),
+        extra(other.extra),
+        allStrings(other.allStrings),
+        notableStrings(mozilla::Move(other.notableStrings)),
+        isTotals(other.isTotals)
     {
-        other.strings = nullptr;
+        other.allStrings = nullptr;
+        MOZ_ASSERT(!other.isTotals);
+    }
+
+    ~ZoneStats() {
+        // |allStrings| is usually deleted and set to nullptr before this
+        // destructor runs. But there are failure cases due to OOMs that may
+        // prevent that, so it doesn't hurt to try again here.
+        js_delete(allStrings);
     }
 
     bool initStrings(JSRuntime *rt);
 
-    // Add |other|'s numbers to this object's numbers.  The strings data isn't
-    // touched.
-    void addIgnoringStrings(const ZoneStats &other) {
-        ZoneStatsPod::add(other);
-    }
-
-    // Add |other|'s strings data to this object's strings data.  (We don't do
-    // anything with notableStrings.)
-    void addStrings(const ZoneStats &other) {
-        for (StringsHashMap::Range r = other.strings->all(); !r.empty(); r.popFront()) {
-            StringsHashMap::AddPtr p = strings->lookupForAdd(r.front().key());
-            if (p) {
-                // We've seen this string before; add its size to our tally.
-                p->value().add(r.front().value());
-            } else {
-                // We haven't seen this string before; add it to the hashtable.
-                strings->add(p, r.front().key(), r.front().value());
-            }
-        }
+    void addSizes(const ZoneStats &other) {
+        MOZ_ASSERT(isTotals);
+        FOR_EACH_SIZE(ADD_OTHER_SIZE)
+        stringInfo.add(other.stringInfo);
     }
 
     size_t sizeOfLiveGCThings() const {
-        size_t n = ZoneStatsPod::sizeOfLiveGCThings();
-        for (size_t i = 0; i < notableStrings.length(); i++) {
-            const JS::NotableStringInfo& info = notableStrings[i];
-            n += info.gcHeap;
-        }
+        MOZ_ASSERT(isTotals);
+        size_t n = 0;
+        FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
+        n += stringInfo.sizeOfLiveGCThings();
         return n;
     }
 
-    typedef js::HashMap<JSString*,
-                        StringInfo,
+    void addToTabSizes(JS::TabSizes *sizes) const {
+        MOZ_ASSERT(isTotals);
+        FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
+        stringInfo.addToTabSizes(sizes);
+    }
+
+    // These string measurements are initially for all strings.  At the end,
+    // if the measurement granularity is FineGrained, we subtract the
+    // measurements of the notable script sources and move them into
+    // |notableStrings|.
+    FOR_EACH_SIZE(DECL_SIZE)
+    StringInfo stringInfo;
+    void *extra;    // This field can be used by embedders.
+
+    typedef js::HashMap<JSString*, StringInfo,
                         js::InefficientNonFlatteningStringHashPolicy,
                         js::SystemAllocPolicy> StringsHashMap;
 
-    // |strings| is only used transiently.  During the zone traversal it is
+    // |allStrings| is only used transiently.  During the zone traversal it is
     // filled with info about every string in the zone.  It's then used to fill
     // in |notableStrings| (which actually gets reported), and immediately
     // discarded afterwards.
-    StringsHashMap *strings;
+    StringsHashMap *allStrings;
     js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
+    bool isTotals;
+
+#undef FOR_EACH_SIZE
 };
 
 struct CompartmentStats
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(Objects, IsLiveGCThing,  objectsGCHeapOrdinary) \
-    macro(Objects, IsLiveGCThing,  objectsGCHeapFunction) \
-    macro(Objects, IsLiveGCThing,  objectsGCHeapDenseArray) \
-    macro(Objects, IsLiveGCThing,  objectsGCHeapSlowArray) \
-    macro(Objects, IsLiveGCThing,  objectsGCHeapCrossCompartmentWrapper) \
     macro(Private, NotLiveGCThing, objectsPrivate) \
-    macro(Other,   IsLiveGCThing,  shapesGCHeapTreeGlobalParented) \
-    macro(Other,   IsLiveGCThing,  shapesGCHeapTreeNonGlobalParented) \
-    macro(Other,   IsLiveGCThing,  shapesGCHeapDict) \
-    macro(Other,   IsLiveGCThing,  shapesGCHeapBase) \
-    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeTables) \
-    macro(Other,   NotLiveGCThing, shapesMallocHeapDictTables) \
-    macro(Other,   NotLiveGCThing, shapesMallocHeapTreeShapeKids) \
-    macro(Other,   NotLiveGCThing, shapesMallocHeapCompartmentTables) \
     macro(Other,   IsLiveGCThing,  scriptsGCHeap) \
     macro(Other,   NotLiveGCThing, scriptsMallocHeapData) \
     macro(Other,   NotLiveGCThing, baselineData) \
     macro(Other,   NotLiveGCThing, baselineStubsFallback) \
     macro(Other,   NotLiveGCThing, baselineStubsOptimized) \
     macro(Other,   NotLiveGCThing, ionData) \
     macro(Other,   NotLiveGCThing, typeInferenceTypeScripts) \
     macro(Other,   NotLiveGCThing, typeInferenceAllocationSiteTables) \
     macro(Other,   NotLiveGCThing, typeInferenceArrayTypeTables) \
     macro(Other,   NotLiveGCThing, typeInferenceObjectTypeTables) \
     macro(Other,   NotLiveGCThing, compartmentObject) \
+    macro(Other,   NotLiveGCThing, compartmentTables) \
     macro(Other,   NotLiveGCThing, crossCompartmentWrappersTable) \
     macro(Other,   NotLiveGCThing, regexpCompartment) \
     macro(Other,   NotLiveGCThing, debuggeesSet)
 
     CompartmentStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
-        objectsExtra(),
-        extra()
+        classInfo(),
+        extra(),
+        allClasses(nullptr),
+        notableClasses(),
+        isTotals(true)
     {}
 
-    CompartmentStats(const CompartmentStats &other)
+    CompartmentStats(CompartmentStats &&other)
       : FOR_EACH_SIZE(COPY_OTHER_SIZE)
-        objectsExtra(other.objectsExtra),
-        extra(other.extra)
-    {}
+        classInfo(mozilla::Move(other.classInfo)),
+        extra(other.extra),
+        allClasses(other.allClasses),
+        notableClasses(mozilla::Move(other.notableClasses)),
+        isTotals(other.isTotals)
+    {
+        other.allClasses = nullptr;
+        MOZ_ASSERT(!other.isTotals);
+    }
 
-    void add(const CompartmentStats &other) {
+    ~CompartmentStats() {
+        // |allClasses| is usually deleted and set to nullptr before this
+        // destructor runs. But there are failure cases due to OOMs that may
+        // prevent that, so it doesn't hurt to try again here.
+        js_delete(allClasses);
+    }
+
+    bool initClasses(JSRuntime *rt);
+
+    void addSizes(const CompartmentStats &other) {
+        MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
-        objectsExtra.add(other.objectsExtra);
-        // Do nothing with |extra|.
+        classInfo.add(other.classInfo);
     }
 
     size_t sizeOfLiveGCThings() const {
+        MOZ_ASSERT(isTotals);
         size_t n = 0;
         FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
-        n += objectsExtra.sizeOfLiveGCThings();
-        // Do nothing with |extra|.
+        n += classInfo.sizeOfLiveGCThings();
         return n;
     }
 
     void addToTabSizes(TabSizes *sizes) const {
+        MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
-        objectsExtra.addToTabSizes(sizes);
-        // Do nothing with |extra|.
+        classInfo.addToTabSizes(sizes);
     }
 
+    // The class measurements in |classInfo| are initially for all classes.  At
+    // the end, if the measurement granularity is FineGrained, we subtract the
+    // measurements of the notable classes and move them into |notableClasses|.
     FOR_EACH_SIZE(DECL_SIZE)
-    ObjectsExtraSizes  objectsExtra;
-    void               *extra;  // This field can be used by embedders.
+    ClassInfo   classInfo;
+    void        *extra;     // This field can be used by embedders.
+
+    typedef js::HashMap<const char*, ClassInfo,
+                        js::CStringHashPolicy,
+                        js::SystemAllocPolicy> ClassesHashMap;
+
+    // These are similar to |allStrings| and |notableStrings| in ZoneStats.
+    ClassesHashMap *allClasses;
+    js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
+    bool isTotals;
 
 #undef FOR_EACH_SIZE
 };
 
 typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
 typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
 
 struct RuntimeStats
@@ -565,12 +703,14 @@ AddSizeOfTab(JSRuntime *rt, JS::HandleOb
              ObjectPrivateVisitor *opv, TabSizes *sizes);
 
 } // namespace JS
 
 #undef DECL_SIZE
 #undef ZERO_SIZE
 #undef COPY_OTHER_SIZE
 #undef ADD_OTHER_SIZE
+#undef SUB_OTHER_SIZE
+#undef ADD_SIZE_TO_N
 #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
 #undef ADD_TO_TAB_SIZES
 
 #endif /* js_MemoryMetrics_h */
--- a/js/src/assembler/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/assembler/jit/ExecutableAllocatorWin.cpp
@@ -23,16 +23,17 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "assembler/jit/ExecutableAllocator.h"
 
 #if ENABLE_ASSEMBLER && WTF_OS_WINDOWS
 
 #include "jswin.h"
+#include "mozilla/WindowsVersion.h"
 
 extern uint64_t random_next(uint64_t *, int);
 
 namespace JSC {
 
 uint64_t ExecutableAllocator::rngSeed;
 
 size_t ExecutableAllocator::determinePageSize()
@@ -67,26 +68,18 @@ void *ExecutableAllocator::computeRandom
 #endif
     uint64_t rand = random_next(&rngSeed, 32) << chunkBits;
     return (void *) (base | rand & mask);
 }
 
 static bool
 RandomizeIsBrokenImpl()
 {
-    OSVERSIONINFO osvi;
-    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
-    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-
-    GetVersionEx(&osvi);
-
-    // Version number mapping is available at:
-    // http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx
     // We disable everything before Vista, for now.
-    return osvi.dwMajorVersion <= 5;
+    return !mozilla::IsVistaOrLater();
 }
 
 static bool
 RandomizeIsBroken()
 {
     // Use the compiler's intrinsic guards for |static type value = expr| to avoid some potential
     // races if runtimes are created from multiple threads.
     static int result = RandomizeIsBrokenImpl();
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -315,17 +315,17 @@ EvalKernel(JSContext *cx, const CallArgs
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setPrincipals(principals)
                .setOriginPrincipals(originPrincipals)
-               .setIntroductionInfo(introducerFilename, "eval", lineno, pcOffset);
+               .setIntroductionInfo(introducerFilename, "eval", lineno, script, pcOffset);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
                                                      chars.get(), length, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
@@ -387,17 +387,17 @@ js::DirectEvalStringFromIon(JSContext *c
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setPrincipals(principals)
                .setOriginPrincipals(originPrincipals)
-               .setIntroductionInfo(introducerFilename, "eval", lineno, pcOffset);
+               .setIntroductionInfo(introducerFilename, "eval", lineno, script, pcOffset);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
                                                      chars.get(), length, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -242,33 +242,33 @@ MarkIfGCThingWord(JSTracer *trc, uintptr
     if (trc->runtime->gcIncrementalState == MARK_ROOTS)
         trc->runtime->mainThread.gcSavedRoots.append(
             PerThreadData::SavedGCRoot(thing, traceKind));
 #endif
 
     return CGCT_VALID;
 }
 
+#ifndef JSGC_USE_EXACT_ROOTING
 static void
 MarkWordConservatively(JSTracer *trc, uintptr_t w)
 {
     /*
      * The conservative scanner may access words that valgrind considers as
      * undefined. To avoid false positives and not to alter valgrind view of
      * the memory we make as memcheck-defined the argument, a copy of the
      * original word. See bug 572678.
      */
 #ifdef MOZ_VALGRIND
     JS_SILENCE_UNUSED_VALUE_IN_EXPR(VALGRIND_MAKE_MEM_DEFINED(&w, sizeof(w)));
 #endif
 
     MarkIfGCThingWord(trc, w);
 }
 
-#ifndef JSGC_USE_EXACT_ROOTING
 MOZ_ASAN_BLACKLIST
 static void
 MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end)
 {
     JS_ASSERT(begin <= end);
     for (const uintptr_t *i = begin; i < end; ++i)
         MarkWordConservatively(trc, *i);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Source-introductionScript-01.js
@@ -0,0 +1,118 @@
+// Dynamically generated sources should have their introduction script and
+// offset set correctly.
+
+var g = newGlobal();
+var dbg = new Debugger;
+var gDO = dbg.addDebuggee(g);
+var log;
+
+// Direct eval, while the frame is live.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  var source = frame.script.source;
+  var introducer = frame.older;
+  assertEq(source.introductionScript, introducer.script);
+  assertEq(source.introductionOffset, introducer.offset);
+};
+log = '';
+g.eval('\n\neval("\\n\\ndebugger;");');
+assertEq(log, 'd');
+
+// Direct eval, after the frame has been popped.
+var introducer, introduced;
+dbg.onDebuggerStatement = function (frame) {
+  log += 'de1';
+  introducer = frame.script;
+  dbg.onDebuggerStatement = function (frame) {
+    log += 'de2';
+    introduced = frame.script.source;
+  };
+};
+log = '';
+g.evaluate('debugger; eval("\\n\\ndebugger;");', { lineNumber: 1812 });
+assertEq(log, 'de1de2');
+assertEq(introduced.introductionScript, introducer);
+assertEq(introducer.getOffsetLine(introduced.introductionOffset), 1812);
+
+// Indirect eval, while the frame is live.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  var source = frame.script.source;
+  var introducer = frame.older;
+  assertEq(source.introductionScript, introducer.script);
+  assertEq(source.introductionOffset, introducer.offset);
+};
+log = '';
+g.eval('\n\n(0,eval)("\\n\\ndebugger;");');
+assertEq(log, 'd');
+
+// Indirect eval, after the frame has been popped.
+var introducer, introduced;
+dbg.onDebuggerStatement = function (frame) {
+  log += 'de1';
+  introducer = frame.script;
+  dbg.onDebuggerStatement = function (frame) {
+    log += 'de2';
+    introduced = frame.script.source;
+  };
+};
+log = '';
+g.evaluate('debugger; (0,eval)("\\n\\ndebugger;");', { lineNumber: 1066 });
+assertEq(log, 'de1de2');
+assertEq(introduced.introductionScript, introducer);
+assertEq(introducer.getOffsetLine(introduced.introductionOffset), 1066);
+
+// Function constructor.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'o';
+  var outerScript = frame.script;
+  var outerOffset = frame.offset;
+  dbg.onDebuggerStatement = function (frame) {
+    log += 'i';
+    var source = frame.script.source;
+    assertEq(source.introductionScript, outerScript);
+    assertEq(outerScript.getOffsetLine(source.introductionOffset),
+             outerScript.getOffsetLine(outerOffset));
+  };
+};
+log = '';
+g.eval('\n\n\ndebugger; Function("debugger;")()');
+assertEq(log, 'oi');
+
+// Function constructor, after the the introduction call's frame has been
+// popped.
+var introducer;
+dbg.onDebuggerStatement = function (frame) {
+  log += 'F2';
+  introducer = frame.script;
+};
+log = '';
+var fDO = gDO.evalInGlobal('debugger; Function("origami;")', { lineNumber: 1685 }).return;
+var source = fDO.script.source;
+assertEq(log, 'F2');
+assertEq(source.introductionScript, introducer);
+assertEq(introducer.getOffsetLine(source.introductionOffset), 1685);
+
+// If the introduction script is in a different global from the script it
+// introduced, we don't record it.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'x';
+  var source = frame.script.source;
+  assertEq(source.introductionScript, undefined);
+  assertEq(source.introductionOffset, undefined);
+};
+log = '';
+g.eval('debugger;'); // introduction script is this top-level script
+assertEq(log, 'x');
+
+// If the code is introduced by a function that doesn't provide
+// introduction information, that shouldn't be a problem.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'x';
+  var source = frame.script.source;
+  assertEq(source.introductionScript, undefined);
+  assertEq(source.introductionOffset, undefined);
+};
+log = '';
+g.eval('evaluate("debugger;", { lineNumber: 1729 });');
+assertEq(log, 'x');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Source-introductionScript-02.js
@@ -0,0 +1,31 @@
+// Calls to 'eval', etc. by JS primitives get attributed to the point of
+// the primitive's call.
+
+var g = newGlobal();
+var dbg = new Debugger;
+var gDO = dbg.addDebuggee(g);
+var log = '';
+
+function outerHandler(frame) {
+  log += 'o';
+  var outerScript = frame.script;
+
+  dbg.onDebuggerStatement = function (frame) {
+    log += 'i';
+    var source = frame.script.source;
+    var introScript = source.introductionScript;
+    assertEq(introScript, outerScript);
+    assertEq(introScript.getOffsetLine(source.introductionOffset), 1234);
+  };
+};
+
+log = '';
+dbg.onDebuggerStatement = outerHandler;
+g.evaluate('debugger; ["debugger;"].map(eval)', { lineNumber: 1234 });
+assertEq(log, 'oi');
+
+log = '';
+dbg.onDebuggerStatement = outerHandler;
+g.evaluate('debugger; "debugger;".replace(/.*/, eval);',
+           { lineNumber: 1234 });
+assertEq(log, 'oi');
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4321,17 +4321,18 @@ JS::ReadOnlyCompileOptions::originPrinci
 {
     return NormalizeOriginPrincipals(principals_, originPrincipals_);
 }
 
 JS::OwningCompileOptions::OwningCompileOptions(JSContext *cx)
     : ReadOnlyCompileOptions(),
       runtime(GetRuntime(cx)),
       elementRoot(cx),
-      elementAttributeNameRoot(cx)
+      elementAttributeNameRoot(cx),
+      introductionScriptRoot(cx)
 {
 }
 
 JS::OwningCompileOptions::~OwningCompileOptions()
 {
     if (principals_)
         JS_DropPrincipals(runtime, principals_);
     if (originPrincipals_)
@@ -4347,16 +4348,17 @@ bool
 JS::OwningCompileOptions::copy(JSContext *cx, const ReadOnlyCompileOptions &rhs)
 {
     copyPODOptions(rhs);
 
     setPrincipals(rhs.principals());
     setOriginPrincipals(rhs.originPrincipals());
     setElement(rhs.element());
     setElementAttributeName(rhs.elementAttributeName());
+    setIntroductionScript(rhs.introductionScript());
 
     return (setFileAndLine(cx, rhs.filename(), rhs.lineno) &&
             setSourceMapURL(cx, rhs.sourceMapURL()) &&
             setIntroducerFilename(cx, rhs.introducerFilename()));
 }
 
 bool
 JS::OwningCompileOptions::setFile(JSContext *cx, const char *f)
@@ -4423,21 +4425,33 @@ bool
 JS::OwningCompileOptions::wrap(JSContext *cx, JSCompartment *compartment)
 {
     if (!compartment->wrap(cx, &elementRoot))
         return false;
     if (elementAttributeNameRoot) {
         if (!compartment->wrap(cx, elementAttributeNameRoot.address()))
             return false;
     }
+
+    // There is no equivalent of cross-compartment wrappers for scripts. If
+    // the introduction script would be in a different compartment from the
+    // compiled code, we would be creating a cross-compartment script
+    // reference, which would be bogus. In that case, just don't bother to
+    // retain the introduction script.
+    if (introductionScriptRoot) {
+        if (introductionScriptRoot->compartment() != compartment)
+            introductionScriptRoot = nullptr;
+    }
+
     return true;
 }
 
 JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
-    : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx)
+    : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
+      introductionScriptRoot(cx)
 {
     this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
 
     compileAndGo = false;
     noScriptRval = cx->options().noScriptRval();
     strictOption = cx->options().strictMode();
     extraWarningsOption = cx->options().extraWarnings();
     werrorOption = cx->options().werror();
@@ -4448,16 +4462,27 @@ bool
 JS::CompileOptions::wrap(JSContext *cx, JSCompartment *compartment)
 {
     if (!compartment->wrap(cx, &elementRoot))
         return false;
     if (elementAttributeNameRoot) {
         if (!compartment->wrap(cx, elementAttributeNameRoot.address()))
             return false;
     }
+
+    // There is no equivalent of cross-compartment wrappers for scripts. If
+    // the introduction script would be in a different compartment from the
+    // compiled code, we would be creating a cross-compartment script
+    // reference, which would be bogus. In that case, just don't bother to
+    // retain the introduction script.
+    if (introductionScriptRoot) {
+        if (introductionScriptRoot->compartment() != compartment)
+            introductionScriptRoot = nullptr;
+    }
+
     return true;
 }
 
 JSScript *
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
             const jschar *chars, size_t length)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3506,16 +3506,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     // depends on the derived type.
     JSPrincipals *principals() const { return principals_; }
     JSPrincipals *originPrincipals() const;
     const char *filename() const { return filename_; }
     const char *introducerFilename() const { return introducerFilename_; }
     const jschar *sourceMapURL() const { return sourceMapURL_; }
     virtual JSObject *element() const = 0;
     virtual JSString *elementAttributeName() const = 0;
+    virtual JSScript *introductionScript() const = 0;
 
     // POD options.
     JSVersion version;
     bool versionSet;
     bool utf8;
     unsigned lineno;
     unsigned column;
     bool compileAndGo;
@@ -3563,27 +3564,29 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
  * comes to refer to the object that owns this, then the whole cycle, and
  * anything else it entrains, will never be freed.
  */
 class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
 {
     JSRuntime *runtime;
     PersistentRootedObject elementRoot;
     PersistentRootedString elementAttributeNameRoot;
+    PersistentRootedScript introductionScriptRoot;
 
   public:
     // A minimal constructor, for use with OwningCompileOptions::copy. This
     // leaves |this.version| set to JSVERSION_UNKNOWN; the instance
     // shouldn't be used until we've set that to something real (as |copy|
     // will).
     explicit OwningCompileOptions(JSContext *cx);
     ~OwningCompileOptions();
 
     JSObject *element() const MOZ_OVERRIDE { return elementRoot; }
     JSString *elementAttributeName() const MOZ_OVERRIDE { return elementAttributeNameRoot; }
+    JSScript *introductionScript() const MOZ_OVERRIDE { return introductionScriptRoot; }
 
     // Set this to a copy of |rhs|. Return false on OOM.
     bool copy(JSContext *cx, const ReadOnlyCompileOptions &rhs);
 
     /* These setters make copies of their string arguments, and are fallible. */
     bool setFile(JSContext *cx, const char *f);
     bool setFileAndLine(JSContext *cx, const char *f, unsigned l);
     bool setSourceMapURL(JSContext *cx, const jschar *s);
@@ -3594,16 +3597,20 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setElement(JSObject *e) {
         elementRoot = e;
         return *this;
     }
     OwningCompileOptions &setElementAttributeName(JSString *p) {
         elementAttributeNameRoot = p;
         return *this;
     }
+    OwningCompileOptions &setIntroductionScript(JSScript *s) {
+        introductionScriptRoot = s;
+        return *this;
+    }
     OwningCompileOptions &setPrincipals(JSPrincipals *p) {
         if (p) JS_HoldPrincipals(p);
         if (principals_) JS_DropPrincipals(runtime, principals_);
         principals_ = p;
         return *this;
     }
     OwningCompileOptions &setOriginPrincipals(JSPrincipals *p) {
         if (p) JS_HoldPrincipals(p);
@@ -3621,22 +3628,23 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     OwningCompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
     OwningCompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     bool setIntroductionInfo(JSContext *cx, const char *introducerFn, const char *intro,
-                             unsigned line, uint32_t offset)
+                             unsigned line, JSScript *script, uint32_t offset)
     {
         if (!setIntroducerFilename(cx, introducerFn))
             return false;
         introductionType = intro;
         introductionLineno = line;
+        introductionScriptRoot = script;
         introductionOffset = offset;
         hasIntroductionInfo = true;
         return true;
     }
 
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
 };
 
@@ -3646,46 +3654,54 @@ class JS_FRIEND_API(OwningCompileOptions
  * filename; source map URL) that are owned by something else. If you
  * create an instance of this type, it's up to you to guarantee that
  * everything you store in it will outlive it.
  */
 class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) : public ReadOnlyCompileOptions
 {
     RootedObject elementRoot;
     RootedString elementAttributeNameRoot;
+    RootedScript introductionScriptRoot;
 
   public:
     explicit CompileOptions(JSContext *cx, JSVersion version = JSVERSION_UNKNOWN);
     CompileOptions(js::ContextFriendFields *cx, const ReadOnlyCompileOptions &rhs)
-      : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx)
+      : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
+        introductionScriptRoot(cx)
     {
         copyPODOptions(rhs);
 
         principals_ = rhs.principals();
         originPrincipals_ = rhs.originPrincipals();
         filename_ = rhs.filename();
         sourceMapURL_ = rhs.sourceMapURL();
         elementRoot = rhs.element();
         elementAttributeNameRoot = rhs.elementAttributeName();
+        introductionScriptRoot = rhs.introductionScript();
     }
 
     JSObject *element() const MOZ_OVERRIDE { return elementRoot; }
     JSString *elementAttributeName() const MOZ_OVERRIDE { return elementAttributeNameRoot; }
+    JSScript *introductionScript() const MOZ_OVERRIDE { return introductionScriptRoot; }
 
     CompileOptions &setFile(const char *f) { filename_ = f; return *this; }
     CompileOptions &setLine(unsigned l) { lineno = l; return *this; }
     CompileOptions &setFileAndLine(const char *f, unsigned l) {
         filename_ = f; lineno = l; return *this;
     }
     CompileOptions &setSourceMapURL(const jschar *s) { sourceMapURL_ = s;       return *this; }
     CompileOptions &setElement(JSObject *e)          { elementRoot = e;         return *this; }
     CompileOptions &setElementAttributeName(JSString *p) {
         elementAttributeNameRoot = p;
         return *this;
     }
+    CompileOptions &setIntroductionScript(JSScript *s) {
+        introductionScriptRoot = s;
+        return *this;
+    }
     CompileOptions &setPrincipals(JSPrincipals *p) {
         principals_ = p;
         return *this;
     }
     CompileOptions &setOriginPrincipals(JSPrincipals *p) {
         originPrincipals_ = p;
         return *this;
     }
@@ -3699,21 +3715,22 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
     CompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     CompileOptions &setIntroductionInfo(const char *introducerFn, const char *intro,
-                                        unsigned line, uint32_t offset)
+                                        unsigned line, JSScript *script, uint32_t offset)
     {
         introducerFilename_ = introducerFn;
         introductionType = intro;
         introductionLineno = line;
+        introductionScriptRoot = script;
         introductionOffset = offset;
         hasIntroductionInfo = true;
         return *this;
     }
 
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
 };
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -993,17 +993,17 @@ ArrayJoinKernel(JSContext *cx, Separator
 
             if (elem.isString()) {
                 if (!sb.append(elem.toString()))
                     return false;
             } else if (elem.isNumber()) {
                 if (!NumberValueToStringBuffer(cx, elem, sb))
                     return false;
             } else if (elem.isBoolean()) {
-                if (!BooleanToStringBuffer(cx, elem.toBoolean(), sb))
+                if (!BooleanToStringBuffer(elem.toBoolean(), sb))
                     return false;
             } else if (elem.isObject()) {
                 /*
                  * Object stringifying could modify the initialized length or make
                  * the array sparse. Delegate it to a separate loop to keep this
                  * one tight.
                  */
                 break;
@@ -1426,17 +1426,17 @@ NumDigitsBase10(uint32_t n)
      * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
      */
     uint32_t log2 = CeilingLog2(n);
     uint32_t t = log2 * 1233 >> 12;
     return t - (n < powersOf10[t]) + 1;
 }
 
 static inline bool
-CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
+CompareLexicographicInt32(const Value &a, const Value &b, bool *lessOrEqualp)
 {
     int32_t aint = a.toInt32();
     int32_t bint = b.toInt32();
 
     /*
      * If both numbers are equal ... trivial
      * If only one of both is negative --> arithmetic comparison as char code
      * of '-' is always less than any other digit
@@ -1499,23 +1499,18 @@ struct SortComparatorStrings
 
     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
         return CompareStringValues(cx, a, b, lessOrEqualp);
     }
 };
 
 struct SortComparatorLexicographicInt32
 {
-    JSContext   *const cx;
-
-    SortComparatorLexicographicInt32(JSContext *cx)
-      : cx(cx) {}
-
     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
-        return CompareLexicographicInt32(cx, a, b, lessOrEqualp);
+        return CompareLexicographicInt32(a, b, lessOrEqualp);
     }
 };
 
 struct StringifiedElement
 {
     size_t charsBegin;
     size_t charsEnd;
     size_t elementIndex;
@@ -1935,17 +1930,17 @@ js::array_sort(JSContext *cx, unsigned a
              */
             if (allStrings) {
                 JS_ALWAYS_TRUE(vec.resize(n * 2));
                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
                     return false;
             } else if (allInts) {
                 JS_ALWAYS_TRUE(vec.resize(n * 2));
                 if (!MergeSort(vec.begin(), n, vec.begin() + n,
-                               SortComparatorLexicographicInt32(cx))) {
+                               SortComparatorLexicographicInt32())) {
                     return false;
                 }
             } else {
                 if (!SortLexicographically(cx, &vec, n))
                     return false;
             }
         } else {
             ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -48,17 +48,17 @@ MOZ_ALWAYS_INLINE bool
 bool_toSource_impl(JSContext *cx, CallArgs args)
 {
     HandleValue thisv = args.thisv();
     JS_ASSERT(IsBoolean(thisv));
 
     bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().as<BooleanObject>().unbox();
 
     StringBuffer sb(cx);
-    if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(cx, b, sb) || !sb.append("))"))
+    if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(b, sb) || !sb.append("))"))
         return false;
 
     JSString *str = sb.finishString();
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
@@ -191,14 +191,14 @@ js::ToBooleanSlow(HandleValue v)
     return !EmulatesUndefined(&v.toObject());
 }
 
 /*
  * This slow path is only ever taken for proxies wrapping Boolean objects
  * The only caller of the fast path, JSON's PreprocessValue, ensures that.
  */
 bool
-js::BooleanGetPrimitiveValueSlow(HandleObject wrappedBool, JSContext *cx)
+js::BooleanGetPrimitiveValueSlow(HandleObject wrappedBool)
 {
     JSObject *obj = wrappedBool->as<ProxyObject>().target();
     JS_ASSERT(obj);
     return obj->as<BooleanObject>().unbox();
 }
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -17,13 +17,13 @@ extern JSObject *
 js_InitBooleanClass(JSContext *cx, js::HandleObject obj);
 
 extern JSString *
 js_BooleanToString(js::ExclusiveContext *cx, bool b);
 
 namespace js {
 
 inline bool
-BooleanGetPrimitiveValue(HandleObject obj, JSContext *cx);
+BooleanGetPrimitiveValue(HandleObject obj);
 
 } /* namespace js */
 
 #endif /* jsbool_h */
--- a/js/src/jsboolinlines.h
+++ b/js/src/jsboolinlines.h
@@ -10,25 +10,25 @@
 #include "jsbool.h"
 
 #include "vm/BooleanObject.h"
 #include "vm/WrapperObject.h"
 
 namespace js {
 
 bool
-BooleanGetPrimitiveValueSlow(HandleObject, JSContext *);
+BooleanGetPrimitiveValueSlow(HandleObject);
 
 inline bool
-BooleanGetPrimitiveValue(HandleObject obj, JSContext *cx)
+BooleanGetPrimitiveValue(HandleObject obj)
 {
     if (obj->is<BooleanObject>())
         return obj->as<BooleanObject>().unbox();
 
-    return BooleanGetPrimitiveValueSlow(obj, cx);
+    return BooleanGetPrimitiveValueSlow(obj);
 }
 
 inline bool
 EmulatesUndefined(JSObject *obj)
 {
     JSObject *actual = MOZ_LIKELY(!obj->is<WrapperObject>()) ? obj : UncheckedUnwrap(obj);
     return actual->getClass()->emulatesUndefined();
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -907,29 +907,29 @@ JSCompartment::clearTraps(FreeOp *fop)
 }
 
 void
 JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                       size_t *tiAllocationSiteTables,
                                       size_t *tiArrayTypeTables,
                                       size_t *tiObjectTypeTables,
                                       size_t *compartmentObject,
-                                      size_t *shapesCompartmentTables,
+                                      size_t *compartmentTables,
                                       size_t *crossCompartmentWrappersArg,
                                       size_t *regexpCompartment,
                                       size_t *debuggeesSet,
                                       size_t *baselineStubsOptimized)
 {
     *compartmentObject += mallocSizeOf(this);
     types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
                                  tiArrayTypeTables, tiObjectTypeTables);
-    *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
-                              + initialShapes.sizeOfExcludingThis(mallocSizeOf)
-                              + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
-                              + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
+    *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
+                        + initialShapes.sizeOfExcludingThis(mallocSizeOf)
+                        + newTypeObjects.sizeOfExcludingThis(mallocSizeOf)
+                        + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf);
     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
     *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
     *debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf);
 #ifdef JS_ION
     if (jitCompartment()) {
         *baselineStubsOptimized +=
             jitCompartment()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
     }
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -221,17 +221,17 @@ struct JSCompartment
     bool                         globalWriteBarriered;
 
   public:
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t *tiAllocationSiteTables,
                                 size_t *tiArrayTypeTables,
                                 size_t *tiObjectTypeTables,
                                 size_t *compartmentObject,
-                                size_t *shapesCompartmentTables,
+                                size_t *compartmentTables,
                                 size_t *crossCompartmentWrappers,
                                 size_t *regexpCompartment,
                                 size_t *debuggeesSet,
                                 size_t *baselineStubsOptimized);
 
     /*
      * Shared scope property tree, and arena-pool for allocating its nodes.
      */
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -546,17 +546,17 @@ GetAnyCompartmentInZone(JS::Zone *zone);
 namespace shadow {
 
 struct TypeObject {
     const Class *clasp;
     JSObject    *proto;
 };
 
 struct BaseShape {
-    const js::Class *clasp;
+    const js::Class *clasp_;
     JSObject *parent;
     JSObject *_1;
     JSCompartment *compartment;
 };
 
 class Shape {
 public:
     shadow::BaseShape *base;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1527,17 +1527,17 @@ FunctionConstructor(JSContext *cx, unsig
         introducerFilename = script->scriptSource()->introducerFilename();
 
     CompileOptions options(cx);
     options.setPrincipals(principals)
            .setOriginPrincipals(originPrincipals)
            .setFileAndLine(filename, 1)
            .setNoScriptRval(false)
            .setCompileAndGo(true)
-           .setIntroductionInfo(introducerFilename, introductionType, lineno, pcOffset);
+           .setIntroductionInfo(introducerFilename, introductionType, lineno, script, pcOffset);
 
     unsigned n = args.length() ? args.length() - 1 : 0;
     if (n > 0) {
         /*
          * Collect the function-argument arguments into one string, separated
          * by commas, then make a tokenstream from that string, and scan it to
          * get the arguments.  We need to throw the full scanner at the
          * problem, because the argument string can legitimately contain
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5889,33 +5889,33 @@ js_DumpBacktrace(JSContext *cx)
         JSScript *script = i.script();
         sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
                         depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
                         script, script->pcToOffset(i.pc()));
     }
     fprintf(stdout, "%s", sprinter.string());
 }
 void
-JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
+JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info)
 {
     if (hasDynamicSlots())
-        sizes->mallocHeapSlots += mallocSizeOf(slots);
+        info->objectsMallocHeapSlots += mallocSizeOf(slots);
 
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
         if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer())) {
 #if defined (JS_CPU_X64)
             // On x64, ArrayBufferObject::prepareForAsmJS switches the
             // ArrayBufferObject to use mmap'd storage.
-            sizes->nonHeapElementsAsmJS += as<ArrayBufferObject>().byteLength();
+            info->objectsNonHeapElementsAsmJS += as<ArrayBufferObject>().byteLength();
 #else
-            sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements);
+            info->objectsMallocHeapElementsAsmJS += mallocSizeOf(elements);
 #endif
         } else {
-            sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
+            info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
         }
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
     if (is<JSFunction>() ||
         is<JSObject>() ||
         is<ArrayObject>() ||
         is<CallObject>() ||
@@ -5928,26 +5928,26 @@ JSObject::addSizeOfExcludingThis(mozilla
         // - (53.7%, 53.7%): Function
         // - (18.0%, 71.7%): Object
         // - (16.9%, 88.6%): Array
         // - ( 3.9%, 92.5%): Call
         // - ( 2.8%, 95.3%): RegExp
         // - ( 1.0%, 96.4%): Proxy
 
     } else if (is<ArgumentsObject>()) {
-        sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
+        info->objectsMallocHeapMisc += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
     } else if (is<RegExpStaticsObject>()) {
-        sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
+        info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
     } else if (is<PropertyIteratorObject>()) {
-        sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
+        info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
 #ifdef JS_ION
     } else if (is<AsmJSModuleObject>()) {
-        as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
-                                              &sizes->mallocHeapAsmJSModuleData);
+        as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
+                                              &info->objectsMallocHeapMisc);
 #endif
 #ifdef JS_HAS_CTYPES
     } else {
         // This must be the last case.
-        sizes->mallocHeapCtypesData +=
+        info->objectsMallocHeapMisc +=
             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
 #endif
     }
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -20,17 +20,17 @@
 
 #include "gc/Marking.h"
 #include "js/GCAPI.h"
 #include "vm/ObjectImpl.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 
 namespace JS {
-struct ObjectsExtraSizes;
+struct ClassInfo;
 }
 
 namespace js {
 
 class AutoPropDescArrayRooter;
 struct GCMarker;
 struct NativeIterator;
 class Nursery;
@@ -349,17 +349,17 @@ class JSObject : public js::ObjectImpl
     uint32_t propertyCount() const {
         return lastProperty()->entryCount();
     }
 
     bool hasShapeTable() const {
         return lastProperty()->hasTable();
     }
 
-    void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes);
+    void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info);
 
     bool hasIdempotentProtoChain() const;
 
     // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
     // size classes will give an object.
     static const uint32_t MAX_FIXED_SLOTS = 16;
 
   public:
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -245,17 +245,17 @@ PreprocessValue(JSContext *cx, HandleObj
                 return false;
             vp.set(NumberValue(d));
         } else if (ObjectClassIs(obj, ESClass_String, cx)) {
             JSString *str = ToStringSlow<CanGC>(cx, vp);
             if (!str)
                 return false;
             vp.set(StringValue(str));
         } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) {
-            vp.setBoolean(BooleanGetPrimitiveValue(obj, cx));
+            vp.setBoolean(BooleanGetPrimitiveValue(obj));
         }
     }
 
     return true;
 }
 
 /*
  * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1265,16 +1265,41 @@ const Value &
 ScriptSourceObject::elementAttributeName() const
 {
     const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT);
     JS_ASSERT(prop.isUndefined() || prop.isString());
     return prop;
 }
 
 void
+ScriptSourceObject::initIntroductionScript(JSScript *script)
+{
+    JS_ASSERT(!getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate());
+
+    // There is no equivalent of cross-compartment wrappers for scripts. If
+    // the introduction script would be in a different compartment from the
+    // compiled code, we would be creating a cross-compartment script
+    // reference, which would be bogus. In that case, just don't bother to
+    // retain the introduction script.
+    if (script && script->compartment() == compartment())
+        setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
+}
+
+void
+ScriptSourceObject::trace(JSTracer *trc, JSObject *obj)
+{
+    ScriptSourceObject *sso = static_cast<ScriptSourceObject *>(obj);
+
+    if (JSScript *script = sso->introductionScript()) {
+        MarkScriptUnbarriered(trc, &script, "ScriptSourceObject introductionScript");
+        sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
+    }
+}
+
+void
 ScriptSourceObject::finalize(FreeOp *fop, JSObject *obj)
 {
     // ScriptSource::setSource automatically takes care of the refcount
     obj->as<ScriptSourceObject>().setSource(nullptr);
 }
 
 const Class ScriptSourceObject::class_ = {
     "ScriptSource",
@@ -1282,17 +1307,21 @@ const Class ScriptSourceObject::class_ =
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,        /* addProperty */
     JS_DeletePropertyStub,  /* delProperty */
     JS_PropertyStub,        /* getProperty */
     JS_StrictPropertyStub,  /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    ScriptSourceObject::finalize
+    finalize,
+    nullptr,                /* call        */
+    nullptr,                /* hasInstance */
+    nullptr,                /* construct   */
+    trace
 };
 
 ScriptSourceObject *
 ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source,
                            const ReadOnlyCompileOptions &options)
 {
     RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr, cx->global()));
     if (!object)
@@ -1302,16 +1331,19 @@ ScriptSourceObject::create(ExclusiveCont
     source->incref();
     sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source));
     sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element()));
     if (options.elementAttributeName())
         sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName()));
     else
         sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue());
 
+    sourceObject->initSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(nullptr));
+    sourceObject->initIntroductionScript(options.introductionScript());
+
     return sourceObject;
 }
 
 static const unsigned char emptySource[] = "";
 
 /* Adjust the amount of memory this script source uses for source data,
    reallocating if needed. */
 bool
@@ -1617,27 +1649,28 @@ ScriptSource::destroy()
     js_free(displayURL_);
     js_free(sourceMapURL_);
     if (originPrincipals_)
         JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_);
     ready_ = false;
     js_free(this);
 }
 
-size_t
-ScriptSource::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+void
+ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                     JS::ScriptSourceInfo *info) const
 {
-    // |data| is a union, but both members are pointers to allocated memory,
-    // |emptySource|, or nullptr, so just using |data.compressed| will work.
-    size_t n = mallocSizeOf(this);
-    n += (ready() && data.compressed != emptySource)
-       ? mallocSizeOf(data.compressed)
-       : 0;
-    n += mallocSizeOf(filename_);
-    return n;
+    if (ready() && data.compressed != emptySource) {
+        if (compressed())
+            info->compressed += mallocSizeOf(data.compressed);
+        else
+            info->uncompressed += mallocSizeOf(data.source);
+    }
+    info->misc += mallocSizeOf(this) + mallocSizeOf(filename_);
+    info->numScripts++;
 }
 
 template<XDRMode mode>
 bool
 ScriptSource::performXDR(XDRState<mode> *xdr)
 {
     uint8_t hasSource = hasSourceData();
     if (!xdr->codeUint8(&hasSource))
@@ -1789,17 +1822,17 @@ ScriptSource::initFromOptions(ExclusiveC
     JS_ASSERT(!filename_);
     JS_ASSERT(!introducerFilename_);
 
     originPrincipals_ = options.originPrincipals();
     if (originPrincipals_)
         JS_HoldPrincipals(originPrincipals_);
 
     introductionType_ = options.introductionType;
-    introductionOffset_ = options.introductionOffset;
+    setIntroductionOffset(options.introductionOffset);
 
     if (options.hasIntroductionInfo) {
         JS_ASSERT(options.introductionType != nullptr);
         const char *filename = options.filename() ? options.filename() : "<unknown>";
         char *formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno,
                                                    options.introductionType);
         if (!formatted)
             return false;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -20,16 +20,20 @@
 #include "jsopcode.h"
 #include "jstypes.h"
 
 #include "gc/Barrier.h"
 #include "gc/Rooting.h"
 #include "jit/IonCode.h"
 #include "vm/Shape.h"
 
+namespace JS {
+struct ScriptSourceInfo;
+}
+
 namespace js {
 
 namespace jit {
     struct BaselineScript;
     struct IonScriptCounts;
 }
 
 # define ION_DISABLED_SCRIPT ((js::jit::IonScript *)0x1)
@@ -477,17 +481,18 @@ class ScriptSource
         return length_;
     }
     bool argumentsNotIncluded() const {
         JS_ASSERT(hasSourceData());
         return argumentsNotIncluded_;
     }
     const jschar *chars(JSContext *cx, const SourceDataCache::AutoSuppressPurge &asp);
     JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop);
-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+    void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                JS::ScriptSourceInfo *info) const;
 
     // XDR handling
     template <XDRMode mode>
     bool performXDR(XDRState<mode> *xdr);
 
     bool setFilename(ExclusiveContext *cx, const char *filename);
     const char *introducerFilename() const {
         return introducerFilename_;
@@ -552,35 +557,43 @@ class ScriptSourceHolder
     }
 };
 
 class ScriptSourceObject : public JSObject
 {
   public:
     static const Class class_;
 
+    static void trace(JSTracer *trc, JSObject *obj);
     static void finalize(FreeOp *fop, JSObject *obj);
     static ScriptSourceObject *create(ExclusiveContext *cx, ScriptSource *source,
                                       const ReadOnlyCompileOptions &options);
 
     ScriptSource *source() {
         return static_cast<ScriptSource *>(getReservedSlot(SOURCE_SLOT).toPrivate());
     }
 
     void setSource(ScriptSource *source);
 
     JSObject *element() const;
     void initElement(HandleObject element);
     const Value &elementAttributeName() const;
 
+    JSScript *introductionScript() const {
+        void *untyped = getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate();
+        return static_cast<JSScript *>(untyped);
+    }
+    void initIntroductionScript(JSScript *script);
+
   private:
     static const uint32_t SOURCE_SLOT = 0;
     static const uint32_t ELEMENT_SLOT = 1;
     static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
-    static const uint32_t RESERVED_SLOTS = 3;
+    static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
+    static const uint32_t RESERVED_SLOTS = 4;
 };
 
 enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
 
 static inline unsigned
 GeneratorKindAsBits(GeneratorKind generatorKind) {
     return static_cast<unsigned>(generatorKind);
 }
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -172,31 +172,34 @@ static const JSClass workerGlobalClass =
     JS_ConvertStub,   nullptr
 };
 
 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const jschar *chars, size_t length, JSObject *scopeChain,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain),
-    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx), callback(callback),
-    callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false)
+    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
+    optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
+    script(nullptr), errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
     if (!this->options.copy(cx, options))
         return false;
 
     // Save those compilation options that the ScriptSourceObject can't
     // point at while it's in the compilation's temporary compartment.
     optionsElement = this->options.element();
     this->options.setElement(nullptr);
+    optionsIntroductionScript = this->options.introductionScript();
+    this->options.setIntroductionScript(nullptr);
 
     return true;
 }
 
 void
 ParseTask::activate(JSRuntime *rt)
 {
     rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
@@ -206,16 +209,17 @@ ParseTask::activate(JSRuntime *rt)
 void
 ParseTask::finish()
 {
     if (script) {
         // Initialize the ScriptSourceObject slots that we couldn't while the SSO
         // was in the temporary compartment.
         ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
         sso.initElement(optionsElement);
+        sso.initIntroductionScript(optionsIntroductionScript);
     }
 }
 
 ParseTask::~ParseTask()
 {
     // ParseTask takes over ownership of its input exclusive context.
     js_delete(cx);
 
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -400,16 +400,17 @@ struct ParseTask
     PersistentRootedObject exclusiveContextGlobal;
 
     // Saved GC-managed CompileOptions fields that will populate slots in
     // the ScriptSourceObject. We create the ScriptSourceObject in the
     // compilation's temporary compartment, so storing these values there
     // at that point would create cross-compartment references. Instead we
     // hold them here, and install them after merging the compartments.
     PersistentRootedObject optionsElement;
+    PersistentRootedScript optionsIntroductionScript;
 
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3916,32 +3916,52 @@ static bool
 DebuggerSource_getElementProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, sourceObject);
     args.rval().set(sourceObject->elementAttributeName());
     return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
 }
 
 static bool
+DebuggerSource_getIntroductionScript(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionScript)", args, obj, sourceObject);
+
+    RootedScript script(cx, sourceObject->introductionScript());
+    if (script) {
+        RootedObject scriptDO(cx, Debugger::fromChildJSObject(obj)->wrapScript(cx, script));
+        if (!scriptDO)
+            return false;
+        args.rval().setObject(*scriptDO);
+    } else {
+        args.rval().setUndefined();
+    }
+    return true;
+}
+
+static bool
 DebuggerSource_getIntroductionOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject);
 
+    // Regardless of what's recorded in the ScriptSourceObject and
+    // ScriptSource, only hand out the introduction offset if we also have
+    // the script within which it applies.
     ScriptSource *ss = sourceObject->source();
-    if (ss->hasIntroductionOffset())
+    if (ss->hasIntroductionOffset() && sourceObject->introductionScript())
         args.rval().setInt32(ss->introductionOffset());
     else
         args.rval().setUndefined();
     return true;
 }
 
 static bool
 DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp)
 {
-    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject);
+    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionType)", args, obj, sourceObject);
 
     ScriptSource *ss = sourceObject->source();
     if (ss->hasIntroductionType()) {
         JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->introductionType());
         if (!str)
             return false;
         args.rval().setString(str);
     } else {
@@ -3950,16 +3970,17 @@ DebuggerSource_getIntroductionType(JSCon
     return true;
 }
 
 static const JSPropertySpec DebuggerSource_properties[] = {
     JS_PSG("text", DebuggerSource_getText, 0),
     JS_PSG("url", DebuggerSource_getUrl, 0),
     JS_PSG("element", DebuggerSource_getElement, 0),
     JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
+    JS_PSG("introductionScript", DebuggerSource_getIntroductionScript, 0),
     JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0),
     JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0),
     JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec DebuggerSource_methods[] = {
     JS_FS_END
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -20,16 +20,17 @@
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/WrapperObject.h"
 
 using mozilla::DebugOnly;
 using mozilla::MallocSizeOf;
 using mozilla::Move;
+using mozilla::PodCopy;
 using mozilla::PodEqual;
 
 using namespace js;
 
 using JS::RuntimeStats;
 using JS::ObjectPrivateVisitor;
 using JS::ZoneStats;
 using JS::CompartmentStats;
@@ -51,38 +52,26 @@ InefficientNonFlatteningStringHashPolicy
         chars = l->pureChars();
     } else {
         // Slowest hash function evar!
         if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars))
             MOZ_CRASH("oom");
         chars = ownedChars;
     }
 
-    // We include the result of isShort() in the hash.  This is because it is
-    // possible for a particular string (i.e. unique char sequence) to have one
-    // or more copies as short strings and one or more copies as non-short
-    // strings, and treating them separately for the purposes of notable string
-    // detection makes things simpler.  In practice, although such collisions
-    // do happen, they are sufficiently rare that they are unlikely to have a
-    // significant effect on which strings are considered notable.
-    return mozilla::AddToHash(mozilla::HashString(chars, l->length()),
-                              l->isShort());
+    return mozilla::HashString(chars, l->length());
 }
 
 /* static */ bool
 InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const Lookup &l)
 {
     // We can't use js::EqualStrings, because that flattens our strings.
     if (k->length() != l->length())
         return false;
 
-    // Just like in hash(), we must consider isShort() for the two strings.
-    if (k->isShort() != l->isShort())
-        return false;
-
     const jschar *c1;
     ScopedJSFreePtr<jschar> ownedChars1;
     if (k->hasPureChars()) {
         c1 = k->pureChars();
     } else {
         if (!k->copyNonPureChars(/* tcx */ nullptr, ownedChars1))
             MOZ_CRASH("oom");
         c1 = ownedChars1;
@@ -96,47 +85,60 @@ InefficientNonFlatteningStringHashPolicy
         if (!l->copyNonPureChars(/* tcx */ nullptr, ownedChars2))
             MOZ_CRASH("oom");
         c2 = ownedChars2;
     }
 
     return PodEqual(c1, c2, k->length());
 }
 
+/* static */ HashNumber
+CStringHashPolicy::hash(const Lookup &l)
+{
+    return mozilla::HashString(l);
+}
+
+/* static */ bool
+CStringHashPolicy::match(const char *const &k, const Lookup &l)
+{
+    return strcmp(k, l) == 0;
+}
+
 } // namespace js
 
 namespace JS {
 
 NotableStringInfo::NotableStringInfo()
-  : buffer(0),
+  : StringInfo(),
+    buffer(0),
     length(0)
 {
 }
 
 NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
   : StringInfo(info),
     length(str->length())
 {
-    size_t bufferSize = Min(str->length() + 1, size_t(4096));
+    size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS));
     buffer = js_pod_malloc<char>(bufferSize);
     if (!buffer) {
         MOZ_CRASH("oom");
     }
 
     const jschar* chars;
     ScopedJSFreePtr<jschar> ownedChars;
     if (str->hasPureChars()) {
         chars = str->pureChars();
     } else {
         if (!str->copyNonPureChars(/* tcx */ nullptr, ownedChars))
             MOZ_CRASH("oom");
         chars = ownedChars;
     }
 
-    // We might truncate |str| even if it's much shorter than 4096 chars, if
+    // We might truncate |str| even if it's much shorter than 1024 chars, if
     // |str| contains unicode chars.  Since this is just for a memory reporter,
     // we don't care.
     PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
 }
 
 NotableStringInfo::NotableStringInfo(NotableStringInfo &&info)
   : StringInfo(Move(info)),
     length(info.length)
@@ -148,16 +150,79 @@ NotableStringInfo::NotableStringInfo(Not
 NotableStringInfo &NotableStringInfo::operator=(NotableStringInfo &&info)
 {
     MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
     this->~NotableStringInfo();
     new (this) NotableStringInfo(Move(info));
     return *this;
 }
 
+NotableClassInfo::NotableClassInfo()
+  : ClassInfo(),
+    className_(nullptr)
+{
+}
+
+NotableClassInfo::NotableClassInfo(const char *className, const ClassInfo &info)
+  : ClassInfo(info)
+{
+    size_t bytes = strlen(className) + 1;
+    className_ = js_pod_malloc<char>(bytes);
+    if (!className_)
+        MOZ_CRASH("oom");
+    PodCopy(className_, className, bytes);
+}
+
+NotableClassInfo::NotableClassInfo(NotableClassInfo &&info)
+  : ClassInfo(Move(info))
+{
+    className_ = info.className_;
+    info.className_ = nullptr;
+}
+
+NotableClassInfo &NotableClassInfo::operator=(NotableClassInfo &&info)
+{
+    MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
+    this->~NotableClassInfo();
+    new (this) NotableClassInfo(Move(info));
+    return *this;
+}
+
+NotableScriptSourceInfo::NotableScriptSourceInfo()
+  : ScriptSourceInfo(),
+    filename_(nullptr)
+{
+}
+
+NotableScriptSourceInfo::NotableScriptSourceInfo(const char *filename, const ScriptSourceInfo &info)
+  : ScriptSourceInfo(info)
+{
+    size_t bytes = strlen(filename) + 1;
+    filename_ = js_pod_malloc<char>(bytes);
+    if (!filename_)
+        MOZ_CRASH("oom");
+    PodCopy(filename_, filename, bytes);
+}
+
+NotableScriptSourceInfo::NotableScriptSourceInfo(NotableScriptSourceInfo &&info)
+  : ScriptSourceInfo(Move(info))
+{
+    filename_ = info.filename_;
+    info.filename_ = nullptr;
+}
+
+NotableScriptSourceInfo &NotableScriptSourceInfo::operator=(NotableScriptSourceInfo &&info)
+{
+    MOZ_ASSERT(this != &info, "self-move assignment is prohibited");
+    this->~NotableScriptSourceInfo();
+    new (this) NotableScriptSourceInfo(Move(info));
+    return *this;
+}
+
+
 } // namespace JS
 
 typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
 
 struct StatsClosure
 {
     RuntimeStats *rtStats;
     ObjectPrivateVisitor *opv;
@@ -188,43 +253,46 @@ static void
 StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone)
 {
     // Append a new CompartmentStats to the vector.
     RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
 
     // CollectRuntimeStats reserves enough space.
     MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
     ZoneStats &zStats = rtStats->zoneStatsVector.back();
-    zStats.initStrings(rt);
+    if (!zStats.initStrings(rt))
+        MOZ_CRASH("oom");
     rtStats->initExtraZoneStats(zone, &zStats);
     rtStats->currZoneStats = &zStats;
 
     zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &zStats.typePool);
 }
 
 static void
 StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
 {
     // Append a new CompartmentStats to the vector.
     RuntimeStats *rtStats = static_cast<StatsClosure *>(data)->rtStats;
 
     // CollectRuntimeStats reserves enough space.
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
+    if (!cStats.initClasses(rt))
+        MOZ_CRASH("oom");
     rtStats->initExtraCompartmentStats(compartment, &cStats);
 
     compartment->compartmentStats = &cStats;
 
     // Measure the compartment object itself, and things hanging off it.
     compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
                                         &cStats.typeInferenceAllocationSiteTables,
                                         &cStats.typeInferenceArrayTypeTables,
                                         &cStats.typeInferenceObjectTypeTables,
                                         &cStats.compartmentObject,
-                                        &cStats.shapesMallocHeapCompartmentTables,
+                                        &cStats.compartmentTables,
                                         &cStats.crossCompartmentWrappersTable,
                                         &cStats.regexpCompartment,
                                         &cStats.debuggeesSet,
                                         &cStats.baselineStubsOptimized);
 }
 
 static void
 StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
@@ -250,105 +318,118 @@ GetCompartmentStats(JSCompartment *comp)
     return static_cast<CompartmentStats *>(comp->compartmentStats);
 }
 
 enum Granularity {
     FineGrained,    // Corresponds to CollectRuntimeStats()
     CoarseGrained   // Corresponds to AddSizeOfTab()
 };
 
+static void
+AddClassInfo(Granularity granularity, CompartmentStats *cStats, const char *className,
+             JS::ClassInfo &info)
+{
+    if (granularity == FineGrained) {
+        CompartmentStats::ClassesHashMap::AddPtr p =
+            cStats->allClasses->lookupForAdd(className);
+        if (!p) {
+            // Ignore failure -- we just won't record the
+            // object/shape/base-shape as notable.
+            (void)cStats->allClasses->add(p, className, info);
+        } else {
+            p->value().add(info);
+        }
+    }
+}
+
+// The various kinds of hashing are expensive, and the results are unused when
+// doing coarse-grained measurements. Skipping them more than doubles the
+// profile speed for complex pages such as gmail.com.
 template <Granularity granularity>
 static void
 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
                   size_t thingSize)
 {
     StatsClosure *closure = static_cast<StatsClosure *>(data);
     RuntimeStats *rtStats = closure->rtStats;
     ZoneStats *zStats = rtStats->currZoneStats;
     switch (traceKind) {
       case JSTRACE_OBJECT: {
         JSObject *obj = static_cast<JSObject *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(obj->compartment());
-        if (obj->is<JSFunction>())
-            cStats->objectsGCHeapFunction += thingSize;
-        else if (obj->is<ArrayObject>())
-            cStats->objectsGCHeapDenseArray += thingSize;
-        else if (obj->is<CrossCompartmentWrapperObject>())
-            cStats->objectsGCHeapCrossCompartmentWrapper += thingSize;
-        else
-            cStats->objectsGCHeapOrdinary += thingSize;
+
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        info.objectsGCHeap += thingSize;
+        obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
-        obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra);
+        cStats->classInfo.add(info);
+
+        const char* className = obj->getClass()->name;
+        AddClassInfo(granularity, cStats, className, info);
 
         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);
 
-        bool isShort = str->isShort();
-        size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        JS::StringInfo info;
+        info.gcHeap = thingSize;
+        info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        info.numCopies = 1;
 
+        zStats->stringInfo.add(info);
 
-        if (isShort) {
-            zStats->stringsShortGCHeap += thingSize;
-            MOZ_ASSERT(strCharsSize == 0);
-        } else {
-            zStats->stringsNormalGCHeap += thingSize;
-            zStats->stringsNormalMallocHeap += strCharsSize;
-        }
-
-        // This string hashing is expensive.  Its results are unused when doing
-        // coarse-grained measurements, and skipping it more than doubles the
-        // profile speed for complex pages such as gmail.com.
         if (granularity == FineGrained) {
-            ZoneStats::StringsHashMap::AddPtr p = zStats->strings->lookupForAdd(str);
+            ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
             if (!p) {
-                JS::StringInfo info(isShort, thingSize, strCharsSize);
-                zStats->strings->add(p, str, info);
+                // Ignore failure -- we just won't record the string as notable.
+                (void)zStats->allStrings->add(p, str, info);
             } else {
-                p->value().add(isShort, thingSize, strCharsSize);
+                p->value().add(info);
             }
         }
-
         break;
       }
 
       case JSTRACE_SHAPE: {
         Shape *shape = static_cast<Shape *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(shape->compartment());
-        if (shape->inDictionary()) {
-            cStats->shapesGCHeapDict += thingSize;
 
-            // nullptr because kidsSize shouldn't be incremented in this case.
-            shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
-                                          &cStats->shapesMallocHeapDictTables, nullptr);
-        } else {
-            JSObject *parent = shape->base()->getObjectParent();
-            if (parent && parent->is<GlobalObject>())
-                cStats->shapesGCHeapTreeGlobalParented += thingSize;
-            else
-                cStats->shapesGCHeapTreeNonGlobalParented += thingSize;
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        if (shape->inDictionary())
+            info.shapesGCHeapDict += thingSize;
+        else
+            info.shapesGCHeapTree += thingSize;
+        shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
 
-            shape->addSizeOfExcludingThis(rtStats->mallocSizeOf_,
-                                          &cStats->shapesMallocHeapTreeTables,
-                                          &cStats->shapesMallocHeapTreeShapeKids);
-        }
+        cStats->classInfo.add(info);
+
+        const char* className = shape->base()->clasp()->name;
+        AddClassInfo(granularity, cStats, className, info);
         break;
       }
 
       case JSTRACE_BASE_SHAPE: {
         BaseShape *base = static_cast<BaseShape *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(base->compartment());
-        cStats->shapesGCHeapBase += thingSize;
+
+        JS::ClassInfo info;        // This zeroes all the sizes.
+        info.shapesGCHeapBase += thingSize;
+        // No malloc-heap measurements.
+
+        cStats->classInfo.add(info);
+
+        const char* className = base->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_);
@@ -357,19 +438,40 @@ StatsCellCallback(JSRuntime *rt, void *d
         jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData,
                                    &cStats->baselineStubsFallback);
         cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_);
 #endif
 
         ScriptSource *ss = script->scriptSource();
         SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
         if (!entry) {
-            closure->seenSources.add(entry, ss); // Not much to be done on failure.
-            rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf_);
+            (void)closure->seenSources.add(entry, ss); // Not much to be done on failure.
+
+            JS::ScriptSourceInfo info;  // This zeroes all the sizes.
+            ss->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &info);
+            MOZ_ASSERT(info.compressed == 0 || info.uncompressed == 0);
+
+            rtStats->runtime.scriptSourceInfo.add(info);
+
+            if (granularity == FineGrained) {
+                const char* filename = ss->filename();
+                if (!filename)
+                    filename = "<no filename>";
+
+                JS::RuntimeSizes::ScriptSourcesHashMap::AddPtr p =
+                    rtStats->runtime.allScriptSources->lookupForAdd(filename);
+                if (!p) {
+                    // Ignore failure -- we just won't record the script source as notable.
+                    (void)rtStats->runtime.allScriptSources->add(p, filename, info);
+                } else {
+                    p->value().add(info);
+                }
+            }
         }
+
         break;
       }
 
       case JSTRACE_LAZY_SCRIPT: {
         LazyScript *lazy = static_cast<LazyScript *>(thing);
         zStats->lazyScriptsGCHeap += thingSize;
         zStats->lazyScriptsMallocHeap += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
@@ -393,65 +495,141 @@ StatsCellCallback(JSRuntime *rt, void *d
       default:
         MOZ_ASSUME_UNREACHABLE("invalid traceKind");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     zStats->unusedGCThings -= thingSize;
 }
 
-static void
+bool
+ZoneStats::initStrings(JSRuntime *rt)
+{
+    isTotals = false;
+    allStrings = rt->new_<StringsHashMap>();
+    if (!allStrings || !allStrings->init()) {
+        js_delete(allStrings);
+        allStrings = nullptr;
+        return false;
+    }
+    return true;
+}
+
+bool
+CompartmentStats::initClasses(JSRuntime *rt)
+{
+    isTotals = false;
+    allClasses = rt->new_<ClassesHashMap>();
+    if (!allClasses || !allClasses->init()) {
+        js_delete(allClasses);
+        allClasses = nullptr;
+        return false;
+    }
+    return true;
+}
+
+static bool
 FindNotableStrings(ZoneStats &zStats)
 {
     using namespace JS;
 
-    // You should only run FindNotableStrings once per ZoneStats object
-    // (although it's not going to break anything if you run it more than once,
-    // unless you add to |strings| in the meantime).
+    // We should only run FindNotableStrings once per ZoneStats object.
     MOZ_ASSERT(zStats.notableStrings.empty());
 
-    for (ZoneStats::StringsHashMap::Range r = zStats.strings->all(); !r.empty(); r.popFront()) {
+    for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) {
 
         JSString *str = r.front().key();
         StringInfo &info = r.front().value();
 
-        // If this string is too small, or if we can't grow the notableStrings
-        // vector, skip this string.
-        if (info.gcHeap + info.mallocHeap < NotableStringInfo::notableSize() ||
-            !zStats.notableStrings.growBy(1))
+        if (!info.isNotable())
             continue;
 
+        if (!zStats.notableStrings.growBy(1))
+            return false;
+
         zStats.notableStrings.back() = NotableStringInfo(str, info);
 
         // We're moving this string from a non-notable to a notable bucket, so
         // subtract it out of the non-notable tallies.
-        if (info.isShort) {
-            MOZ_ASSERT(zStats.stringsShortGCHeap >= info.gcHeap);
-            zStats.stringsShortGCHeap -= info.gcHeap;
-            MOZ_ASSERT(info.mallocHeap == 0);
-        } else {
-            MOZ_ASSERT(zStats.stringsNormalGCHeap >= info.gcHeap);
-            MOZ_ASSERT(zStats.stringsNormalMallocHeap >= info.mallocHeap);
-            zStats.stringsNormalGCHeap -= info.gcHeap;
-            zStats.stringsNormalMallocHeap -= info.mallocHeap;
-        }
+        zStats.stringInfo.subtract(info);
     }
+    // Delete |allStrings| now, rather than waiting for zStats's destruction,
+    // to reduce peak memory consumption during reporting.
+    js_delete(zStats.allStrings);
+    zStats.allStrings = nullptr;
+    return true;
 }
 
-bool
-ZoneStats::initStrings(JSRuntime *rt)
+static bool
+FindNotableClasses(CompartmentStats &cStats)
 {
-    strings = rt->new_<StringsHashMap>();
-    if (!strings)
-        return false;
-    if (!strings->init()) {
-        js_delete(strings);
-        strings = nullptr;
-        return false;
+    using namespace JS;
+
+    // We should only run FindNotableClasses once per ZoneStats object.
+    MOZ_ASSERT(cStats.notableClasses.empty());
+
+    for (CompartmentStats::ClassesHashMap::Range r = cStats.allClasses->all();
+         !r.empty();
+         r.popFront())
+    {
+        const char *className = r.front().key();
+        ClassInfo &info = r.front().value();
+
+        // If this class isn't notable, or if we can't grow the notableStrings
+        // vector, skip this string.
+        if (!info.isNotable())
+            continue;
+
+        if (!cStats.notableClasses.growBy(1))
+            return false;
+
+        cStats.notableClasses.back() = NotableClassInfo(className, info);
+
+        // We're moving this class from a non-notable to a notable bucket, so
+        // subtract it out of the non-notable tallies.
+        cStats.classInfo.subtract(info);
     }
+    // Delete |allClasses| now, rather than waiting for zStats's destruction,
+    // to reduce peak memory consumption during reporting.
+    js_delete(cStats.allClasses);
+    cStats.allClasses = nullptr;
+    return true;
+}
+
+static bool
+FindNotableScriptSources(JS::RuntimeSizes &runtime)
+{
+    using namespace JS;
+
+    // We should only run FindNotableScriptSources once per RuntimeSizes.
+    MOZ_ASSERT(runtime.notableScriptSources.empty());
+
+    for (RuntimeSizes::ScriptSourcesHashMap::Range r = runtime.allScriptSources->all();
+         !r.empty();
+         r.popFront())
+    {
+        const char *filename = r.front().key();
+        ScriptSourceInfo &info = r.front().value();
+
+        if (!info.isNotable())
+            continue;
+
+        if (!runtime.notableScriptSources.growBy(1))
+            return false;
+
+        runtime.notableScriptSources.back() = NotableScriptSourceInfo(filename, info);
+
+        // We're moving this script source from a non-notable to a notable
+        // bucket, so subtract its sizes from the non-notable tallies.
+        runtime.scriptSourceInfo.subtract(info);
+    }
+    // Delete |allScriptSources| now, rather than waiting for zStats's
+    // destruction, to reduce peak memory consumption during reporting.
+    js_delete(runtime.allScriptSources);
+    runtime.allScriptSources = nullptr;
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
 {
     if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
         return false;
@@ -473,59 +651,48 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
     if (!closure.init())
         return false;
     IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
                                         StatsArenaCallback, StatsCellCallback<FineGrained>);
 
     // Take the "explicit/js/runtime/" measurements.
     rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
 
+    if (!FindNotableScriptSources(rtStats->runtime))
+        return false;
+
     ZoneStatsVector &zs = rtStats->zoneStatsVector;
     ZoneStats &zTotals = rtStats->zTotals;
 
-    // For each zone:
-    // - sum everything except its strings data into zTotals, and
-    // - find its notable strings.
-    // Also, record which zone had the biggest |strings| hashtable -- to save
-    // time and memory, we will re-use that hashtable to find the notable
-    // strings for zTotals.
-    size_t iMax = 0;
-    for (size_t i = 0; i < zs.length(); i++) {
-        zTotals.addIgnoringStrings(zs[i]);
-        FindNotableStrings(zs[i]);
-        if (zs[i].strings->count() > zs[iMax].strings->count())
-            iMax = i;
+    // We don't look for notable strings for zTotals. So we first sum all the
+    // zones' measurements to get the totals. Then we find the notable strings
+    // within each zone.
+    for (size_t i = 0; i < zs.length(); i++)
+        zTotals.addSizes(zs[i]);
+
+    for (size_t i = 0; i < zs.length(); i++)
+        if (!FindNotableStrings(zs[i]))
+            return false;
+
+    MOZ_ASSERT(!zTotals.allStrings);
+
+    CompartmentStatsVector &cs = rtStats->compartmentStatsVector;
+    CompartmentStats &cTotals = rtStats->cTotals;
+
+    // As with the zones, we sum all compartments first, and then get the
+    // notable classes within each zone.
+    for (size_t i = 0; i < cs.length(); i++)
+        cTotals.addSizes(cs[i]);
+
+    for (size_t i = 0; i < cs.length(); i++) {
+        if (!FindNotableClasses(cs[i]))
+            return false;
     }
 
-    // Transfer the biggest strings table to zTotals.  We can do this because:
-    // (a) we've found the notable strings for zs[IMax], and so don't need it
-    //     any more for zs, and
-    // (b) zs[iMax].strings contains a subset of the values that will end up in
-    //     zTotals.strings.
-    MOZ_ASSERT(!zTotals.strings);
-    zTotals.strings = zs[iMax].strings;
-    zs[iMax].strings = nullptr;
-
-    // Add the remaining strings hashtables to zTotals, and then get the
-    // notable strings for zTotals.
-    for (size_t i = 0; i < zs.length(); i++) {
-        if (i != iMax) {
-            zTotals.addStrings(zs[i]);
-            js_delete(zs[i].strings);
-            zs[i].strings = nullptr;
-        }
-    }
-    FindNotableStrings(zTotals);
-    js_delete(zTotals.strings);
-    zTotals.strings = nullptr;
-
-    for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
-        CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
-        rtStats->cTotals.add(cStats);
-    }
+    MOZ_ASSERT(!cTotals.allClasses);
 
     rtStats->gcHeapGCThings = rtStats->zTotals.sizeOfLiveGCThings() +
                               rtStats->cTotals.sizeOfLiveGCThings();
 
 #ifdef DEBUG
     // Check that the in-arena measurements look ok.
     size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
                             rtStats->zTotals.unusedGCThings +
@@ -618,22 +785,20 @@ AddSizeOfTab(JSRuntime *rt, HandleObject
     StatsClosure closure(&rtStats, opv);
     if (!closure.init())
         return false;
     IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
                                        StatsCompartmentCallback, StatsArenaCallback,
                                        StatsCellCallback<CoarseGrained>);
 
     JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
-    rtStats.zTotals.add(rtStats.zoneStatsVector[0]);
+    rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
 
-    for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
-        CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
-        rtStats.cTotals.add(cStats);
-    }
+    for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++)
+        rtStats.cTotals.addSizes(rtStats.compartmentStatsVector[i]);
 
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         comp->compartmentStats = nullptr;
 
     rtStats.zTotals.addToTabSizes(sizes);
     rtStats.cTotals.addToTabSizes(sizes);
 
     return true;
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1433,17 +1433,17 @@ StackBaseShape::hash(const StackBaseShap
     hash = RotateLeft(hash, 4) ^ uintptr_t(base->rawSetter);
     return hash;
 }
 
 /* static */ inline bool
 StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
 {
     return key->flags == lookup->flags
-        && key->clasp == lookup->clasp
+        && key->clasp_ == lookup->clasp
         && key->parent == lookup->parent
         && key->metadata == lookup->metadata
         && key->rawGetter == lookup->rawGetter
         && key->rawSetter == lookup->rawSetter;
 }
 
 void
 StackBaseShape::trace(JSTracer *trc)
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -21,16 +21,17 @@
 #include "jstypes.h"
 #include "NamespaceImports.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "gc/Rooting.h"
 #include "js/HashTable.h"
+#include "js/MemoryMetrics.h"
 #include "js/RootingAPI.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
@@ -576,17 +577,17 @@ class BaseShape : public gc::BarrieredCe
         NEW_TYPE_UNKNOWN    =  0x400,
         UNCACHEABLE_PROTO   =  0x800,
         HAD_ELEMENTS_ACCESS = 0x1000,
 
         OBJECT_FLAG_MASK    = 0x1ff8
     };
 
   private:
-    const Class         *clasp;         /* Class of referring object. */
+    const Class         *clasp_;        /* Class of referring object. */
     HeapPtrObject       parent;         /* Parent of referring object. */
     HeapPtrObject       metadata;       /* Optional holder of metadata about
                                          * the referring object. */
     JSCompartment       *compartment_;  /* Compartment shape belongs to. */
     uint32_t            flags;          /* Vector of above flags. */
     uint32_t            slotSpan_;      /* Object slot span for BaseShapes at
                                          * dictionary last properties. */
 
@@ -613,30 +614,30 @@ class BaseShape : public gc::BarrieredCe
   public:
     void finalize(FreeOp *fop);
 
     BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata,
               uint32_t objectFlags)
     {
         JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
         mozilla::PodZero(this);
-        this->clasp = clasp;
+        this->clasp_ = clasp;
         this->parent = parent;
         this->metadata = metadata;
         this->flags = objectFlags;
         this->compartment_ = comp;
     }
 
     BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata,
               uint32_t objectFlags, uint8_t attrs,
               PropertyOp rawGetter, StrictPropertyOp rawSetter)
     {
         JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
         mozilla::PodZero(this);
-        this->clasp = clasp;
+        this->clasp_ = clasp;
         this->parent = parent;
         this->metadata = metadata;
         this->flags = objectFlags;
         this->rawGetter = rawGetter;
         this->rawSetter = rawSetter;
         if ((attrs & JSPROP_GETTER) && rawGetter) {
             this->flags |= HAS_GETTER_OBJECT;
             GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
@@ -649,17 +650,17 @@ class BaseShape : public gc::BarrieredCe
     }
 
     inline BaseShape(const StackBaseShape &base);
 
     /* Not defined: BaseShapes must not be stack allocated. */
     ~BaseShape();
 
     BaseShape &operator=(const BaseShape &other) {
-        clasp = other.clasp;
+        clasp_ = other.clasp_;
         parent = other.parent;
         metadata = other.metadata;
         flags = other.flags;
         slotSpan_ = other.slotSpan_;
         if (flags & HAS_GETTER_OBJECT) {
             getterObj = other.getterObj;
             GetterSetterWriteBarrierPost(runtimeFromMainThread(), &getterObj);
         } else {
@@ -674,16 +675,18 @@ class BaseShape : public gc::BarrieredCe
             if (rawSetter)
                 GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &setterObj);
             rawSetter = other.rawSetter;
         }
         compartment_ = other.compartment_;
         return *this;
     }
 
+    const Class *clasp() const { return clasp_; }
+
     bool isOwned() const { return !!(flags & OWNED_SHAPE); }
 
     bool matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const {
         return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
     }
 
     inline void adoptUnowned(UnownedBaseShape *other);
 
@@ -755,17 +758,17 @@ class BaseShape : public gc::BarrieredCe
             gc::MarkObject(trc, &parent, "parent");
 
         if (metadata)
             gc::MarkObject(trc, &metadata, "metadata");
     }
 
   private:
     static void staticAsserts() {
-        JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
+        JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));
     }
 };
 
 class UnownedBaseShape : public BaseShape {};
 
 inline void
 BaseShape::adoptUnowned(UnownedBaseShape *other)
 {
@@ -812,17 +815,17 @@ struct StackBaseShape
     JSObject *parent;
     JSObject *metadata;
     PropertyOp rawGetter;
     StrictPropertyOp rawSetter;
     JSCompartment *compartment;
 
     explicit StackBaseShape(BaseShape *base)
       : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
-        clasp(base->clasp),
+        clasp(base->clasp_),
         parent(base->parent),
         metadata(base->metadata),
         rawGetter(nullptr),
         rawSetter(nullptr),
         compartment(base->compartment())
     {}
 
     inline StackBaseShape(ThreadSafeContext *cx, const Class *clasp,
@@ -851,17 +854,17 @@ struct StackBaseShape
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
     void trace(JSTracer *trc);
 };
 
 inline
 BaseShape::BaseShape(const StackBaseShape &base)
 {
     mozilla::PodZero(this);
-    this->clasp = base.clasp;
+    this->clasp_ = base.clasp;
     this->parent = base.parent;
     this->metadata = base.metadata;
     this->flags = base.flags;
     this->rawGetter = base.rawGetter;
     this->rawSetter = base.rawSetter;
     if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter)
         GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
     if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter)
@@ -977,22 +980,27 @@ class Shape : public gc::BarrieredCell<S
 
     bool makeOwnBaseShape(ThreadSafeContext *cx);
 
   public:
     bool hasTable() const { return base()->hasTable(); }
     ShapeTable &table() const { return base()->table(); }
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                size_t *propTableSize, size_t *kidsSize) const {
-        if (hasTable())
-            *propTableSize += table().sizeOfIncludingThis(mallocSizeOf);
+                                JS::ClassInfo *info) const
+    {
+        if (hasTable()) {
+            if (inDictionary())
+                info->shapesMallocHeapDictTables += table().sizeOfIncludingThis(mallocSizeOf);
+            else
+                info->shapesMallocHeapTreeTables += table().sizeOfIncludingThis(mallocSizeOf);
+        }
 
         if (!inDictionary() && kids.isHash())
-            *kidsSize += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
+            info->shapesMallocHeapTreeKids += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
     }
 
     bool isNative() const {
         JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
         return !(flags & NON_NATIVE);
     }
 
     const HeapPtrShape &previous() const { return parent; }
@@ -1025,17 +1033,17 @@ class Shape : public gc::BarrieredCell<S
 
         void popFront() {
             JS_ASSERT(!empty());
             cursor = cursor->parent;
         }
     };
 
     const Class *getObjectClass() const {
-        return base()->clasp;
+        return base()->clasp_;
     }
     JSObject *getObjectParent() const { return base()->parent; }
     JSObject *getObjectMetadata() const { return base()->metadata; }
 
     static Shape *setObjectParent(ExclusiveContext *cx,
                                   JSObject *obj, TaggedProto proto, Shape *last);
     static Shape *setObjectMetadata(JSContext *cx,
                                     JSObject *metadata, TaggedProto proto, Shape *last);
@@ -1476,17 +1484,17 @@ struct StackShape
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
 
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
     uint32_t maybeSlot() const { return slot_; }
 
     uint32_t slotSpan() const {
-        uint32_t free = JSSLOT_FREE(base->clasp);
+        uint32_t free = JSSLOT_FREE(base->clasp_);
         return hasMissingSlot() ? free : (maybeSlot() + 1);
     }
 
     void setSlot(uint32_t slot) {
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
         slot_ = slot;
     }
 
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -87,14 +87,14 @@ js::ValueToStringBufferSlow(JSContext *c
     if (!ToPrimitive(cx, JSTYPE_STRING, &v))
         return false;
 
     if (v.isString())
         return sb.append(v.toString());
     if (v.isNumber())
         return NumberValueToStringBuffer(cx, v, sb);
     if (v.isBoolean())
-        return BooleanToStringBuffer(cx, v.toBoolean(), sb);
+        return BooleanToStringBuffer(v.toBoolean(), sb);
     if (v.isNull())
         return sb.append(cx->names().null);
     JS_ASSERT(v.isUndefined());
     return sb.append(cx->names().undefined);
 }
--- a/js/src/vm/StringBuffer.h
+++ b/js/src/vm/StringBuffer.h
@@ -136,16 +136,16 @@ ValueToStringBuffer(JSContext *cx, const
     if (v.isString())
         return sb.append(v.toString());
 
     return ValueToStringBufferSlow(cx, v, sb);
 }
 
 /* ES5 9.8 ToString for booleans, appending the result to the string buffer. */
 inline bool
-BooleanToStringBuffer(JSContext *cx, bool b, StringBuffer &sb)
+BooleanToStringBuffer(bool b, StringBuffer &sb)
 {
     return b ? sb.append("true") : sb.append("false");
 }
 
 }  /* namespace js */
 
 #endif /* vm_StringBuffer_h */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1648,21 +1648,21 @@ JSMainRuntimeCompartmentsUserDistinguish
 class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData)
     {
-        return MOZ_COLLECT_REPORT(
-            "js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES,
+        return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak",
+            KIND_OTHER, UNITS_BYTES,
             JSMainRuntimeTemporaryPeakDistinguishedAmount(),
-            "The peak size of the transient storage in the main JSRuntime "
-            "(the current size of which is reported as "
+            "Peak transient data size in the main JSRuntime (the current size "
+            "of which is reported as "
             "'explicit/js-non-window/runtime/temporary').");
     }
 };
 
 NS_IMPL_ISUPPORTS1(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
 
 // The REPORT* macros do an unconditional report.  The ZCREPORT* macros are for
 // compartments and zones; they aggregate any entries smaller than
@@ -1681,93 +1681,64 @@ NS_IMPL_ISUPPORTS1(JSMainRuntimeTemporar
                           NS_LITERAL_CSTRING(_desc),                          \
                           closure);                                           \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
     } while (0)
 
 #define REPORT_BYTES(_path, _kind, _amount, _desc)                            \
     REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
 
-// REPORT2 and REPORT_BYTES2 are just like REPORT and REPORT_BYTES, except the
-// description is an nsCString, instead of a literal string.
-
-#define REPORT2(_path, _kind, _units, _amount, _desc)                         \
-    do {                                                                      \
-        nsresult rv;                                                          \
-        rv = cb->Callback(EmptyCString(), _path,                              \
-                          nsIMemoryReporter::_kind,                           \
-                          nsIMemoryReporter::_units,                          \
-                          _amount,                                            \
-                          _desc,                                              \
-                          closure);                                           \
-        NS_ENSURE_SUCCESS(rv, rv);                                            \
-    } while (0)
-
-#define REPORT_BYTES2(_path, _kind, _amount, _desc)                           \
-    REPORT2(_path, _kind, UNITS_BYTES, _amount, _desc);
-
 #define REPORT_GC_BYTES(_path, _amount, _desc)                                \
     do {                                                                      \
         size_t amount = _amount;  /* evaluate _amount only once */            \
         nsresult rv;                                                          \
         rv = cb->Callback(EmptyCString(), _path,                              \
                           nsIMemoryReporter::KIND_NONHEAP,                    \
                           nsIMemoryReporter::UNITS_BYTES, amount,             \
                           NS_LITERAL_CSTRING(_desc), closure);                \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
         gcTotal += amount;                                                    \
     } while (0)
 
-// Report compartment/zone bytes.  Note that _descLiteral must be a literal
-// string.
-//
-// Nb: all non-GC compartment reports are currently KIND_HEAP, and this macro
-// relies on that.
-#define ZCREPORT_BYTES(_path, _amount, _descLiteral)                          \
+// Report compartment/zone non-GC (KIND_HEAP) bytes.
+#define ZCREPORT_BYTES(_path, _amount, _desc)                                 \
     do {                                                                      \
         /* Assign _descLiteral plus "" into a char* to prove that it's */     \
         /* actually a literal. */                                             \
-        const char* unusedDesc = _descLiteral "";                             \
-        (void) unusedDesc;                                                    \
-        ZCREPORT_BYTES2(_path, _amount, NS_LITERAL_CSTRING(_descLiteral));    \
-    } while (0)
-
-// ZCREPORT_BYTES2 is identical to ZCREPORT_BYTES, except the description is a
-// nsCString instead of a literal string.
-#define ZCREPORT_BYTES2(_path, _amount, _desc)                                \
-    do {                                                                      \
         size_t amount = _amount;  /* evaluate _amount only once */            \
         if (amount >= SUNDRIES_THRESHOLD) {                                   \
             nsresult rv;                                                      \
             rv = cb->Callback(EmptyCString(), _path,                          \
                               nsIMemoryReporter::KIND_HEAP,                   \
                               nsIMemoryReporter::UNITS_BYTES, amount,         \
-                              _desc, closure);                                \
+                              NS_LITERAL_CSTRING(_desc), closure);            \
             NS_ENSURE_SUCCESS(rv, rv);                                        \
         } else {                                                              \
             sundriesMallocHeap += amount;                                     \
         }                                                                     \
     } while (0)
 
+// Report compartment/zone GC bytes.
 #define ZCREPORT_GC_BYTES(_path, _amount, _desc)                              \
     do {                                                                      \
         size_t amount = _amount;  /* evaluate _amount only once */            \
         if (amount >= SUNDRIES_THRESHOLD) {                                   \
             nsresult rv;                                                      \
             rv = cb->Callback(EmptyCString(), _path,                          \
                               nsIMemoryReporter::KIND_NONHEAP,                \
                               nsIMemoryReporter::UNITS_BYTES, amount,         \
                               NS_LITERAL_CSTRING(_desc), closure);            \
             NS_ENSURE_SUCCESS(rv, rv);                                        \
             gcTotal += amount;                                                \
         } else {                                                              \
             sundriesGCHeap += amount;                                         \
         }                                                                     \
     } while (0)
 
+// Report runtime bytes.
 #define RREPORT_BYTES(_path, _kind, _amount, _desc)                           \
     do {                                                                      \
         size_t amount = _amount;  /* evaluate _amount only once */            \
         nsresult rv;                                                          \
         rv = cb->Callback(EmptyCString(), _path,                              \
                           nsIMemoryReporter::_kind,                           \
                           nsIMemoryReporter::UNITS_BYTES, amount,             \
                           NS_LITERAL_CSTRING(_desc), closure);                \
@@ -1783,202 +1754,281 @@ static nsresult
 ReportZoneStats(const JS::ZoneStats &zStats,
                 const xpc::ZoneStatsExtras &extras,
                 nsIMemoryReporterCallback *cb,
                 nsISupports *closure, size_t *gcTotalOut = nullptr)
 {
     const nsAutoCString& pathPrefix = extras.pathPrefix;
     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
 
+    MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
+
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
-                      zStats.gcHeapArenaAdmin,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap, within arenas, that is used (a) to hold internal "
-                      "bookkeeping information, and (b) to provide padding to "
-                      "align GC things.");
+        zStats.gcHeapArenaAdmin,
+        "Bookkeeping information and alignment padding within GC arenas.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
-                      zStats.unusedGCThings,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap taken by empty GC thing slots within non-empty "
-                      "arenas.");
+        zStats.unusedGCThings,
+        "Empty GC thing cells within non-empty arenas.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
-                      zStats.lazyScriptsGCHeap,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap that represents scripts which haven't executed yet.");
+        zStats.lazyScriptsGCHeap,
+        "Scripts that haven't executed yet.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
-                   zStats.lazyScriptsMallocHeap,
-                   "Memory holding miscellaneous additional information associated with lazy "
-                   "scripts.  This memory is allocated on the malloc heap.");
+        zStats.lazyScriptsMallocHeap,
+        "Lazy script tables containing free variables or inner functions.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
-                      zStats.jitCodesGCHeap,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap that holds references to executable code pools "
-                      "used by the JITs.");
+        zStats.jitCodesGCHeap,
+        "References to executable code pools used by the JITs.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"),
-                      zStats.typeObjectsGCHeap,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap that holds type inference information.");
+        zStats.typeObjectsGCHeap,
+        "Type inference information about objects.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"),
-                   zStats.typeObjectsMallocHeap,
-                   "Memory holding miscellaneous additional information associated with type "
-                   "objects.");
+        zStats.typeObjectsMallocHeap,
+        "Type object addenda.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
-                   zStats.typePool,
-                   "Memory holding contents of type sets and related data.");
+        zStats.typePool,
+        "Type sets and related data.");
 
     size_t stringsNotableAboutMemoryGCHeap = 0;
     size_t stringsNotableAboutMemoryMallocHeap = 0;
 
+    #define MAYBE_INLINE \
+        "The characters may be inline or on the malloc heap."
+    #define MAYBE_OVERALLOCATED \
+        "Sometimes over-allocated to simplify string concatenation."
+
     for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
         const JS::NotableStringInfo& info = zStats.notableStrings[i];
 
+        MOZ_ASSERT(!zStats.isTotals);
+
         nsDependentCString notableString(info.buffer);
 
         // Viewing about:memory generates many notable strings which contain
         // "string(length=".  If we report these as notable, then we'll create
         // even more notable strings the next time we open about:memory (unless
         // there's a GC in the meantime), and so on ad infinitum.
         //
         // To avoid cluttering up about:memory like this, we stick notable
-        // strings which contain "strings/notable/string(length=" into their own
-        // bucket.
+        // strings which contain "string(length=" into their own bucket.
 #       define STRING_LENGTH "string(length="
         if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
             stringsNotableAboutMemoryGCHeap += info.gcHeap;
             stringsNotableAboutMemoryMallocHeap += info.mallocHeap;
             continue;
         }
 
         // Escape / to \ before we put notableString into the memory reporter
         // path, because we don't want any forward slashes in the string to
         // count as path separators.
         nsCString escapedString(notableString);
         escapedString.ReplaceSubstring("/", "\\");
 
         bool truncated = notableString.Length() < info.length;
 
         nsCString path = pathPrefix +
-            nsPrintfCString("strings/notable/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
+            nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
                             info.length, info.numCopies, escapedString.get(),
                             truncated ? " (truncated)" : "");
 
-        REPORT_BYTES2(path + NS_LITERAL_CSTRING("gc-heap"),
-            KIND_NONHEAP,
-            info.gcHeap,
-            nsPrintfCString("Memory allocated to hold headers for copies of "
-            "the given notable string.  A string is notable if all of its copies "
-            "together use more than %d bytes total of JS GC heap and malloc heap "
-            "memory.\n\n"
-            "These headers may contain the string data itself, if the string "
-            "is short enough.  If so, the string won't have any memory reported "
-            "under 'string-chars'.",
-            JS::NotableStringInfo::notableSize()));
-        gcTotal += info.gcHeap;
+        if (info.gcHeap > 0) {
+            REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
+                info.gcHeap,
+                "Strings. " MAYBE_INLINE);
+        }
 
         if (info.mallocHeap > 0) {
-            REPORT_BYTES2(path + NS_LITERAL_CSTRING("malloc-heap"),
-                KIND_HEAP,
-                info.mallocHeap,
-                nsPrintfCString("Memory allocated on the malloc heap to hold "
-                "string data for copies of the given notable string.  A string is "
-                "notable if all of its copies together use more than %d bytes "
-                "total of JS GC heap and malloc heap memory.",
-                JS::NotableStringInfo::notableSize()));
+            REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
+                KIND_HEAP, info.mallocHeap,
+                "Non-inline string characters. " MAYBE_OVERALLOCATED);
         }
     }
 
-    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/short-gc-heap"),
-                      zStats.stringsShortGCHeap,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap that holds headers for strings which are short "
-                      "enough to be stored completely within the header.  That "
-                      "is, a 'short' string uses no string-chars.");
-
-    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/normal/gc-heap"),
-                      zStats.stringsNormalGCHeap,
-                      "Memory on the garbage-collected JavaScript "
-                      "heap that holds string headers for strings which are too "
-                      "long to fit entirely within the header.  The character "
-                      "data for such strings is counted under "
-                      "strings/normal/malloc-heap.");
-
-    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/normal/malloc-heap"),
-                   zStats.stringsNormalMallocHeap,
-                   "Memory allocated to hold characters for strings which are too long "
-                   "to fit entirely within their string headers.\n\n"
-                   "Sometimes more memory is allocated than necessary, to "
-                   "simplify string concatenation.");
+    nsCString nonNotablePath = pathPrefix;
+    nonNotablePath += zStats.isTotals
+                    ? NS_LITERAL_CSTRING("strings/")
+                    : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
+
+    if (zStats.stringInfo.gcHeap > 0) {
+        REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
+            zStats.stringInfo.gcHeap,
+            "Strings. " MAYBE_INLINE);
+    }
+
+    if (zStats.stringInfo.mallocHeap > 0) {
+        REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
+            KIND_HEAP, zStats.stringInfo.mallocHeap,
+            "Non-inline string characters. " MAYBE_OVERALLOCATED);
+    }
 
     if (stringsNotableAboutMemoryGCHeap > 0) {
-        ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/notable/about-memory/gc-heap"),
-                          stringsNotableAboutMemoryGCHeap,
-                          "Memory allocated on the garbage-collected JavaScript "
-                          "heap that holds headers for notable strings which "
-                          "contain the string '" STRING_LENGTH "'.  These "
-                          "strings are likely from about:memory itself.  We "
-                          "filter them out rather than display them, because "
-                          "displaying them would create even more strings every "
-                          "time you refresh about:memory.");
+        MOZ_ASSERT(!zStats.isTotals);
+        REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
+            stringsNotableAboutMemoryGCHeap,
+            "Strings that contain the characters '" STRING_LENGTH "', which "
+            "are probably from about:memory itself." MAYBE_INLINE
+            " We filter them out rather than display them, because displaying "
+            "them would create even more such strings every time about:memory "
+            "is refreshed.");
     }
 
     if (stringsNotableAboutMemoryMallocHeap > 0) {
-        ZCREPORT_BYTES(pathPrefix +
-                       NS_LITERAL_CSTRING("strings/notable/about-memory/malloc-heap"),
-                       stringsNotableAboutMemoryMallocHeap,
-                       "Memory allocated to hold characters of notable strings "
-                       "which contain the string '" STRING_LENGTH "'.  These "
-                       "strings are likely from about:memory itself.  We filter "
-                       "them out rather than display them, because displaying "
-                       "them would create even more strings every time you "
-                       "refresh about:memory.");
+        MOZ_ASSERT(!zStats.isTotals);
+        REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
+            KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
+            "Non-inline string characters of strings that contain the "
+            "characters '" STRING_LENGTH "', which are probably from "
+            "about:memory itself. " MAYBE_OVERALLOCATED
+            " We filter them out rather than display them, because displaying "
+            "them would create even more such strings eve