--- 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