diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index 4cb0a7c1feb9..b313dd209466 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -11,6 +11,9 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}); const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}); const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {}); +const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); + +const UPDATE_TOPIC = "about:performance-update-immediately"; /** * The various measures we display. @@ -277,7 +280,7 @@ function updateLiveData() { _el.textContent = a ? a.name : _item.name }); } else { - el.textContent = item.name; + el.textContent = item.title || item.name; } } } catch (ex) { @@ -294,5 +297,8 @@ function go() { document.getElementById("intervalDropdown").addEventListener("change", () => AutoUpdate.updateRefreshRate()); State.update(); - setTimeout(update, 1000); -} \ No newline at end of file + let observer = update; + + Services.obs.addObserver(update, UPDATE_TOPIC, false); + window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_TOPIC)); +} diff --git a/toolkit/components/aboutperformance/tests/browser/browser.ini b/toolkit/components/aboutperformance/tests/browser/browser.ini index 292d14623166..87eb74f607da 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser.ini +++ b/toolkit/components/aboutperformance/tests/browser/browser.ini @@ -1,11 +1,9 @@ -# 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_compartments_frame.html + browser_compartments_script.js [browser_aboutperformance.js] skip-if = e10s # Feature not implemented yet – bug 1140310 diff --git a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js index 79b994d6338b..1cf51927748e 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js +++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js @@ -11,9 +11,19 @@ const URL = "http://example.com/browser/toolkit/components/aboutperformance/test function frameScript() { "use strict"; - addMessageListener("aboutperformance-test:hasItems", ({data: url}) => { + addMessageListener("aboutperformance-test:done", () => { + content.postMessage("stop", "*"); + sendAsyncMessage("aboutperformance-test:done", null); + }); + addMessageListener("aboutperformance-test:setTitle", ({data: title}) => { + content.document.title = title; + sendAsyncMessage("aboutperformance-test:setTitle", null); + }); + + addMessageListener("aboutperformance-test:hasItems", ({data: title}) => { + Services.obs.notifyObservers(null, "about:performance-update-immediately", ""); let hasPlatform = false; - let hasURL = false; + let hasTitle = false; try { let eltData = content.document.getElementById("liveData"); @@ -24,42 +34,51 @@ function frameScript() { // 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; - } - } + // Find if we have a row for our content page + let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent]; + hasTitle = titles.includes(title); } catch (ex) { Cu.reportError("Error in content: " + ex); Cu.reportError(ex.stack); } finally { - sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasURL}); + sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle}); } }); } -add_task(function* test() { - let tabAboutPerformance = gBrowser.addTab("about:performance"); - let tabContent = gBrowser.addTab(URL); - +add_task(function* go() { + info("Setting up about:performance"); + let tabAboutPerformance = gBrowser.selectedTab = gBrowser.addTab("about:performance"); yield ContentTask.spawn(tabAboutPerformance.linkedBrowser, null, frameScript); + info(`Setting up ${URL}`); + let tabContent = gBrowser.addTab(URL); + yield ContentTask.spawn(tabContent.linkedBrowser, null, frameScript); + + let title = "Testing about:performance " + Math.random(); + info(`Setting up title ${title}`); while (true) { + yield promiseContentResponse(tabContent.linkedBrowser, "aboutperformance-test:setTitle", title); 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 and a row for our URL"); + let {hasPlatform, hasTitle} = (yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", title)); + info(`Platform: ${hasPlatform}, title: ${hasTitle}`); + if (hasPlatform && hasTitle) { + Assert.ok(true, "Found a row for and a row for our page"); break; } } // Cleanup - gBrowser.removeTab(tabContent); - gBrowser.removeTab(tabAboutPerformance); + info("Cleaning up"); + yield promiseContentResponse(tabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null); + + info("Closing tabs"); + for (let tab of gBrowser.tabs) { + yield BrowserTestUtils.removeTab(tab); + } + + info("Done"); + gBrowser.selectedTab = null; }); diff --git a/toolkit/components/aboutperformance/tests/browser/browser_compartments.html b/toolkit/components/aboutperformance/tests/browser/browser_compartments.html index 120aeff789ca..a74a5745af02 100644 --- a/toolkit/components/aboutperformance/tests/browser/browser_compartments.html +++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments.html @@ -2,24 +2,19 @@ - browser_compartments.html + Main frame for test browser_aboutperformance.js - -browser_compartments.html +Main frame. + + + + + diff --git a/toolkit/components/aboutperformance/tests/browser/browser_compartments_frame.html b/toolkit/components/aboutperformance/tests/browser/browser_compartments_frame.html new file mode 100644 index 000000000000..69edfe871bab --- /dev/null +++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments_frame.html @@ -0,0 +1,12 @@ + + + + + Subframe for test browser_compartments.html (do not change this title) + + + + +Subframe loaded. + + diff --git a/toolkit/components/aboutperformance/tests/browser/browser_compartments_script.js b/toolkit/components/aboutperformance/tests/browser/browser_compartments_script.js new file mode 100644 index 000000000000..3d5f7114f663 --- /dev/null +++ b/toolkit/components/aboutperformance/tests/browser/browser_compartments_script.js @@ -0,0 +1,29 @@ + +var carryOn = true; + +window.addEventListener("message", e => { + console.log("frame content", "message", e); + if ("title" in e.data) { + document.title = e.data.title; + } + if ("stop" in e.data) { + carryOn = false; + } +}); + +// Use some CPU. +var interval = window.setInterval(() => { + if (!carryOn) { + window.clearInterval(interval); + return; + } + + // 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++; + } +}, 300); diff --git a/toolkit/components/perfmonitoring/PerformanceStats.jsm b/toolkit/components/perfmonitoring/PerformanceStats.jsm index 47bbb4c0617f..bf3e30229d31 100644 --- a/toolkit/components/perfmonitoring/PerformanceStats.jsm +++ b/toolkit/components/perfmonitoring/PerformanceStats.jsm @@ -25,7 +25,8 @@ let performanceStatsService = const PROPERTIES_NUMBERED = ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"]; -const PROPERTIES_META = ["name", "addonId", "isSystem"]; +const PROPERTIES_META_IMMUTABLE = ["name", "addonId", "isSystem"]; +const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title"]; const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META]; /** @@ -41,6 +42,15 @@ const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META]; * * @field {string} addonId The identifier of the addon (e.g. "myaddon@foo.bar"). * + * @field {string|null} title The title of the webpage to which this code + * belongs. Note that this is the title of the entire webpage (i.e. the tab), + * even if the code is executed in an iframe. Also note that this title may + * change over time. + * + * @field {number} windowId The outer window ID of the top-level nsIDOMWindow + * to which this code belongs. May be 0 if the code doesn't belong to any + * nsIDOMWindow. + * * @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). * diff --git a/toolkit/components/perfmonitoring/moz.build b/toolkit/components/perfmonitoring/moz.build index 6d53445e3945..a649065295e5 100644 --- a/toolkit/components/perfmonitoring/moz.build +++ b/toolkit/components/perfmonitoring/moz.build @@ -28,4 +28,8 @@ EXPORTS += [ 'nsPerformanceStats.h' ] +LOCAL_INCLUDES += [ + '/dom/base', +] + FINAL_LIBRARY = 'xul' diff --git a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl index 72f105ea63f3..af8b18fc2f3e 100644 --- a/toolkit/components/perfmonitoring/nsIPerformanceStats.idl +++ b/toolkit/components/perfmonitoring/nsIPerformanceStats.idl @@ -6,6 +6,7 @@ #include "nsISupports.idl" #include "nsIArray.idl" +#include "nsIDOMWindow.idl" /** * Mechanisms for querying the current process about performance @@ -21,7 +22,7 @@ * All values are monotonic and are updated only when * `nsIPerformanceStatsService.isStopwatchActive` is `true`. */ -[scriptable, uuid(f015cbad-e16f-4982-a080-67e4e69a5b2e)] +[scriptable, uuid(7741ba2d-3171-42cd-9f36-e46a5ce6d5b1)] interface nsIPerformanceStats: nsISupports { /** * The name of the component: @@ -38,6 +39,18 @@ interface nsIPerformanceStats: nsISupports { */ readonly attribute AString addonId; + /** + * If the component is code executed in a window, the ID of the topmost + * outer window (i.e. the tab), otherwise 0. + */ + readonly attribute uint64_t windowId; + + /** + * If the component is code executed in a window, the title of the topmost + * window (i.e. the tab), otherwise an empty string. + */ + readonly attribute AString title; + /** * Total amount of time spent executing code in this group, in * microseconds. @@ -91,7 +104,7 @@ interface nsIPerformanceSnapshot: nsISupports { nsIPerformanceStats getProcessData(); }; -[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)] +[scriptable, builtinclass, uuid(c989c220-6581-4252-9aad-358fdec9ec8c)] interface nsIPerformanceStatsService : nsISupports { /** * `true` if we should monitor performance, `false` otherwise. @@ -101,7 +114,7 @@ interface nsIPerformanceStatsService : nsISupports { /** * Capture a snapshot of the performance data. */ - nsIPerformanceSnapshot getSnapshot(); + [implicit_jscontext] nsIPerformanceSnapshot getSnapshot(); }; %{C++ diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 544fe01210fe..f2fa34e1d060 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -3,24 +3,37 @@ * 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 "jsapi.h" #include "nsJSUtils.h" #include "xpcpublic.h" #include "jspubtd.h" +#include "nsIJSRuntimeService.h" + +#include "nsIDOMWindow.h" +#include "nsGlobalWindow.h" class nsPerformanceStats: public nsIPerformanceStats { public: - nsPerformanceStats(nsAString& aName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData& aPerformanceData) + nsPerformanceStats(const nsAString& aName, + const nsAString& aAddonId, + const nsAString& aTitle, + const uint64_t aWindowId, + const bool aIsSystem, + const js::PerformanceData& aPerformanceData) : mName(aName) , mAddonId(aAddonId) + , mTitle(aTitle) + , mWindowId(aWindowId) , mIsSystem(aIsSystem) , mPerformanceData(aPerformanceData) { @@ -41,6 +54,24 @@ public: return NS_OK; }; + /* readonly attribute uint64_t windowId; */ + NS_IMETHOD GetWindowId(uint64_t *aWindowId) override { + *aWindowId = mWindowId; + return NS_OK; + } + + /* readonly attribute AString title; */ + NS_IMETHOD GetTitle(nsAString & aTitle) override { + aTitle.Assign(mTitle); + return NS_OK; + } + + /* readonly attribute bool isSystem; */ + NS_IMETHOD GetIsSystem(bool *_retval) override { + *_retval = mIsSystem; + return NS_OK; + } + /* readonly attribute unsigned long long totalUserTime; */ NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override { *aTotalUserTime = mPerformanceData.totalUserTime; @@ -78,16 +109,13 @@ public: return NS_OK; }; - /* readonly attribute bool isSystem; */ - NS_IMETHOD GetIsSystem(bool *_retval) override { - *_retval = mIsSystem; - return NS_OK; - } - private: nsString mName; nsString mAddonId; + nsString mTitle; + uint64_t mWindowId; bool mIsSystem; + js::PerformanceData mPerformanceData; virtual ~nsPerformanceStats() {} @@ -103,15 +131,45 @@ public: NS_DECL_NSIPERFORMANCESNAPSHOT nsPerformanceSnapshot(); - nsresult Init(); + nsresult Init(JSContext*); private: virtual ~nsPerformanceSnapshot(); /** - * Import a `PerformanceStats` as a `nsIPerformanceStats`. + * Import a `js::PerformanceStats` as a `nsIPerformanceStats`. + * + * Precondition: this method assumes that we have entered the JSCompartment for which data `c` + * has been collected. + * + * `cx` may be `nullptr` if we are importing the statistics for the + * entire process, rather than the statistics for a specific set of + * compartments. */ - already_AddRefed ImportStats(js::PerformanceStats* c); + already_AddRefed ImportStats(JSContext* cx, const js::PerformanceData& data); + /** + * Callbacks for iterating through the `PerformanceStats` of a runtime. + */ + bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats); + static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self); + + // If the context represents a window, extract the title and window ID. + // Otherwise, extract "" and 0. + static void GetWindowData(JSContext*, + nsString& title, + uint64_t* windowId); + + // If the context presents an add-on, extract the addon ID. + // Otherwise, extract "". + static void GetAddonId(JSContext*, + JS::Handle global, + nsAString& addonId); + + // Determine whether a context is part of the system principals. + static bool GetIsSystem(JSContext*, + JS::Handle global); + +private: nsCOMArray mComponentsData; nsCOMPtr mProcessData; }; @@ -126,36 +184,119 @@ nsPerformanceSnapshot::~nsPerformanceSnapshot() { } -already_AddRefed -nsPerformanceSnapshot::ImportStats(js::PerformanceStats* c) { - nsString addonId; - if (c->addonId) { - AssignJSFlatString(addonId, (JSFlatString*)c->addonId); +/* static */ void +nsPerformanceSnapshot::GetWindowData(JSContext* cx, + nsString& title, + uint64_t* windowId) +{ + MOZ_ASSERT(windowId); + + title.SetIsVoid(true); + *windowId = 0; + + nsCOMPtr win = xpc::CurrentWindowOrNull(cx); + if (!win) { + return; } - nsCString cname(c->name); - NS_ConvertUTF8toUTF16 name(cname); - nsCOMPtr result = new nsPerformanceStats(name, addonId, c->isSystem, c->performance); + + nsCOMPtr top; + nsresult rv = win->GetTop(getter_AddRefs(top)); + if (!top || NS_FAILED(rv)) { + return; + } + + nsCOMPtr ptop = do_QueryInterface(top); + if (!ptop) { + return; + } + + nsCOMPtr doc = ptop->GetExtantDoc(); + if (!doc) { + return; + } + + doc->GetTitle(title); + *windowId = ptop->WindowID(); +} + +/* static */ void +nsPerformanceSnapshot::GetAddonId(JSContext*, + JS::Handle global, + nsAString& addonId) +{ + addonId.AssignLiteral(""); + + JSAddonId* jsid = AddonIdOfObject(global); + if (!jsid) { + return; + } + AssignJSFlatString(addonId, (JSFlatString*)jsid); +} + +/* static */ bool +nsPerformanceSnapshot::GetIsSystem(JSContext*, + JS::Handle global) +{ + return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global)); +} + +already_AddRefed +nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance) { + JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + + if (!global) { + // While it is possible for a compartment to have no global + // (e.g. atoms), this compartment is not very interesting for us. + return nullptr; + } + + nsString addonId; + GetAddonId(cx, global, addonId); + + nsString title; + uint64_t windowId; + GetWindowData(cx, title, &windowId); + + nsAutoString name; + nsAutoCString cname; + xpc::GetCurrentCompartmentName(cx, cname); + name.Assign(NS_ConvertUTF8toUTF16(cname)); + + bool isSystem = GetIsSystem(cx, global); + + nsCOMPtr result = + new nsPerformanceStats(name, addonId, title, windowId, isSystem, performance); return result.forget(); } -nsresult -nsPerformanceSnapshot::Init() { - JSRuntime* rt; - nsCOMPtr 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; +/*static*/ bool +nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self) { + return reinterpret_cast(self)->IterPerformanceStatsCallbackInternal(cx, stats); +} + +bool +nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats) { + nsCOMPtr result = ImportStats(cx, stats); + if (result) { + mComponentsData.AppendElement(result); } - size_t num = componentsStats.length(); - for (size_t pos = 0; pos < num; pos++) { - nsCOMPtr stats = ImportStats(&componentsStats[pos]); - mComponentsData.AppendObject(stats); + return true; +} + +nsresult +nsPerformanceSnapshot::Init(JSContext* cx) { + js::PerformanceData processStats; + if (!js::IterPerformanceStats(cx, nsPerformanceSnapshot::IterPerformanceStatsCallback, &processStats, this)) { + return NS_ERROR_UNEXPECTED; } - mProcessData = ImportStats(&processStats); + + mProcessData = new nsPerformanceStats(NS_LITERAL_STRING(""), // name + NS_LITERAL_STRING(""), // add-on id + NS_LITERAL_STRING(""), // title + 0, // window id + true, // isSystem + processStats); return NS_OK; } @@ -209,10 +350,10 @@ NS_IMETHODIMP nsPerformanceStatsService::SetIsStopwatchActive(JSContext* cx, boo } /* readonly attribute nsIPerformanceSnapshot snapshot; */ -NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(nsIPerformanceSnapshot * *aSnapshot) +NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot) { nsRefPtr snapshot = new nsPerformanceSnapshot(); - nsresult rv = snapshot->Init(); + nsresult rv = snapshot->Init(cx); if (NS_FAILED(rv)) { return rv; } diff --git a/toolkit/components/perfmonitoring/tests/browser/browser.ini b/toolkit/components/perfmonitoring/tests/browser/browser.ini index fcf0f90c0a4d..9c8a84e6dbb9 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser.ini +++ b/toolkit/components/perfmonitoring/tests/browser/browser.ini @@ -3,6 +3,7 @@ head = head.js support-files = browser_Addons_sample.xpi browser_compartments.html + browser_compartments_frame.html [browser_AddonWatcher.js] [browser_compartments.js] diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html index 120aeff789ca..d7ee6c418910 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html @@ -2,24 +2,19 @@ - browser_compartments.html + Main frame for test browser_compartments.js - -browser_compartments.html +Main frame. + + + + + diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js index dfb02d7e4de1..de3b9b3e7e02 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js @@ -3,10 +3,17 @@ "use strict"; +/** + * Test that we see jank that takes place in a webpage, + * and that jank from several iframes are actually charged + * to the top window. + */ Cu.import("resource://gre/modules/PerformanceStats.jsm", this); Cu.import("resource://testing-common/ContentTask.jsm", this); const URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random(); +const PARENT_TITLE = `Main frame for test browser_compartments.js ${Math.random()}`; +const FRAME_TITLE = `Subframe for test browser_compartments.js ${Math.random()}`; // This function is injected as source as a frameScript function frameScript() { @@ -30,6 +37,20 @@ function frameScript() { Cu.reportError(ex.stack); } }); + + addMessageListener("compartments-test:setTitles", titles => { + try { + content.document.title = titles.data.parent; + for (let i = 0; i < content.frames.length; ++i) { + content.frames[i].postMessage({title: titles.data.frames}, "*"); + } + console.log("content", "Done setting titles", content.document.title); + sendAsyncMessage("compartments-test:setTitles"); + } catch (ex) { + Cu.reportError("Error in content: " + ex); + Cu.reportError(ex.stack); + } + }); } // A variant of `Assert` that doesn't spam the logs @@ -72,7 +93,6 @@ function monotinicity_tester(source, testName) { }; let sanityCheck = function(prev, next) { - info(`Sanity check: ${JSON.stringify(next, null, "\t")}`); if (prev == null) { return; } @@ -116,18 +136,27 @@ function monotinicity_tester(source, testName) { // Sanity check on components data. let set = new Set(); - let keys = []; + let map = new Map(); for (let item of snapshot.componentsData) { - let key = `{name: ${item.name}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`; - keys.push(key); - set.add(key); - sanityCheck(previous.componentsMap.get(key), item); - previous.componentsMap.set(key, item); - for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime"]) { SilentAssert.leq(item[k], snapshot.processData[k], `Sanity check (${testName}): component has a lower ${k} than process`); } + + let key = `{name: ${item.name}, window: ${item.windowId}, addonId: ${item.addonId}, isSystem: ${item.isSystem}}`; + if (set.has(key)) { + // There are at least two components with the same name (e.g. about:blank). + // Don't perform sanity checks on that name until we know how to make + // the difference. + map.delete(key); + continue; + } + map.set(key, item); + set.add(key); + } + for (let [key, item] of map) { + sanityCheck(previous.componentsMap.get(key), item); + previous.componentsMap.set(key, item); } info(`Deactivating deduplication check (Bug 1150045)`); if (false) { @@ -147,7 +176,7 @@ add_task(function* test() { Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1), "The url doesn't appear yet"); - let newTab = gBrowser.addTab(); + let newTab = gBrowser.selectedTab = gBrowser.addTab(); let browser = newTab.linkedBrowser; // Setup monitoring in the tab info("Setting up monitoring in the tab"); @@ -166,17 +195,40 @@ add_task(function* test() { let skipTotalUserTime = hasLowPrecision(); + while (true) { - let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null)); - let found = stats.componentsData.find(stat => { - return (stat.name.indexOf(URL) != -1) - && (skipTotalUserTime || stat.totalUserTime > 1000) + yield new Promise(resolve => setTimeout(resolve, 100)); + + // We may have race conditions with DOM loading. + // Don't waste too much brainpower here, let's just ask + // repeatedly for the title to be changed, until this works. + info("Setting titles"); + yield promiseContentResponse(browser, "compartments-test:setTitles", { + parent: PARENT_TITLE, + frames: FRAME_TITLE }); - if (found) { - info(`Expected totalUserTime > 1000, got ${found.totalUserTime}`); + info("Titles set"); + + let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null)); + + // While the webpage consists in three compartments, we should see only + // one `PerformanceData` in `componentsData`. Its `name` is undefined + // (could be either the main frame or one of its subframes), but its + // `title` should be the title of the main frame. + let frame = stats.componentsData.find(stat => stat.title == FRAME_TITLE); + Assert.equal(frame, null, "Searching by title, the frames don't show up in the list of components"); + + let parent = stats.componentsData.find(stat => stat.title == PARENT_TITLE); + if (!parent) { + info("Searching by title, we didn't find the main frame"); + continue; + } + info("Searching by title, we found the main frame"); + + info(`Total user time: ${parent.totalUserTime}`); + if (skipTotalUserTime || parent.totalUserTime > 1000) { break; } - yield new Promise(resolve => setTimeout(resolve, 100)); } // Cleanup diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments_frame.html b/toolkit/components/perfmonitoring/tests/browser/browser_compartments_frame.html new file mode 100644 index 000000000000..69edfe871bab --- /dev/null +++ b/toolkit/components/perfmonitoring/tests/browser/browser_compartments_frame.html @@ -0,0 +1,12 @@ + + + + + Subframe for test browser_compartments.html (do not change this title) + + + + +Subframe loaded. + +