Bug 674779 - Per-component CPU monitoring, high-level. r=blassey, r=mossop
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Tue, 31 Mar 2015 12:40:59 +0200
changeset 255960 dab7343222264558e1c99e06167bc9fc8c3bda4d
parent 255959 ba375ae024b33f10dc2910058ee86cf63b4cba7a
child 255961 b4efb3f1976f6db138152e5b28fbebec90737b29
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, mossop
bugs674779
milestone40.0a1
Bug 674779 - Per-component CPU monitoring, high-level. r=blassey, r=mossop
b2g/installer/package-manifest.in
browser/base/content/test/social/browser_addons.js
browser/installer/package-manifest.in
docshell/base/nsAboutRedirector.cpp
docshell/build/nsDocShellModule.cpp
mobile/android/installer/package-manifest.in
toolkit/components/aboutcompartments/content/aboutCompartments.js
toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
toolkit/components/aboutcompartments/jar.mn
toolkit/components/aboutcompartments/moz.build
toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
toolkit/components/aboutcompartments/nsCompartmentInfo.h
toolkit/components/aboutcompartments/nsICompartmentInfo.idl
toolkit/components/aboutperformance/content/aboutPerformance.js
toolkit/components/aboutperformance/content/aboutPerformance.xhtml
toolkit/components/aboutperformance/jar.mn
toolkit/components/aboutperformance/moz.build
toolkit/components/aboutperformance/nsIPerformanceStats.idl
toolkit/components/aboutperformance/nsPerformanceStats.cpp
toolkit/components/aboutperformance/nsPerformanceStats.h
toolkit/components/aboutperformance/tests/browser/browser.ini
toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
toolkit/components/aboutperformance/tests/browser/browser_compartments.html
toolkit/components/aboutperformance/tests/browser/browser_compartments.js
toolkit/components/aboutperformance/tests/browser/head.js
toolkit/components/aboutperformance/tests/xpcshell/test_compartments.js
toolkit/components/aboutperformance/tests/xpcshell/xpcshell.ini
toolkit/components/build/moz.build
toolkit/components/build/nsToolkitCompsModule.cpp
toolkit/components/moz.build
toolkit/components/telemetry/Histograms.json
toolkit/modules/AddonWatcher.jsm
toolkit/modules/PerformanceStats.jsm
toolkit/modules/moz.build
toolkit/modules/tests/browser/browser.ini
toolkit/modules/tests/browser/browser_AddonWatcher.js
toolkit/modules/tests/browser/browser_Addons_sample.xpi
xpcom/build/XPCOMInit.cpp
xpcom/build/XPCOMModule.inc
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -140,44 +140,43 @@
 @RESPATH@/components/components.manifest
 @RESPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN32
 @BINPATH@/AccessibleMarshal.dll
 #endif
 @RESPATH@/components/accessibility.xpt
 #endif
-@RESPATH@/components/appshell.xpt
-@RESPATH@/components/appstartup.xpt
-@RESPATH@/components/autocomplete.xpt
-@RESPATH@/components/autoconfig.xpt
-@RESPATH@/components/browsercompsbase.xpt
-@RESPATH@/components/browser-element.xpt
-@RESPATH@/components/browser-feeds.xpt
-@RESPATH@/components/caps.xpt
-@RESPATH@/components/chardet.xpt
-@RESPATH@/components/chrome.xpt
-@RESPATH@/components/commandhandler.xpt
-@RESPATH@/components/commandlines.xpt
-@RESPATH@/components/compartments.xpt
-@RESPATH@/components/composer.xpt
-@RESPATH@/components/content_events.xpt
-@RESPATH@/components/content_html.xpt
-@RESPATH@/components/content_xslt.xpt
-@RESPATH@/components/cookie.xpt
-@RESPATH@/components/devtools_security.xpt
-@RESPATH@/components/directory.xpt
-@RESPATH@/components/diskspacewatcher.xpt
-@RESPATH@/components/docshell.xpt
-@RESPATH@/components/dom.xpt
-@RESPATH@/components/dom_activities.xpt
-@RESPATH@/components/dom_apps.xpt
-@RESPATH@/components/dom_audiochannel.xpt
-@RESPATH@/components/dom_base.xpt
-@RESPATH@/components/dom_system.xpt
+@BINPATH@/components/appshell.xpt
+@BINPATH@/components/appstartup.xpt
+@BINPATH@/components/autocomplete.xpt
+@BINPATH@/components/autoconfig.xpt
+@BINPATH@/components/browsercompsbase.xpt
+@BINPATH@/components/browser-element.xpt
+@BINPATH@/components/browser-feeds.xpt
+@BINPATH@/components/caps.xpt
+@BINPATH@/components/chardet.xpt
+@BINPATH@/components/chrome.xpt
+@BINPATH@/components/commandhandler.xpt
+@BINPATH@/components/commandlines.xpt
+@BINPATH@/components/composer.xpt
+@BINPATH@/components/content_events.xpt
+@BINPATH@/components/content_html.xpt
+@BINPATH@/components/content_xslt.xpt
+@BINPATH@/components/cookie.xpt
+@BINPATH@/components/devtools_security.xpt
+@BINPATH@/components/directory.xpt
+@BINPATH@/components/diskspacewatcher.xpt
+@BINPATH@/components/docshell.xpt
+@BINPATH@/components/dom.xpt
+@BINPATH@/components/dom_activities.xpt
+@BINPATH@/components/dom_apps.xpt
+@BINPATH@/components/dom_audiochannel.xpt
+@BINPATH@/components/dom_base.xpt
+@BINPATH@/components/dom_system.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 #ifdef MOZ_B2G_RIL
 @RESPATH@/components/dom_wappush.xpt
 @RESPATH@/components/dom_mobileconnection.xpt
 #endif
@@ -321,16 +320,17 @@
 @RESPATH@/components/spellchecker.xpt
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/telemetry.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
+@RESPATH@/components/toolkit_perfmonitoring.xpt
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -1,12 +1,13 @@
 
 
 let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+let AddonWatcher = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}).AddonWatcher;
 
 const ADDON_TYPE_SERVICE     = "service";
 const ID_SUFFIX              = "@services.mozilla.org";
 const STRING_TYPE_NAME       = "type.%ID%.name";
 const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 
 let manifest = {
   name: "provider 1",
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -175,17 +175,16 @@
 @RESPATH@/components/browser-element.xpt
 @RESPATH@/browser/components/browsercompsbase.xpt
 @RESPATH@/browser/components/browser-feeds.xpt
 @RESPATH@/browser/components/browsermodules.manifest
 @RESPATH@/components/caps.xpt
 @RESPATH@/components/chrome.xpt
 @RESPATH@/components/commandhandler.xpt
 @RESPATH@/components/commandlines.xpt
-@RESPATH@/components/compartments.xpt
 @RESPATH@/components/composer.xpt
 @RESPATH@/components/content_events.xpt
 @RESPATH@/components/content_html.xpt
 @RESPATH@/components/content_geckomediaplugins.xpt
 #ifdef MOZ_WEBRTC
 @RESPATH@/components/content_webrtc.xpt
 #endif
 @RESPATH@/components/content_xslt.xpt
@@ -323,16 +322,17 @@
 @RESPATH@/components/shistory.xpt
 @RESPATH@/components/spellchecker.xpt
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
+@RESPATH@/components/toolkit_perfmonitoring.xpt
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 @RESPATH@/components/uconv.xpt
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -70,17 +70,17 @@ static RedirEntry kRedirMap[] = {
       nsIAboutModule::ALLOW_SCRIPT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT
   },
   {
     "memory", "chrome://global/content/aboutMemory.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
-    "compartments", "chrome://global/content/aboutCompartments.xhtml",
+    "performance", "chrome://global/content/aboutPerformance.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "addons", "chrome://mozapps/content/extensions/extensions.xul",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -161,19 +161,18 @@ const mozilla::Module::ContractIDEntry k
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "credits", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "mozilla", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "logo", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "buildconfig", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
-  { NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
-  { NS_ABOUT_MODULE_CONTRACTID_PREFIX "compartments", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+  { NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -118,17 +118,16 @@
 @BINPATH@/components/browsercompsbase.xpt
 @BINPATH@/components/browser-element.xpt
 @BINPATH@/components/browser-feeds.xpt
 @BINPATH@/components/caps.xpt
 @BINPATH@/components/chardet.xpt
 @BINPATH@/components/chrome.xpt
 @BINPATH@/components/commandhandler.xpt
 @BINPATH@/components/commandlines.xpt
-@BINPATH@/components/compartments.xpt
 @BINPATH@/components/composer.xpt
 @BINPATH@/components/content_events.xpt
 @BINPATH@/components/content_geckomediaplugins.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_webrtc.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
@@ -249,16 +248,17 @@
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/telemetry.xpt
 @BINPATH@/components/toolkit_asyncshutdown.xpt
 @BINPATH@/components/toolkit_filewatcher.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
 @BINPATH@/components/toolkit_formautofill.xpt
 @BINPATH@/components/toolkit_osfile.xpt
+@BINPATH@/components/toolkit_perfmonitoring.xpt
 @BINPATH@/components/toolkit_xulstore.xpt
 @BINPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @BINPATH@/components/toolkitremote.xpt
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 @BINPATH@/components/uconv.xpt
deleted file mode 100644
--- a/toolkit/components/aboutcompartments/content/aboutCompartments.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
-/* 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/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
-
-function go() {
-    let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
-            .getService(Ci.nsICompartmentInfo);
-    let compartments = compartmentInfo.getCompartments();
-    let count = compartments.length;
-    let addons = {};
-    for (let i = 0; i < count; i++) {
-        let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
-        if (addons[compartment.addonId]) {
-            addons[compartment.addonId].time += compartment.time;
-            addons[compartment.addonId].CPOWTime += compartment.CPOWTime;
-            addons[compartment.addonId].compartments.push(compartment);
-        } else {
-            addons[compartment.addonId] = {
-                time: compartment.time,
-                CPOWTime: compartment.CPOWTime,
-                compartments: [compartment]
-            };
-        }
-    }
-    let dataDiv = document.getElementById("data");
-    for (let addon in addons) {
-        let el = document.createElement("tr");
-        let name = document.createElement("td");
-        let time = document.createElement("td");
-        let cpow = document.createElement("td");
-        name.className = "addon";
-        time.className = "time";
-        cpow.className = "cpow";
-        name.textContent = addon;
-        AddonManager.getAddonByID(addon, function(a) {
-            if (a) {
-                name.textContent = a.name;
-            }
-        });
-        time.textContent = addons[addon].time +"μs";
-        cpow.textContent = addons[addon].CPOWTime +"μs";
-        el.appendChild(time);
-        el.appendChild(cpow);
-        el.appendChild(name);
-        let div = document.createElement("tr");
-        for (let comp of addons[addon].compartments) {
-            let c = document.createElement("tr");
-            let name = document.createElement("td");
-            let time = document.createElement("td");
-            let cpow = document.createElement("td");
-            name.className = "addon";
-            time.className = "time";
-            cpow.className = "cpow";
-            name.textContent = comp.compartmentName;
-            time.textContent = comp.time +"μs";
-            cpow.textContent = comp.CPOWTime +"μs";
-            c.appendChild(time);
-            c.appendChild(cpow);
-            c.appendChild(name);
-            div.appendChild(c);
-            div.className = "details";
-        }
-        el.addEventListener("click", function() { div.style.display = (div.style.display != "block" ? "block" : "none"); });
-        el.appendChild(div);
-        dataDiv.appendChild(el);
-    }
-}
deleted file mode 100644
--- a/toolkit/components/aboutcompartments/content/aboutCompartments.xhtml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>about:compartments</title>
-    <script type="text/javascript;version=1.8" src="chrome://global/content/aboutCompartments.js"></script>
-    <style>
-      td.addon {
-        display: inline-block;
-        width: 400px;
-      }
-      td.time {
-        display: inline-block;
-        width: 100px;
-      }
-      td.cpow {
-        display: inline-block;
-        width: 100px;
-      }
-      .header {
-        font-weight: bold;
-      }
-      tr.details {
-        font-weight: lighter;
-        color: gray;
-        display: none;
-      }
-    </style>
-  </head>
-  <body onload="go()">
-    <table id="data">
-      <tr class="header">
-        <td class="time">time</td>
-        <td class="cpow">time in CPOWs</td>
-        <td class="addon">name</td>
-      </tr>
-    </table>
-  </body>
-</html>
deleted file mode 100644
--- a/toolkit/components/aboutcompartments/moz.build
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-XPIDL_MODULE = 'compartments'
-
-XPIDL_SOURCES += [
-    'nsICompartmentInfo.idl',
-]
-
-UNIFIED_SOURCES += [
-    'nsCompartmentInfo.cpp'
-]
-
-EXPORTS += [
-    'nsCompartmentInfo.h'
-]
-
-FINAL_LIBRARY = 'xul'
deleted file mode 100644
--- a/toolkit/components/aboutcompartments/nsCompartmentInfo.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; 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 "nsCompartmentInfo.h"
-#include "nsMemory.h"
-#include "nsLiteralString.h"
-#include "nsCRTGlue.h"
-#include "nsIJSRuntimeService.h"
-#include "nsServiceManagerUtils.h"
-#include "nsIMutableArray.h"
-#include "nsJSUtils.h"
-#include "xpcpublic.h"
-
-class nsCompartment : public nsICompartment {
-public:
-  nsCompartment(nsAString& aCompartmentName, nsAString& aAddonId,
-                uint64_t aTime, uint64_t aCPOWTime)
-    : mCompartmentName(aCompartmentName), mAddonId(aAddonId), mTime(aTime), mCPOWTime(aCPOWTime) {}
-
-  NS_DECL_ISUPPORTS
-
-  /* readonly attribute wstring compartmentName; */
-  NS_IMETHOD GetCompartmentName(nsAString& aCompartmentName) override {
-    aCompartmentName.Assign(mCompartmentName);
-    return NS_OK;
-  };
-
-  /* readonly attribute unsigned long time; */
-  NS_IMETHOD GetTime(uint64_t* aTime) override {
-    *aTime = mTime;
-    return NS_OK;
-  }
-  /* readonly attribute wstring addon id; */
-  NS_IMETHOD GetAddonId(nsAString& aAddonId) override {
-    aAddonId.Assign(mAddonId);
-    return NS_OK;
-  };
-
-  /* readonly attribute unsigned long CPOW time; */
-  NS_IMETHOD GetCPOWTime(uint64_t* aCPOWTime) override {
-    *aCPOWTime = mCPOWTime;
-    return NS_OK;
-  }
-
-private:
-  nsString mCompartmentName;
-  nsString mAddonId;
-  uint64_t mTime;
-  uint64_t mCPOWTime;
-  virtual ~nsCompartment() {}
-};
-
-NS_IMPL_ISUPPORTS(nsCompartment, nsICompartment)
-NS_IMPL_ISUPPORTS(nsCompartmentInfo, nsICompartmentInfo)
-
-nsCompartmentInfo::nsCompartmentInfo()
-{
-}
-
-nsCompartmentInfo::~nsCompartmentInfo()
-{
-}
-
-NS_IMETHODIMP
-nsCompartmentInfo::GetCompartments(nsIArray** aCompartments)
-{
-  JSRuntime* rt;
-  nsCOMPtr<nsIJSRuntimeService> svc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
-  NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
-  svc->GetRuntime(&rt);
-  nsCOMPtr<nsIMutableArray> compartments = do_CreateInstance(NS_ARRAY_CONTRACTID);
-  CompartmentStatsVector stats;
-  if (!JS_GetCompartmentStats(rt, stats))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  size_t num = stats.length();
-  for (size_t pos = 0; pos < num; pos++) {
-    nsString addonId;
-    if (stats[pos].addonId) {
-      AssignJSFlatString(addonId, (JSFlatString*)stats[pos].addonId);
-    } else {
-      addonId.AssignLiteral("<non-addon>");
-    }
-
-    uint32_t cpowTime = xpc::GetCompartmentCPOWMicroseconds(stats[pos].compartment);
-    nsCString compartmentName(stats[pos].compartmentName);
-    NS_ConvertUTF8toUTF16 name(compartmentName);
-    compartments->AppendElement(new nsCompartment(name, addonId, stats[pos].time, cpowTime), false);
-  }
-  compartments.forget(aCompartments);
-  return NS_OK;
-}
deleted file mode 100644
--- a/toolkit/components/aboutcompartments/nsICompartmentInfo.idl
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-#include "nsIArray.idl"
-
-[scriptable, uuid(13dd4c09-ff11-4943-8dc2-d96eb69c963b)]
-interface nsICompartment : nsISupports {
-  /* name of compartment */
-  readonly attribute AString compartmentName;
-  /* time spent executing code in this compartment in microseconds */
-  readonly attribute unsigned long long time;
-  /* the id of the addon associated with this compartment, or null */
-  readonly attribute AString addonId;
-  /* time spent processing CPOWs in microseconds */
-  readonly attribute unsigned long long CPOWTime;
-};
-
-[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
-interface nsICompartmentInfo : nsISupports {
-  nsIArray getCompartments();
-};
-
-%{C++
-#define NS_COMPARTMENT_INFO_CID \
-{ 0x2d3c2f2d, 0x698d, 0x471d, \
-{ 0xba, 0x3e, 0x14, 0x44, 0xdd, 0x52, 0x1e, 0x29 } }
-%}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -0,0 +1,157 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
+/* 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/. */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {});
+
+/**
+ * The various measures we display.
+ */
+const MEASURES = [
+  {key: "longestDuration", percentOfDeltaT: false, label: "Jank level"},
+  {key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"},
+  {key: "totalSystemTime", percentOfDeltaT: true, label: "System (%)"},
+  {key: "totalCPOWTime", percentOfDeltaT: true, label: "Cross-Process (%)"},
+  {key: "ticks", percentOfDeltaT: false, label: "Activations"},
+];
+
+let State = {
+  /**
+   * @type{PerformanceData}
+   */
+  _processData: null,
+  /**
+   * A mapping from name to PerformanceData
+   *
+   * @type{Map}
+   */
+  _componentsData: new Map(),
+
+  /**
+   * A number of milliseconds since the high-performance epoch.
+   */
+  _date: window.performance.now(),
+
+  /**
+   * Fetch the latest information, compute diffs.
+   *
+   * @return {object} An object with the following fields:
+   * - `components`: an array of `PerformanceDiff` representing
+   *   the components, sorted by `longestDuration`, then by `totalUserTime`
+   * - `process`: a `PerformanceDiff` representing the entire process;
+   * - `deltaT`: the number of milliseconds elapsed since the data
+   *   was last displayed.
+   */
+  update: function() {
+    let snapshot = PerformanceStats.getSnapshot();
+    let newData = new Map();
+    let deltas = [];
+    for (let componentNew of snapshot.componentsData) {
+      let componentOld = State._componentsData.get(componentNew.name);
+      deltas.push(componentNew.substract(componentOld));
+      newData.set(componentNew.name, componentNew);
+    }
+    State._componentsData = newData;
+    let now = window.performance.now();
+    let result = {
+      components: deltas.filter(x => x.ticks > 0),
+      process: snapshot.processData.substract(State._processData),
+      deltaT: now - State._date
+    };
+    result.components.sort((a, b) => {
+      if (a.longestDuration < b.longestDuration) {
+        return true;
+      }
+      if (a.longestDuration == b.longestDuration) {
+        return a.totalUserTime <= b.totalUserTime
+      }
+      return false;
+    });
+    State._processData = snapshot.processData;
+    State._date = now;
+    return result;
+  }
+};
+
+
+function update() {
+  try {
+    let dataElt = document.getElementById("data");
+    dataElt.innerHTML = "";
+
+    // Generate table headers
+    let headerElt = document.createElement("tr");
+    dataElt.appendChild(headerElt);
+    headerElt.classList.add("header");
+    for (let column of [...MEASURES, {key: "name", name: ""}]) {
+      let el = document.createElement("td");
+      el.classList.add(column.key);
+      el.textContent = column.label;
+      headerElt.appendChild(el);
+    }
+
+    let deltas = State.update();
+
+    for (let item of deltas.components) {
+      let row = document.createElement("tr");
+      if (item.addonId) {
+        row.classList.add("addon");
+      } else if (item.isSystem) {
+        row.classList.add("platform");
+      } else {
+        row.classList.add("content");
+      }
+      dataElt.appendChild(row);
+
+      // Measures
+      for (let {key, percentOfDeltaT} of MEASURES) {
+        let el = document.createElement("td");
+        el.classList.add(key);
+        el.classList.add("contents");
+        row.appendChild(el);
+
+        let value = percentOfDeltaT ? Math.round(item[key] / deltas.deltaT) : item[key];
+        if (key == "longestDuration") {
+          value += 1;
+          el.classList.add("jank" + value);
+        }
+        el.textContent = value;
+      }
+
+      // Name
+      let el = document.createElement("td");
+      let id = item.id;
+      el.classList.add("contents");
+      el.classList.add("name");
+      row.appendChild(el);
+      if (item.addonId) {
+        let _el = el;
+        let _item = item;
+        AddonManager.getAddonByID(item.addonId, a => {
+          _el.textContent = a?a.name:_item.name
+        });
+      } else {
+        el.textContent = item.name;
+      }
+    }
+  } catch (ex) {
+    console.error(ex);
+  }
+}
+
+function go() {
+  // Compute initial state immediately, then wait a little
+  // before we start computing diffs and refreshing.
+  State.update();
+
+  window.setTimeout(() => {
+    window.setInterval(update, 2000);
+  }, 1000);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>about:performance</title>
+    <script type="text/javascript;version=1.8" src="chrome://global/content/aboutPerformance.js"></script>
+    <style>
+      td.addon {
+        display: inline-block;
+        width: 400px;
+      }
+      td.time {
+        display: inline-block;
+        width: 100px;
+      }
+      td.cpow {
+        display: inline-block;
+        width: 100px;
+      }
+      .header {
+        font-weight: bold;
+      }
+      tr.details {
+        font-weight: lighter;
+        color: gray;
+        display: none;
+      }
+      tr.addon {
+        background-color: white;
+      }
+      tr.platform {
+        background-color: rgb(255, 255, 200);
+      }
+      tr.content {
+        background-color: rgb(200, 255, 255);
+      }
+      td.jank0 {
+        color: rgb(0, 0, 0);
+      }
+      td.jank1 {
+        color: rgb(25, 0, 0);
+      }
+      td.jank2 {
+        color: rgb(50, 0, 0);
+      }
+      td.jank3 {
+        color: rgb(75, 0, 0);
+      }
+      td.jank4 {
+        color: rgb(100, 0, 0);
+      }
+      td.jank5 {
+        color: rgb(125, 0, 0);
+      }
+      td.jank6 {
+        color: rgb(150, 0, 0);
+      }
+      td.jank7 {
+        color: rgb(175, 0, 0);
+      }
+      td.jank8 {
+        color: rgb(200, 0, 0);
+      }
+      td.jank9 {
+        color: rgb(225, 0, 0);
+      }
+      td.jank9 {
+        color: rgb(255, 0, 0);
+      }
+    </style>
+  </head>
+  <body onload="go()">
+    <table id="data">
+    </table>
+  </body>
+</html>
rename from toolkit/components/aboutcompartments/jar.mn
rename to toolkit/components/aboutperformance/jar.mn
--- a/toolkit/components/aboutcompartments/jar.mn
+++ b/toolkit/components/aboutperformance/jar.mn
@@ -1,7 +1,7 @@
 # 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/.
 
 toolkit.jar:
-+ content/global/aboutCompartments.xhtml               (content/aboutCompartments.xhtml)
-+ content/global/aboutCompartments.js                  (content/aboutCompartments.js)
++ content/global/aboutPerformance.xhtml               (content/aboutPerformance.xhtml)
++ content/global/aboutPerformance.js                  (content/aboutPerformance.js)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+FAIL_ON_WARNINGS = True
+
+JAR_MANIFESTS += ['jar.mn']
+
+XPIDL_MODULE = 'toolkit_perfmonitoring'
+
+XPIDL_SOURCES += [
+    'nsIPerformanceStats.idl',
+]
+
+UNIFIED_SOURCES += [
+    'nsPerformanceStats.cpp'
+]
+
+EXPORTS += [
+    'nsPerformanceStats.h'
+]
+
+BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/nsIPerformanceStats.idl
@@ -0,0 +1,111 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIArray.idl"
+
+/**
+ * Mechanisms for querying the current process about performance
+ * information.
+ *
+ * JavaScript clients should rather use PerformanceStats.jsm.
+ */
+
+/**
+ * Snapshot of the performance of a component, e.g. an add-on, a web
+ * page, system built-ins, or the entire process itself.
+ *
+ * All values are monotonic and are updated only when
+ * `nsIPerformanceStatsService.isStopwatchActive` is `true`.
+ */
+[scriptable, uuid(f015cbad-e16f-4982-a080-67e4e69a5b2e)]
+interface nsIPerformanceStats: nsISupports {
+  /**
+   * The name of the component:
+   * - for the process itself, "<process>";
+   * - for platform code, "<platform>";
+   * - for an add-on, the identifier of the addon (e.g. "myaddon@foo.bar");
+   * - for a webpage, the url of the page.
+   */
+  readonly attribute AString name;
+
+  /**
+   * If the component is an add-on, the ID of the addon,
+   * otherwise an empty string.
+   */
+  readonly attribute AString addonId;
+
+  /**
+   * Total amount of time spent executing code in this group, in
+   * microseconds.
+   */
+  readonly attribute unsigned long long totalUserTime;
+  readonly attribute unsigned long long totalSystemTime;
+  readonly attribute unsigned long long totalCPOWTime;
+
+  /**
+   * Total number of times code execution entered this group,
+   * since process launch. This may be greater than the number
+   * of times we have entered the event loop.
+   */
+  readonly attribute unsigned long long ticks;
+
+  /**
+   * `true` if this component is executed with system privileges
+   * (e.g. the platform itself or an add-on), `false` otherwise
+   * (e.g. webpages).
+   */
+  readonly attribute bool isSystem;
+
+  /**
+   * Jank indicator.
+   *
+   * durations[i] == number of times execution of this group
+   * lasted at lest 2^i ms.
+   */
+  void getDurations([optional] out unsigned long aCount,
+                    [retval, array, size_is(aCount)]out unsigned long long aNumberOfOccurrences);
+};
+
+/**
+ * A snapshot of the performance data of the process.
+ */
+[scriptable, uuid(29ecebd0-908a-4b34-8f62-a6015dea1141)]
+interface nsIPerformanceSnapshot: nsISupports {
+  /**
+   * Data on all individual components.
+   */
+  nsIArray getComponentsData();
+
+  /**
+   * Information on the process itself.
+   *
+   * This contains the total amount of time spent executing JS code,
+   * the total amount of time spent waiting for system calls while
+   * executing JS code, the total amount of time performing blocking
+   * inter-process calls, etc.
+   */
+  nsIPerformanceStats getProcessData();
+};
+
+[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
+interface nsIPerformanceStatsService : nsISupports {
+  /**
+   * `true` if we should monitor performance, `false` otherwise.
+   */
+  [implicit_jscontext] attribute bool isStopwatchActive;
+
+  /**
+   * Capture a snapshot of the performance data.
+   */
+  nsIPerformanceSnapshot getSnapshot();
+};
+
+%{C++
+#define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID {0xfd7435d4, 0x9ec4, 0x4699, \
+      {0xad, 0xd4, 0x1b, 0xe8, 0x3d, 0xd6, 0x8e, 0xf3} }
+#define NS_TOOLKIT_PERFORMANCESTATSSERVICE_CONTRACTID "@mozilla.org/toolkit/performance-stats-service;1"
+%}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/nsPerformanceStats.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; 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 "jsapi.h"
+#include "nsPerformanceStats.h"
+#include "nsMemory.h"
+#include "nsLiteralString.h"
+#include "nsCRTGlue.h"
+#include "nsIJSRuntimeService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMArray.h"
+#include "nsIMutableArray.h"
+#include "nsJSUtils.h"
+#include "xpcpublic.h"
+#include "jspubtd.h"
+
+class nsPerformanceStats: public nsIPerformanceStats {
+public:
+  nsPerformanceStats(nsAString& aName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData& aPerformanceData)
+    : mName(aName)
+    , mAddonId(aAddonId)
+    , mIsSystem(aIsSystem)
+    , mPerformanceData(aPerformanceData)
+  {
+  }
+  explicit nsPerformanceStats() {}
+
+  NS_DECL_ISUPPORTS
+
+  /* readonly attribute AString name; */
+  NS_IMETHOD GetName(nsAString& aName) override {
+    aName.Assign(mName);
+    return NS_OK;
+  };
+
+  /* readonly attribute AString addon id; */
+  NS_IMETHOD GetAddonId(nsAString& aAddonId) override {
+    aAddonId.Assign(mAddonId);
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long totalUserTime; */
+  NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override {
+    *aTotalUserTime = mPerformanceData.totalUserTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long totalSystemTime; */
+  NS_IMETHOD GetTotalSystemTime(uint64_t *aTotalSystemTime) override {
+    *aTotalSystemTime = mPerformanceData.totalSystemTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long totalCPOWTime; */
+  NS_IMETHOD GetTotalCPOWTime(uint64_t *aCpowTime) override {
+    *aCpowTime = mPerformanceData.totalCPOWTime;
+    return NS_OK;
+  };
+
+  /* readonly attribute unsigned long long ticks; */
+  NS_IMETHOD GetTicks(uint64_t *aTicks) override {
+    *aTicks = mPerformanceData.ticks;
+    return NS_OK;
+  };
+
+  /* void getDurations (out unsigned long aCount, [array, size_is (aCount), retval] out unsigned long long aNumberOfOccurrences); */
+  NS_IMETHODIMP GetDurations(uint32_t *aCount, uint64_t **aNumberOfOccurrences) override {
+    const size_t length = mozilla::ArrayLength(mPerformanceData.durations);
+    if (aCount) {
+      *aCount = length;
+    }
+    *aNumberOfOccurrences = new uint64_t[length];
+    for (size_t i = 0; i < length; ++i) {
+      (*aNumberOfOccurrences)[i] = mPerformanceData.durations[i];
+    }
+    return NS_OK;
+  };
+
+  /* readonly attribute bool isSystem; */
+  NS_IMETHOD GetIsSystem(bool *_retval) override {
+    *_retval = mIsSystem;
+    return NS_OK;
+  }
+
+private:
+  nsString mName;
+  nsString mAddonId;
+  bool mIsSystem;
+  js::PerformanceData mPerformanceData;
+
+  virtual ~nsPerformanceStats() {}
+};
+
+NS_IMPL_ISUPPORTS(nsPerformanceStats, nsIPerformanceStats)
+
+
+class nsPerformanceSnapshot : public nsIPerformanceSnapshot
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPERFORMANCESNAPSHOT
+
+  nsPerformanceSnapshot();
+  nsresult Init();
+private:
+  virtual ~nsPerformanceSnapshot();
+
+  /**
+   * Import a `PerformanceStats` as a `nsIPerformanceStats`.
+   */
+  already_AddRefed<nsIPerformanceStats> ImportStats(js::PerformanceStats* c);
+
+  nsCOMArray<nsIPerformanceStats> mComponentsData;
+  nsCOMPtr<nsIPerformanceStats> mProcessData;
+};
+
+NS_IMPL_ISUPPORTS(nsPerformanceSnapshot, nsIPerformanceSnapshot)
+
+nsPerformanceSnapshot::nsPerformanceSnapshot()
+{
+}
+
+nsPerformanceSnapshot::~nsPerformanceSnapshot()
+{
+}
+
+already_AddRefed<nsIPerformanceStats>
+nsPerformanceSnapshot::ImportStats(js::PerformanceStats* c) {
+  nsString addonId;
+  if (c->addonId) {
+    AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
+  }
+  nsCString cname(c->name);
+  NS_ConvertUTF8toUTF16 name(cname);
+  nsCOMPtr<nsIPerformanceStats> result = new nsPerformanceStats(name, addonId, c->isSystem, c->performance);
+  return result.forget();
+}
+
+nsresult
+nsPerformanceSnapshot::Init() {
+  JSRuntime* rt;
+  nsCOMPtr<nsIJSRuntimeService> svc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+  NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
+  svc->GetRuntime(&rt);
+  js::PerformanceStats processStats;
+  js::PerformanceStatsVector componentsStats;
+  if (!js::GetPerformanceStats(rt, componentsStats, processStats)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  size_t num = componentsStats.length();
+  for (size_t pos = 0; pos < num; pos++) {
+    nsCOMPtr<nsIPerformanceStats> stats = ImportStats(&componentsStats[pos]);
+    mComponentsData.AppendObject(stats);
+  }
+  mProcessData = ImportStats(&processStats);
+  return NS_OK;
+}
+
+
+/* void getComponentsData (out nsIArray aComponents); */
+NS_IMETHODIMP nsPerformanceSnapshot::GetComponentsData(nsIArray * *aComponents)
+{
+  const size_t length = mComponentsData.Length();
+  nsCOMPtr<nsIMutableArray> components = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  for (size_t i = 0; i < length; ++i) {
+    nsCOMPtr<nsIPerformanceStats> stats = mComponentsData[i];
+    mozilla::DebugOnly<nsresult> rv = components->AppendElement(stats, false);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+  components.forget(aComponents);
+  return NS_OK;
+}
+
+/* readonly attribute nsIPerformanceStats process; */
+NS_IMETHODIMP nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProcess)
+{
+  NS_IF_ADDREF(*aProcess = mProcessData);
+  return NS_OK;
+}
+
+
+NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService)
+
+nsPerformanceStatsService::nsPerformanceStatsService()
+{
+}
+
+nsPerformanceStatsService::~nsPerformanceStatsService()
+{
+}
+
+/* [implicit_jscontext] attribute bool isStopwatchActive; */
+NS_IMETHODIMP nsPerformanceStatsService::GetIsStopwatchActive(JSContext* cx, bool *aIsStopwatchActive)
+{
+  JSRuntime *runtime = JS_GetRuntime(cx);
+  *aIsStopwatchActive = js::IsStopwatchActive(runtime);
+  return NS_OK;
+}
+NS_IMETHODIMP nsPerformanceStatsService::SetIsStopwatchActive(JSContext* cx, bool aIsStopwatchActive)
+{
+  JSRuntime *runtime = JS_GetRuntime(cx);
+  if (!js::SetStopwatchActive(runtime, aIsStopwatchActive)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  return NS_OK;
+}
+
+/* readonly attribute nsIPerformanceSnapshot snapshot; */
+NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(nsIPerformanceSnapshot * *aSnapshot)
+{
+  nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
+  nsresult rv = snapshot->Init();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  snapshot.forget(aSnapshot);
+  return NS_OK;
+}
+
+
rename from toolkit/components/aboutcompartments/nsCompartmentInfo.h
rename to toolkit/components/aboutperformance/nsPerformanceStats.h
--- a/toolkit/components/aboutcompartments/nsCompartmentInfo.h
+++ b/toolkit/components/aboutperformance/nsPerformanceStats.h
@@ -1,25 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; 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 nsCompartmentInfo_h
-#define nsCompartmentInfo_h
+#ifndef nsPerformanceStats_h
+#define nsPerformanceStats_h
 
-#include "nsICompartmentInfo.h"
+#include "nsIPerformanceStats.h"
 
-class nsCompartmentInfo : public nsICompartmentInfo
+class nsPerformanceStatsService : public nsIPerformanceStatsService
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSICOMPARTMENTINFO
+  NS_DECL_NSIPERFORMANCESTATSSERVICE
 
-  nsCompartmentInfo();
+  nsPerformanceStatsService();
 
 private:
-  virtual ~nsCompartmentInfo();
+  virtual ~nsPerformanceStatsService();
 
 protected:
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/browser/browser.ini
@@ -0,0 +1,12 @@
+# 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/.
+
+[DEFAULT]
+head = head.js
+support-files =
+  browser_compartments.html
+
+[browser_aboutperformance.js]
+skip-if = e10s # Feature not implemented yet – bug 1140310
+[browser_compartments.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://testing-common/ContentTask.jsm", this);
+
+const URL = "http://example.com/browser/toolkit/components/aboutperformance/tests/browser/browser_compartments.html?test=" + Math.random();
+
+// This function is injected as source as a frameScript
+function frameScript() {
+  "use strict";
+
+  addMessageListener("aboutperformance-test:hasItems", ({data: url}) => {
+    let hasPlatform = false;
+    let hasURL = false;
+
+    try {
+      let eltData = content.document.getElementById("data");
+      if (!eltData) {
+        return;
+      }
+
+      // Find if we have a row for "platform"
+      hasPlatform = eltData.querySelector("tr.platform") != null;
+
+      // Find if we have a row for our URL
+      hasURL = false;
+      for (let eltContent of eltData.querySelectorAll("tr.content td.name")) {
+        if (eltContent.textContent == url) {
+          hasURL = true;
+          break;
+        }
+      }
+
+    } catch (ex) {
+      Cu.reportError("Error in content: " + ex);
+      Cu.reportError(ex.stack);
+    } finally {
+      sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasURL});
+    }
+  });
+}
+
+
+add_task(function* test() {
+  let tabAboutPerformance = gBrowser.addTab("about:performance");
+  let tabContent = gBrowser.addTab(URL);
+
+  yield ContentTask.spawn(tabAboutPerformance.linkedBrowser, null, frameScript);
+
+  while (true) {
+    yield new Promise(resolve => setTimeout(resolve, 100));
+    let {hasPlatform, hasURL} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", URL));
+    info(`Platform: ${hasPlatform}, url: ${hasURL}`);
+    if (hasPlatform && hasURL) {
+      Assert.ok(true, "Found a row for <platform> and a row for our URL");
+      break;
+    }
+  }
+
+  // Cleanup
+  gBrowser.removeTab(tabContent);
+  gBrowser.removeTab(tabAboutPerformance);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>
+    browser_compartments.html
+  </title>
+  <script type="text/javascript">
+  // Use some CPU.
+  window.setInterval(() => {
+    // Compute an arbitrary value, print it out to make sure that the JS
+    // engine doesn't discard all our computation.
+    var date = Date.now();
+    var array = [];
+    var i = 0;
+    while (Date.now() - date <= 100) {
+      array[i%2] = i++;
+    }
+    console.log("Arbitrary value", array);
+  }, 300);
+  </script>
+</head>
+<body>
+browser_compartments.html
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/PerformanceStats.jsm", this);
+Cu.import("resource://testing-common/ContentTask.jsm", this);
+
+const URL = "http://example.com/browser/toolkit/components/aboutperformance/tests/browser/browser_compartments.html?test=" + Math.random();
+
+// This function is injected as source as a frameScript
+function frameScript() {
+  "use strict";
+
+  const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
+  Cu.import("resource://gre/modules/PerformanceStats.jsm");
+
+  let performanceStatsService =
+    Cc["@mozilla.org/toolkit/performance-stats-service;1"].
+    getService(Ci.nsIPerformanceStatsService);
+
+  // Make sure that the stopwatch is now active.
+  performanceStatsService.isStopwatchActive = true;
+
+  addMessageListener("compartments-test:getStatistics", () => {
+    try {
+      sendAsyncMessage("compartments-test:getStatistics", PerformanceStats.getSnapshot());
+    } catch (ex) {
+      Cu.reportError("Error in content: " + ex);
+      Cu.reportError(ex.stack);
+    }
+  });
+}
+
+function monotinicity_tester(source, name) {
+  // In the background, check invariants:
+  // - numeric data can only ever increase;
+  // - the name, addonId, isSystem of a component never changes;
+  // - the name, addonId, isSystem of the process data;
+  // - there is at most one component with a combination of `name` and `addonId`;
+  // - types, etc.
+  let previous = {
+    processData: null,
+    componentsMap: new Map(),
+  };
+
+  let sanityCheck = function(prev, next) {
+    if (prev == null) {
+      return;
+    }
+    for (let k of ["name", "addonId", "isSystem"]) {
+      Assert.equal(prev[k], next[k], `Sanity check (${name}): ${k} hasn't changed.`);
+    }
+    for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"]) {
+      Assert.equal(typeof next[k], "number", `Sanity check (${name}): ${k} is a number.`);
+      Assert.ok(prev[k] <= next[k], `Sanity check (${name}): ${k} is monotonic.`);
+      Assert.ok(next[k] >= 0, `Sanity check (${name}): ${k} is >= 0.`)
+    }
+    Assert.equal(prev.durations.length, next.durations.length);
+    for (let i = 0; i < next.durations.length; ++i) {
+      Assert.ok(typeof next.durations[i] == "number" && next.durations[i] >= 0,
+        `Sanity check (${name}): durations[${i}] is a non-negative number.`);
+      Assert.ok(prev.durations[i] <= next.durations[i],
+        `Sanity check (${name}): durations[${i}] is monotonic.`)
+    }
+    for (let i = 0; i < next.durations.length - 1; ++i) {
+      Assert.ok(next.durations[i] >= next.durations[i + 1],
+        `Sanity check (${name}): durations[${i}] >= durations[${i + 1}].`)
+    }
+  };
+  let frameCheck = Task.async(function*() {
+    let snapshot = yield source();
+    if (!snapshot) {
+      // This can happen at the end of the test when we attempt
+      // to communicate too late with the content process.
+      window.clearInterval(interval);
+      return;
+    }
+
+    // Sanity check on the process data.
+    sanityCheck(previous.processData, snapshot.processData);
+    Assert.equal(snapshot.processData.isSystem, true);
+    Assert.equal(snapshot.processData.name, "<process>");
+    Assert.equal(snapshot.processData.addonId, "");
+    previous.procesData = snapshot.processData;
+
+    // Sanity check on components data.
+    let set = new Set();
+    for (let item of snapshot.componentsData) {
+      let key = `{name: ${item.name}, addonId: ${item.addonId}}`;
+      set.add(key);
+      sanityCheck(previous.componentsMap.get(key), item);
+      previous.componentsMap.set(key, item);
+
+      for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime"]) {
+        Assert.ok(item[k] <= snapshot.processData[k],
+          `Sanity check (${name}): component has a lower ${k} than process`);
+      }
+      for (let i = 0; i < item.durations.length; ++i) {
+        Assert.ok(item.durations[i] <= snapshot.processData.durations[i],
+          `Sanity check (${name}): component has a lower durations[${i}] than process.`);
+      }
+    }
+    // Check that we do not have duplicate components.
+    Assert.equal(set.size, snapshot.componentsData.length);
+  });
+  let interval = window.setInterval(frameCheck, 300);
+  registerCleanupFunction(() => {
+    window.clearInterval(interval);
+  });
+}
+
+add_task(function* test() {
+  info("Extracting initial state");
+  let stats0 = PerformanceStats.getSnapshot();
+  Assert.notEqual(stats0.componentsData.length, 0, "There is more than one component");
+  Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1),
+    "The url doesn't appear yet");
+
+  let newTab = gBrowser.addTab();
+  let browser = newTab.linkedBrowser;
+  // Setup monitoring in the tab
+  info("Setting up monitoring in the tab");
+  yield ContentTask.spawn(newTab.linkedBrowser, null, frameScript);
+
+  info("Opening URL");
+  newTab.linkedBrowser.loadURI(URL);
+
+  info("Setting up monotonicity testing");
+  monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process");
+  monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
+
+  while (true) {
+    let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null));
+    let found = stats.componentsData.find(stat => {
+      return (stat.name.indexOf(URL) != -1)
+      && (stat.totalUserTime > 1000)
+    });
+    if (found) {
+      break;
+    }
+    yield new Promise(resolve => setTimeout(resolve, 100));
+  }
+
+  // Cleanup
+  gBrowser.removeTab(newTab);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/browser/head.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+function promiseContentResponse(browser, name, message) {
+  let mm = browser.messageManager;
+  let promise = new Promise(resolve => {
+    function removeListener() {
+      mm.removeMessageListener(name, listener);
+    }
+
+    function listener(msg) {
+      removeListener();
+      resolve(msg.data);
+    }
+
+    mm.addMessageListener(name, listener);
+    registerCleanupFunction(removeListener);
+  });
+  mm.sendAsyncMessage(name, message);
+  return promise;
+}
+function promiseContentResponseOrNull(browser, name, message) {
+  if (!browser.messageManager) {
+    return null;
+  }
+  return promiseContentResponse(browser, name, message);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/xpcshell/test_compartments.js
@@ -0,0 +1,116 @@
+"use strict";
+
+const {utils: Cu, interfaces: Ci, classes: Cc} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/PerformanceStats.jsm", this);
+
+function run_test() {
+  run_next_test();
+}
+
+let promiseStatistics = Task.async(function*(name) {
+  yield Promise.resolve(); // Make sure that we wait until
+  // statistics have been updated.
+  let snapshot = PerformanceStats.getSnapshot();
+  do_print("Statistics: " + name);
+  do_print(JSON.stringify(snapshot.processData, null, "\t"));
+  do_print(JSON.stringify(snapshot.componentsData, null, "\t"));
+  return snapshot;
+});
+
+let promiseSetMonitoring = Task.async(function*(to) {
+  Cc["@mozilla.org/toolkit/performance-stats-service;1"].
+    getService(Ci.nsIPerformanceStatsService).
+    isStopwatchActive = to;
+  yield Promise.resolve();
+});
+
+function getBuiltinStatistics(snapshot) {
+  let stats = snapshot.componentsData.find(stats =>
+    stats.isSystem && !stats.addonId
+  );
+  return stats;
+}
+
+function burnCPU(ms) {
+  do_print("Burning CPU");
+  let counter = 0;
+  let ignored = [];
+  let start = Date.now();
+  while (Date.now() - start < ms) {
+    ignored.push(0);
+    ignored.shift();
+    ++counter;
+  }
+  do_print("Burning CPU over, after " + counter + " iterations");
+}
+
+function ensureEquals(snap1, snap2, name) {
+  Assert.equal(
+    JSON.stringify(snap1.processData),
+    JSON.stringify(snap2.processData),
+    "Same process data: " + name);
+  let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name);
+  let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name);
+  Assert.equal(
+    JSON.stringify(stats1),
+    JSON.stringify(stats2),
+    "Same components data: " + name
+  );
+}
+
+add_task(function* test_measure() {
+  let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsInt32("version")];
+  do_print(`Running ${sysName} version ${sysVersion}`);
+
+  do_print("Burn CPU without the stopwatch");
+  yield promiseSetMonitoring(false);
+  let stats0 = yield promiseStatistics("Initial state");
+  burnCPU(300);
+  let stats1 = yield promiseStatistics("Initial state + burn, without stopwatch");
+
+  do_print("Burn CPU with the stopwatch");
+  yield promiseSetMonitoring(true);
+  burnCPU(300);
+  let stats2 = yield promiseStatistics("Second burn, with stopwatch");
+
+  do_print("Burn CPU without the stopwatch again")
+  yield promiseSetMonitoring(false);
+  let stats3 = yield promiseStatistics("Before third burn, without stopwatch");
+  burnCPU(300);
+  let stats4 = yield promiseStatistics("After third burn, without stopwatch");
+
+  ensureEquals(stats0, stats1, "Initial state vs. Initial state + burn, without stopwatch");
+  let process1 = stats1.processData;
+  let process2 = stats2.processData;
+  let process3 = stats3.processData;
+  let process4 = stats4.processData;
+  if (sysName == "WINNT" && sysVersion == "5.2") {
+    do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
+  } else {
+    Assert.ok(process2.totalUserTime - process1.totalUserTime >= 10000, `At least 10ms counted for process time (${process2.totalUserTime - process1.totalUserTime})`);
+  }
+  Assert.equal(process2.totalCPOWTime, process1.totalCPOWTime, "We haven't used any CPOW time during the first burn");
+  Assert.equal(process4.totalUserTime, process3.totalUserTime, "After deactivating the stopwatch, we didn't count any time");
+  Assert.equal(process4.totalCPOWTime, process3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time");
+
+  let builtin1 = getBuiltinStatistics(stats1) || { totalUserTime: 0, totalCPOWTime: 0 };
+  let builtin2 = getBuiltinStatistics(stats2);
+  let builtin3 = getBuiltinStatistics(stats3);
+  let builtin4 = getBuiltinStatistics(stats4);
+  Assert.notEqual(builtin2, null, "Found the statistics for built-ins 2");
+  Assert.notEqual(builtin3, null, "Found the statistics for built-ins 3");
+  Assert.notEqual(builtin4, null, "Found the statistics for built-ins 4");
+
+  if (sysName == "WINNT" && sysVersion == "5.2") {
+    do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
+  } else {
+    Assert.ok(builtin2.totalUserTime - builtin1.totalUserTime >= 10000, `At least 10ms counted for built-in statistics (${builtin2.totalUserTime - builtin1.totalUserTime})`);
+  }
+  Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "We haven't used any CPOW time during the first burn for the built-in");
+  Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics");
+  Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
+  Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/aboutperformance/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head=
+tail=
+
+[test_compartments.js]
--- a/toolkit/components/build/moz.build
+++ b/toolkit/components/build/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
 SOURCES += [
     'nsToolkitCompsModule.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../../xre',
+    '../aboutperformance',
     '../alerts',
     '../downloads',
     '../feeds',
     '../find',
     '../jsdownloads/src',
     '../protobuf',
     '../startup',
     '../statusfilter',
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -46,22 +46,26 @@
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 #define MOZ_HAS_TERMINATOR
 #endif
 
 #if defined(MOZ_HAS_TERMINATOR)
 #include "nsTerminator.h"
 #endif
 
+#include "nsPerformanceStats.h"
+
 using namespace mozilla;
 
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPerformanceStatsService)
+
 #if defined(MOZ_HAS_TERMINATOR)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTerminator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindService)
 
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
@@ -110,16 +114,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateP
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(FinalizationWitnessService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
 
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
+NS_DEFINE_NAMED_CID(NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID);
 #if defined(MOZ_HAS_TERMINATOR)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_TERMINATOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
 NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID);
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID);
 #endif
@@ -144,16 +149,17 @@ NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERN
 NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
 
 static const Module::CIDEntry kToolkitCIDs[] = {
   { &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
 #if defined(MOZ_HAS_TERMINATOR)
   { &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
 #endif
+  { &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID, false, nullptr, nsPerformanceStatsServiceConstructor },
   { &kNS_USERINFO_CID, false, nullptr, nsUserInfoConstructor },
   { &kNS_ALERTSSERVICE_CID, false, nullptr, nsAlertsServiceConstructor },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { &kNS_PARENTALCONTROLSSERVICE_CID, false, nullptr, nsParentalControlsServiceConstructor },
 #endif
   { &kNS_DOWNLOADMANAGER_CID, false, nullptr, nsDownloadManagerConstructor },
   { &kNS_DOWNLOADPLATFORM_CID, false, nullptr, DownloadPlatformConstructor },
   { &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor },
@@ -177,16 +183,17 @@ static const Module::CIDEntry kToolkitCI
   { nullptr }
 };
 
 static const Module::ContractIDEntry kToolkitContracts[] = {
   { NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
 #if defined(MOZ_HAS_TERMINATOR)
   { NS_TOOLKIT_TERMINATOR_CONTRACTID, &kNS_TOOLKIT_TERMINATOR_CID },
 #endif
+  { NS_TOOLKIT_PERFORMANCESTATSSERVICE_CONTRACTID, &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID },
   { NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID },
   { NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { NS_PARENTALCONTROLSSERVICE_CONTRACTID, &kNS_PARENTALCONTROLSSERVICE_CID },
 #endif
   { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID },
   { NS_DOWNLOADPLATFORM_CONTRACTID, &kNS_DOWNLOADPLATFORM_CID },
   { NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID },
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -5,18 +5,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # These component dirs are built for all apps (including suite)
 if CONFIG['MOZ_ENABLE_XREMOTE']:
     DIRS += ['remote']
 
 DIRS += [
     'aboutcache',
-    'aboutcompartments',
     'aboutmemory',
+    'aboutperformance',
     'addoncompat',
     'alerts',
     'apppicker',
     'asyncshutdown',
     'commandlines',
     'console',
     'contentprefs',
     'cookie',
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4816,16 +4816,32 @@
     "description": "Security UI Telemetry"
   },
   "JS_TELEMETRY_ADDON_EXCEPTIONS" : {
     "expires_in_version" : "never",
     "kind": "count",
     "keyed" : true,
     "description" : "Exceptions thrown by add-ons"
   },
+  "MISBEHAVING_ADDONS_CPOW_TIME_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "low": 1,
+    "high": 10000,
+    "n_buckets": 20,
+    "keyed": true,
+    "description": "Time spent by an add-on performing blocking cross-process communications (ms, keyed by add-on ID, updated every 15s by default)"
+  },
+  "MISBEHAVING_ADDONS_JANK_LEVEL": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 10,
+    "keyed": true,
+    "description": "Longest blocking operation performed by the add-on (log2(duration in ms), keyed by add-on, updated every 15s by default)"
+  },
   "SEARCH_COUNTS": {
     "expires_in_version": "never",
     "kind": "count",
     "keyed": true,
     "description": "Record the search counts for search engines"
   },
   "SEARCH_DEFAULT_ENGINE": {
     "expires_in_version": "never",
--- a/toolkit/modules/AddonWatcher.jsm
+++ b/toolkit/modules/AddonWatcher.jsm
@@ -8,76 +8,187 @@
 this.EXPORTED_SYMBOLS = ["AddonWatcher"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
                                   "resource://gre/modules/Preferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+                                  "resource://gre/modules/devtools/Console.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats",
+                                  "resource://gre/modules/PerformanceStats.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
+                                  "@mozilla.org/base/telemetry;1",
+                                  Ci.nsITelemetry);
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
 
 let AddonWatcher = {
-  _lastAddonTime: {},
+  _previousPerformanceIndicators: {},
   _timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
   _callback: null,
-  _interval: 1500,
+  /**
+   * The interval at which we poll the available performance information
+   * to find out about possibly slow add-ons, in milliseconds.
+   */
+  _interval: 15000,
   _ignoreList: null,
+  /**
+   * Initialize and launch the AddonWatcher.
+   *
+   * @param {function} callback A callback, called whenever we determine
+   * that an add-on is causing performance issues. It takes as argument
+   *  {string} addonId The identifier of the add-on known to cause issues.
+   *  {string} reason The reason for which the add-on has been flagged,
+   *     as one of "totalCPOWTime" (the add-on has caused blocking process
+   *     communications, which freeze the UX)
+   *     Use preference "browser.addon-watch.limits.totalCPOWTime" to control
+   *     the maximal amount of CPOW time per watch interval.
+   *
+   *     or "longestDuration" (the add-on has caused user-visible missed frames).
+   *     Use preference "browser.addon-watch.limits.longestDuration" to control
+   *     the longest uninterrupted execution of code of an add-on during a watch
+   *     interval.
+   */
   init: function(callback) {
     if (!callback) {
       return;
     }
 
     if (this._callback) {
+      // Already initialized
       return;
     }
 
     this._interval = Preferences.get("browser.addon-watch.interval", 15000);
     if (this._interval == -1) {
+      // Deactivated by preferences
       return;
     }
 
     this._callback = callback;
     try {
       this._ignoreList = new Set(JSON.parse(Preferences.get("browser.addon-watch.ignore", null)));
     } catch (ex) {
       // probably some malformed JSON, ignore and carry on
       this._ignoreList = new Set();
     }
-    this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
+
+    // Start monitoring
+    this.paused = false;
+
+    Services.obs.addObserver(() => {
+      this.uninit();
+    }, "profile-before-change", false);
   },
   uninit: function() {
-    if (this._timer) {
+    this.paused = true;
+    this._callback = null;
+  },
+
+  /**
+   * Interrupt temporarily add-on watching.
+   */
+  set paused(isPaused) {
+    if (!this._callback || this._interval == -1) {
+      return;
+    }
+    if (isPaused) {
       this._timer.cancel();
-      this._timer = null;
+    } else {
+      PerformanceStats.init();
+      this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
     }
+    this._isPaused = isPaused;
   },
+  get paused() {
+    return this._isPaused;
+  },
+  _isPaused: true,
+
+  /**
+   * Check the performance of add-ons during the latest slice of time.
+   *
+   * We consider that an add-on is causing slowdown if it has executed
+   * without interruption for at least 64ms (4 frames) at least once
+   * during the latest slice, or if it has used any CPOW during the latest
+   * slice.
+   */
   _checkAddons: function() {
-    let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
-      .getService(Ci.nsICompartmentInfo);
-    let compartments = compartmentInfo.getCompartments();
-    let count = compartments.length;
-    let addons = {};
-    for (let i = 0; i < count; i++) {
-      let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
-      if (compartment.addonId) {
-        if (addons[compartment.addonId]) {
-          addons[compartment.addonId] += compartment.time;
-        } else {
-          addons[compartment.addonId] = compartment.time;
+    try {
+      let snapshot = PerformanceStats.getSnapshot();
+
+      let limits = {
+        // By default, warn if we have a total time of 1s of CPOW per 15 seconds
+        totalCPOWTime: Math.round(Preferences.get("browser.addon-watch.limits.totalCPOWTime", 1000) * this._interval / 15000),
+        // By default, warn if we have skipped 4 consecutive frames
+        // at least once during the latest slice.
+        longestDuration: Math.round(Math.log2(Preferences.get("browser.addon-watch.limits.longestDuration", 7))),
+      };
+
+      for (let item of snapshot.componentsData) {
+        let addonId = item.addonId;
+        if (!item.isSystem || !addonId) {
+          // We are only interested in add-ons.
+          continue;
+        }
+        if (this._ignoreList.has(addonId)) {
+          // This add-on has been explicitly put in the ignore list
+          // by the user. Don't waste time with it.
+          continue;
+        }
+        let previous = this._previousPerformanceIndicators[addonId];
+        this._previousPerformanceIndicators[addonId] = item;
+
+        if (!previous) {
+          // This is the first time we see the addon, so we are probably
+          // executed right during/after startup. Performance is always
+          // weird during startup, with the JIT warming up, competition
+          // in disk access, etc. so we do not take this as a reason to
+          // display the slow addon warning.
+          continue;
+        }
+
+        // Report misbehaviors to Telemetry
+
+        let diff = item.substract(previous);
+        if (diff.longestDuration > 5) {
+          Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_JANK_LEVEL").
+            add(addonId, diff.longestDuration);
+        }
+        if (diff.totalCPOWTime > 0) {
+          Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS").
+            add(addonId, diff.totalCPOWTime);
+        }
+
+        // Report mibehaviors to the user.
+        let reason = null;
+
+        for (let k of ["longestDuration", "totalCPOWTime"]) {
+          if (limits[k] > 0 && diff[k] > limits[k]) {
+            reason = k;
+          }
+        }
+
+        if (!reason) {
+          continue;
+        }
+
+        try {
+          this._callback(addonId, reason);
+        } catch (ex) {
+          Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex);
+          Cu.reportError(ex.stack);
         }
       }
-    }
-    let limit = this._interval * Preferences.get("browser.addon-watch.percentage-limit", 75) * 10;
-    for (let addonId in addons) {
-      if (!this._ignoreList.has(addonId)) {
-        if (this._lastAddonTime[addonId] && ((addons[addonId] - this._lastAddonTime[addonId]) > limit)) {
-          this._callback(addonId);
-        }
-        this._lastAddonTime[addonId] = addons[addonId];
-      }
+    } catch (ex) {
+      Cu.reportError("Error in AddonWatcher._checkAddons " + ex);
+      Cu.reportError(ex.stack);
     }
   },
   ignoreAddonForSession: function(addonid) {
     this._ignoreList.add(addonid);
   },
   ignoreAddonPermanently: function(addonid) {
     this._ignoreList.add(addonid);
     try {
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/PerformanceStats.jsm
@@ -0,0 +1,205 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["PerformanceStats"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+/**
+ * API for querying and examining performance data.
+ *
+ * The data exposed by this API is computed internally by the JavaScript VM.
+ * See `PerformanceData` for the detail of the information provided by this
+ * API.
+ */
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+
+let performanceStatsService =
+  Cc["@mozilla.org/toolkit/performance-stats-service;1"].
+  getService(Ci.nsIPerformanceStatsService);
+
+
+const PROPERTIES_NUMBERED = ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"];
+const PROPERTIES_META = ["name", "addonId", "isSystem"];
+const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
+
+/**
+ * Information on a single component.
+ *
+ * This offers the following fields:
+ *
+ * @field {string} name The name of the component:
+ * - for the process itself, "<process>";
+ * - for platform code, "<platform>";
+ * - for an add-on, the identifier of the addon (e.g. "myaddon@foo.bar");
+ * - for a webpage, the url of the page.
+ *
+ * @field {string} addonId The identifier of the addon (e.g. "myaddon@foo.bar").
+ *
+ * @field {boolean} isSystem `true` if the component is a system component (i.e.
+ *  an add-on or platform-code), `false` otherwise (i.e. a webpage).
+ *
+ * @field {number} totalUserTime The total amount of time spent executing code.
+ *
+ * @field {number} totalSystemTime The total amount of time spent executing
+ *  system calls.
+ *
+ * @field {number} totalCPOWTime The total amount of time spent waiting for
+ *  blocking cross-process communications
+ *
+ * @field {number} ticks The number of times the JavaScript VM entered the code
+ *  of this component to execute it.
+ *
+ * @field {Array<number>} durations An array containing at each position `i`
+ * the number of times execution of this component has lasted at least `2^i`
+ * milliseconds.
+ *
+ * All numeric values are non-negative and can only increase.
+ */
+function PerformanceData(xpcom) {
+  for (let k of PROPERTIES_FLAT) {
+    this[k] = xpcom[k];
+  }
+  this.durations = xpcom.getDurations();
+}
+PerformanceData.prototype = {
+  /**
+   * Compare two instances of `PerformanceData`
+   *
+   * @return `true` if `this` and `to` have equal values in all fields.
+   */
+  equals: function(to) {
+    if (!(to instanceof PerformanceData)) {
+      throw new TypeError();
+    }
+    for (let k of PROPERTIES_FLAT) {
+      if (this[k] != to[k]) {
+        return false;
+      }
+    }
+    for (let i = 0; i < this.durations.length; ++i) {
+      if (to.durations[i] != this.durations[i]) {
+        return false;
+      }
+    }
+    return true;
+  },
+
+  /**
+   * Compute the delta between two instances of `PerformanceData`.
+   *
+   * @param {PerformanceData|null} to. If `null`, assumed an instance of
+   * `PerformanceData` in which all numeric values are 0.
+   *
+   * @return {PerformanceDiff} The performance usage between `to` and `this`.
+   */
+  substract: function(to = null) {
+    return new PerformanceDiff(this, to);
+  }
+};
+
+/**
+ * The delta between two instances of `PerformanceData`.
+ *
+ * Used to monitor resource usage between two timestamps.
+ *
+ * @field {number} longestDuration An indication of the longest
+ * execution duration between two timestamps:
+ * - -1 == less than 1ms
+ * - 0 == [1, 2[ ms
+ * - 1 == [2, 4[ ms
+ * - 3 == [4, 8[ ms
+ * - 4 == [8, 16[ ms
+ * - ...
+ * - 7 == [128, ...] ms
+ */
+function PerformanceDiff(current, old = null) {
+  for (let k of PROPERTIES_META) {
+    this[k] = current[k];
+  }
+
+  if (old) {
+    if (!(old instanceof PerformanceData)) {
+      throw new TypeError();
+    }
+    if (current.durations.length != old.durations.length) {
+      throw new TypeError("Internal error: mismatched length for `durations`.");
+    }
+
+    this.durations = [];
+
+    this.longestDuration = -1;
+
+    for (let i = 0; i < current.durations.length; ++i) {
+      let delta = current.durations[i] - old.durations[i];
+      this.durations[i] = delta;
+      if (delta > 0) {
+        this.longestDuration = i;
+      }
+    }
+    for (let k of PROPERTIES_NUMBERED) {
+      this[k] = current[k] - old[k];
+    }
+  } else {
+    this.durations = current.durations.slice(0);
+
+    for (let k of PROPERTIES_NUMBERED) {
+      this[k] = current[k];
+    }
+
+    this.longestDuration = -1;
+    for (let i = this.durations.length - 1; i >= 0; --i) {
+      if (this.durations[i] > 0) {
+        this.longestDuration = i;
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * A snapshot of the performance usage of the process.
+ */
+function Snapshot(xpcom) {
+  this.componentsData = [];
+  let enumeration = xpcom.getComponentsData().enumerate();
+  while (enumeration.hasMoreElements()) {
+    let stat = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
+    this.componentsData.push(new PerformanceData(stat));
+  }
+  this.processData = new PerformanceData(xpcom.getProcessData());
+}
+
+
+this.PerformanceStats = {
+  /**
+   * Activate monitoring.
+   */
+  init() {
+    //
+    // The implementation actually does nothing, as monitoring is
+    // initiated when loading the module.
+    //
+    // This function is actually provided as a gentle way to ensure
+    // that client code that imports `PerformanceStats` lazily
+    // does not forget to force the import, hence triggering
+    // actual load of the module.
+    //
+  },
+
+  /**
+   * Get a snapshot of the performance usage of the current process.
+   *
+   * @type {Snapshot}
+   */
+  getSnapshot() {
+    return new Snapshot(performanceStatsService.getSnapshot());
+  },
+};
+
+performanceStatsService.isStopwatchActive = true;
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -27,16 +27,17 @@ EXTRA_JS_MODULES += [
     'Http.jsm',
     'InlineSpellChecker.jsm',
     'InlineSpellCheckerContent.jsm',
     'LoadContextInfo.jsm',
     'Log.jsm',
     'NewTabUtils.jsm',
     'PageMenu.jsm',
     'PageMetadata.jsm',
+    'PerformanceStats.jsm',
     'PermissionsUtils.jsm',
     'PopupNotifications.jsm',
     'Preferences.jsm',
     'PrivateBrowsingUtils.jsm',
     'ProfileAge.jsm',
     'Promise-backend.js',
     'Promise.jsm',
     'PromiseUtils.jsm',
--- a/toolkit/modules/tests/browser/browser.ini
+++ b/toolkit/modules/tests/browser/browser.ini
@@ -1,14 +1,16 @@
 [DEFAULT]
 support-files =
   dummy_page.html
   metadata_*.html
   testremotepagemanager.html
+  browser_Addons_sample.xpi
 
+[browser_AddonWatcher.js]
 [browser_Battery.js]
 [browser_Deprecated.js]
 [browser_Finder.js]
 skip-if = e10s # Bug ?????? - test already uses content scripts, but still fails only under e10s.
 [browser_Geometry.js]
 [browser_InlineSpellChecker.js]
 [browser_PageMetadata.js]
 [browser_RemotePageManager.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/tests/browser/browser_AddonWatcher.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests for AddonWatcher.jsm
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Promise.jsm", this);
+Cu.import("resource://gre/modules/AddonManager.jsm", this);
+Cu.import("resource://gre/modules/AddonWatcher.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+const ADDON_URL = "http://example.com/browser/toolkit/modules/tests/browser/browser_Addons_sample.xpi";
+const ADDON_ID = "addonwatcher-test@mozilla.com";
+
+add_task(function* init() {
+  AddonWatcher.uninit();
+
+  info("Installing test add-on");
+  let installer = yield new Promise(resolve => AddonManager.getInstallForURL(ADDON_URL, resolve, "application/x-xpinstall"));
+  if (installer.error) {
+    throw installer.error;
+  }
+  let ready = new Promise((resolve, reject) => installer.addListener({
+    onInstallEnded: (_, addon) => resolve(addon),
+    onInstallFailed: reject,
+    onDownloadFailed: reject
+  }));
+  installer.install();
+
+  info("Waiting for installation to terminate");
+  let addon = yield ready;
+
+  registerCleanupFunction(() => {
+    info("Uninstalling test add-on");
+    addon.uninstall()
+  });
+
+  Services.prefs.setIntPref("browser.addon-watch.interval", 1000);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("browser.addon-watch.interval");
+  });
+
+  let oldCanRecord = Services.telemetry.canRecord;
+  Services.telemetry.canRecordExtended = true;
+  registerCleanupFunction(function () {
+    Services.telemetry.canRecordExtended = oldCanRecord;
+  });
+});
+
+// Utility function to burn some resource, trigger a reaction of the add-on watcher
+// and check both its notification and telemetry.
+let burn_rubber = Task.async(function*({histogramName, topic, expectedReason, prefs, expectedMinSum}) {
+  try {
+    for  (let key of Object.keys(prefs)) {
+      Services.prefs.setIntPref(key, prefs[key]);
+    }
+    info("Preparing add-on watcher");
+    let wait = new Promise(resolve => AddonWatcher.init((id, reason) => {
+      Assert.equal(id, ADDON_ID, "The add-on watcher has detected the misbehaving addon");
+      resolve(reason);
+    }));
+    let done = false;
+    wait = wait.then(result => {
+      done = true;
+      return result;
+    });
+
+    let histogram = Services.telemetry.getKeyedHistogramById(histogramName);
+    histogram.clear();
+    let snap1 = histogram.snapshot(ADDON_ID);
+    Assert.equal(snap1.sum, 0, `Histogram ${histogramName} is initially empty for the add-on`);
+    while (!done) {
+      yield new Promise(resolve => setTimeout(resolve, 100));
+      info("Burning some CPU. This should cause an add-on watcher notification");
+      Services.obs.notifyObservers(null, topic, "");
+    }
+    let reason = yield wait;
+
+    Assert.equal(reason, expectedReason, "Reason is valid");
+    let snap2 = histogram.snapshot(ADDON_ID);
+
+    Assert.ok(snap2.sum > expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap2.sum}, expecting at least ${expectedMinSum}.`);
+  } finally {
+    AddonWatcher.uninit();
+    for  (let key of Object.keys(prefs)) {
+      Services.prefs.clearUserPref(key);
+    }
+  }
+});
+
+// Test that burning CPU will cause the add-on watcher to notice that
+// the add-on is misbehaving.
+add_task(function* test_burn_CPU() {
+  yield burn_rubber({
+    prefs: {
+      "browser.addon-watch.limits.longestDuration": 2,
+      "browser.addon-watch.limits.totalCPOWTime": -1,
+    },
+    histogramName: "MISBEHAVING_ADDONS_JANK_LEVEL",
+    topic: "test-addonwatcher-burn-some-cpu",
+    expectedReason: "longestDuration",
+    expectedMinSum: 7,
+  });
+});
+
+// Test that burning CPOW will cause the add-on watcher to notice that
+// the add-on is misbehaving.
+add_task(function* test_burn_CPOW() {
+  if (!gMultiProcessBrowser) {
+    info("This is a single-process Firefox, we can't test for CPOW");
+    return;
+  }
+  yield burn_rubber({
+    prefs: {
+      "browser.addon-watch.limits.longestDuration": -1,
+      "browser.addon-watch.limits.totalCPOWTime": 100,
+    },
+    histogramName: "MISBEHAVING_ADDONS_CPOW_TIME_MS",
+    topic: "test-addonwatcher-burn-some-cpow",
+    expectedReason: "totalCPOWTime",
+    expectedMinSum: 1000,
+  });
+});
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..92e0019d6c7e9ce8cb479628fd1332ddd9d8406f
GIT binary patch
literal 2133
zc$^FHW@Zs#U}E54INy=$rZnI3@+u|<hNr9y3_J`n3`zO<CB-F0i3NID#i1db49slY
z%%MC$Tw1}+z{v80k%0lIn}MNsN`C$=1A$}rp9jyGpte*bY1j1Bxo(%sR+(@$h3#3f
z>Xe1rj^Z~b>K$^AmfyEziOMklrZ(yA@1Jt^<>HrG=2fvRJDsllX(w}{;^UbKS3dI0
zVKaYj@FiGNBIZRC*Hjk<gYtDYsx6X!b0Z3tbvtR#&X_!T6W6&_5uXnAs;PUnJ4|*F
zR5V!Ts%d#<OXJFm-lxwd>!02-E8zIEJ1YKWT~haoHk{*lCUsXMsbX_;`1_VE%P0I2
zGw@vdH1S}R!j#8pZjoEXv@M!L#FvJ}Sngh$&wlvzmydt)N+$EYH(2s;M`^B<k^c3W
zw}bOTZog{U9{JMny?cRyrO&IUVsGR7j_;j6o&7<>94^&6({@Y$t=c2?Rlwi<`r+FR
z8+RUjeer{=NsQZs!XF_<##<gtlMJ*nG5j&@m&*ddV@G)pul?56DqVEgbD^>RPSxZ*
zF{WPU1sPj5Tsb6H`oDSi(r@4OggxIWOt11v-7oKT;GMKR*I%V2OG3lfoDW#I)KY)N
zq4h84naFLJIcd)BD9<@vE;nEQvwAaq!w-|ZU#~w*Xfv5DXp$IHG}Uo~%$x)L+nt^^
zEIs<{h}-PSqjN5BIi(hu+ckyzh?sQ6WH!&OW&6&5x9YrAmdv}Dg!AfN`i~c%?L2VT
zFGA-z({8i6?uN47+UCiA%QyQm%zpj;=tskv#`XLsx(}>hbT$4cH|s)MyQy|FuH5?k
z`R%Um^3byUZ3dm6f)?3E@4EEtwA8(8k^Z?&x+XIo-O=6AnOz^X`}4&n$*I%A^<*5_
zCtthQvFD&;>0QCbJK9-)&PQfACKg-#j=TNq-^z-Hq<I#_W;Z)l=&bvaXYA#>d9{J?
z<1DpDdskLX-dUA@`_673{j2HwB;#JN+9%v~KW}%`?$$PSmk0d~UXia4{D0Pa%=56^
zar>fW<xYF8B>u1mz|(tjZ?>C<n!_$zU?R=|rg?5)de6)&E=kPE(JM+x11I;H1q`89
zJDAXud+DUpeuoVNT)+S1I(=7B)sLB5N>y*FmR-Yhp`D$#rT%oh(bTN5?faa;tz><E
z&f}fQOC#i_y3D_0l~~gG+_OvY`qY)J_on{%`O<FRQEw^NqlbUVRfg;qde1m(&V+5b
zlfPLRmbpLcKH9WI^5mu)*KXga%}h8Rv0Jh);5)0~^H+yLpGA9|Jj5C~>z=?mtLnCu
z)4NQ|r;76ai0H^Y!{}AB!AgEfOa{BpsR%>eZFd|e>^T;ymv!pZtrycAw>~#xyT4Dm
zJ=9HfNxNF&jd{PGI_KN?GZ^fCydg_UQ;RM5wbGLSDd7ggPtJ3m{ZiF`_HBXCuT&Yi
zTj{OEJ+jANh4#<oau)ie?#1xm^Iq&OLpGU+YXKYB<z579&N4VFvb^8NbkgFr8#9u9
zU+<`mF7^L&%f9f_yC(S?et$)mEBt%E@FG7V27E(u-9qH%>`(*-sWK4r17jdLqbNT&
zRWCO&FEcH*xC9&vO6!@h#e(;_^V*(gwF5nTJWrn2_Sf+|=b?K^*Hc$7(4+0di(*Cw
zkWE|+An;^zjvE6K-~ic_pI4HaSE3KLBKj;NwsH;O)+4~uv1<O@Jbj?laUd2$G9j%f
zF*mh1xhS)s1XfOd;9?H_&xK)L=mf{?Lk<G1?|+K+J1BOkDScV=H9)7eDQID9(2>Jw
z#-7`b$ljc5a^q3``&Cyj9X2>Kd**w?$zMN4N%xEA%~d<IQ^97_ZKaHTXYD<>TNF(l
zPvqL}uo3nCXXLwhL)PbGFF0y-demNLHk=oETr%a+%9f^QcmM6$9JL@=ZW?Rr$45T*
zxI|y{TuD6eLwT8rZ^E8p?zK7=;;BxXSX5WImQB{VJ#TMtS*X~I$b!>BUp8xWMf+Pc
zZ9Q^X<5Nm^_5STup7&OFO^P^iIzW{5wqNJEle}O1c6V-cd&IbpUr|XRf9~A%^X{u@
zM6W)&!MAET+l2`%%uiX2Z8R2MDVqLe+4XrVb#@nA=6QWK^6jev!AbiQdFnaKcgXhY
zCB%eX-*1_FBJ81|xkNtGyZu#e@{5vXn*IWl3L}#oGp_1M0vwgVpk-Ln2x6gDR;-Z9
z3L`=hCSn!<$R>UOil7t+tdQaWkAax!8QH)h76#1J%?e4~SPjLMbS0oH0fx7ZcR+^1
p6EZ6#A>(l(W->;0q8uv&&P2@$Nz@o-v$BEovjX8Fpx|6E4**%KHoyP?
--- a/xpcom/build/XPCOMInit.cpp
+++ b/xpcom/build/XPCOMInit.cpp
@@ -45,18 +45,16 @@
 
 #include "nsComponentManager.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsIServiceManager.h"
 
 #include "nsThreadManager.h"
 #include "nsThreadPool.h"
 
-#include "nsCompartmentInfo.h"
-
 #include "xptinfo.h"
 #include "nsIInterfaceInfoManager.h"
 #include "xptiprivate.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 
 #include "nsTimerImpl.h"
 #include "TimerThread.h"
 
@@ -237,18 +235,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacUtil
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemInfo, Init)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemoryReporterManager, Init)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMemoryInfoDumper)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStatusReporterManager, Init)
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsCompartmentInfo)
-
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIOUtil)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityConsoleMessage)
 
 static nsresult
 nsThreadManagerGetSingleton(nsISupports* aOuter,
                             const nsIID& aIID,
                             void** aInstancePtr)
--- a/xpcom/build/XPCOMModule.inc
+++ b/xpcom/build/XPCOMModule.inc
@@ -77,9 +77,8 @@
 
     COMPONENT(SYSTEMINFO, nsSystemInfoConstructor)
     COMPONENT(MEMORY_REPORTER_MANAGER, nsMemoryReporterManagerConstructor)
     COMPONENT(MEMORY_INFO_DUMPER, nsMemoryInfoDumperConstructor)
     COMPONENT(IOUTIL, nsIOUtilConstructor)
     COMPONENT(CYCLE_COLLECTOR_LOGGER, nsCycleCollectorLoggerConstructor)
     COMPONENT(MESSAGE_LOOP, nsMessageLoopConstructor)
     COMPONENT(STATUS_REPORTER_MANAGER, nsStatusReporterManagerConstructor)
-    COMPONENT(COMPARTMENT_INFO, nsCompartmentInfoConstructor)
\ No newline at end of file