forked from mirrors/gecko-dev
		
	 5dec0e0beb
			
		
	
	
		5dec0e0beb
		
	
	
	
	
		
			
			This patch was autogenerated by my decomponents.py
It covers almost every file with the extension js, jsm, html, py,
xhtml, or xul.
It removes blank lines after removed lines, when the removed lines are
preceded by either blank lines or the start of a new block. The "start
of a new block" is defined fairly hackily: either the line starts with
//, ends with */, ends with {, <![CDATA[, """ or '''. The first two
cover comments, the third one covers JS, the fourth covers JS embedded
in XUL, and the final two cover JS embedded in Python. This also
applies if the removed line was the first line of the file.
It covers the pattern matching cases like "var {classes: Cc,
interfaces: Ci, utils: Cu, results: Cr} = Components;". It'll remove
the entire thing if they are all either Ci, Cr, Cc or Cu, or it will
remove the appropriate ones and leave the residue behind. If there's
only one behind, then it will turn it into a normal, non-pattern
matching variable definition. (For instance, "const { classes: Cc,
Constructor: CC, interfaces: Ci, utils: Cu } = Components" becomes
"const CC = Components.Constructor".)
MozReview-Commit-ID: DeSHcClQ7cG
--HG--
extra : rebase_source : d9c41878036c1ef7766ef5e91a7005025bc1d72b
		
	
			
		
			
				
	
	
		
			303 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* Any copyright is dedicated to the Public Domain.
 | |
|  * http://creativecommons.org/publicdomain/zero/1.0/ */
 | |
| 
 | |
| /* eslint-env mozilla/frame-script */
 | |
| /* eslint-disable mozilla/no-arbitrary-setTimeout */
 | |
| 
 | |
| "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.
 | |
|  */
 | |
| ChromeUtils.import("resource://gre/modules/PerformanceStats.jsm", this);
 | |
| ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 | |
| ChromeUtils.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()}`;
 | |
| 
 | |
| const PARENT_PID = Services.appinfo.processID;
 | |
| 
 | |
| // This function is injected as source as a frameScript
 | |
| function frameScript() {
 | |
|   try {
 | |
|     "use strict";
 | |
| 
 | |
|     ChromeUtils.import("resource://gre/modules/PerformanceStats.jsm");
 | |
|     ChromeUtils.import("resource://gre/modules/Services.jsm");
 | |
| 
 | |
|     // Make sure that the stopwatch is now active.
 | |
|     let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]);
 | |
| 
 | |
|     addMessageListener("compartments-test:getStatistics", () => {
 | |
|       try {
 | |
|         monitor.promiseSnapshot().then(snapshot => {
 | |
|           sendAsyncMessage("compartments-test:getStatistics", {snapshot, pid: Services.appinfo.processID});
 | |
|         });
 | |
|       } catch (ex) {
 | |
|         Cu.reportError("Error in content (getStatistics): " + ex);
 | |
|         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 (setTitles): " + ex);
 | |
|         Cu.reportError(ex.stack);
 | |
|       }
 | |
|     });
 | |
|   } catch (ex) {
 | |
|     Cu.reportError("Error in content (setup): " + ex);
 | |
|     Cu.reportError(ex.stack);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // A variant of `Assert` that doesn't spam the logs
 | |
| // in case of success.
 | |
| var SilentAssert = {
 | |
|   equal(a, b, msg) {
 | |
|     if (a == b) {
 | |
|       return;
 | |
|     }
 | |
|     Assert.equal(a, b, msg);
 | |
|   },
 | |
|   notEqual(a, b, msg) {
 | |
|     if (a != b) {
 | |
|       return;
 | |
|     }
 | |
|     Assert.notEqual(a, b, msg);
 | |
|   },
 | |
|   ok(a, msg) {
 | |
|     if (a) {
 | |
|       return;
 | |
|     }
 | |
|     Assert.ok(a, msg);
 | |
|   },
 | |
|   leq(a, b, msg) {
 | |
|     this.ok(a <= b, `${msg}: ${a} <= ${b}`);
 | |
|   }
 | |
| };
 | |
| 
 | |
| var isShuttingDown = false;
 | |
| function monotinicity_tester(source, testName) {
 | |
|   // In the background, check invariants:
 | |
|   // - numeric data can only ever increase;
 | |
|   // - the name, isSystem of a component never changes;
 | |
|   // - the name, isSystem of the process data;
 | |
|   // - there is at most one component with `name`;
 | |
|   // - types, etc.
 | |
|   let previous = {
 | |
|     processData: null,
 | |
|     componentsMap: new Map(),
 | |
|   };
 | |
| 
 | |
|   let sanityCheck = function(prev, next) {
 | |
|     if (prev == null) {
 | |
|       return;
 | |
|     }
 | |
|     for (let k of ["groupId", "isSystem"]) {
 | |
|       SilentAssert.equal(prev[k], next[k], `Sanity check (${testName}): ${k} hasn't changed (${prev.name}).`);
 | |
|     }
 | |
|     for (let [probe, k] of [
 | |
|       ["jank", "totalUserTime"],
 | |
|       ["jank", "totalSystemTime"],
 | |
|       ["cpow", "totalCPOWTime"],
 | |
|       ["ticks", "ticks"]
 | |
|     ]) {
 | |
|       SilentAssert.equal(typeof next[probe][k], "number", `Sanity check (${testName}): ${k} is a number.`);
 | |
|       SilentAssert.leq(prev[probe][k], next[probe][k], `Sanity check (${testName}): ${k} is monotonic.`);
 | |
|       SilentAssert.leq(0, next[probe][k], `Sanity check (${testName}): ${k} is >= 0.`);
 | |
|     }
 | |
|     SilentAssert.equal(prev.jank.durations.length, next.jank.durations.length,
 | |
|                        `Sanity check (${testName}): Jank durations should be equal`);
 | |
|     for (let i = 0; i < next.jank.durations.length; ++i) {
 | |
|       SilentAssert.ok(typeof next.jank.durations[i] == "number" && next.jank.durations[i] >= 0,
 | |
|         `Sanity check (${testName}): durations[${i}] is a non-negative number.`);
 | |
|       SilentAssert.leq(prev.jank.durations[i], next.jank.durations[i],
 | |
|         `Sanity check (${testName}): durations[${i}] is monotonic.`);
 | |
|     }
 | |
|     for (let i = 0; i < next.jank.durations.length - 1; ++i) {
 | |
|       SilentAssert.leq(next.jank.durations[i + 1], next.jank.durations[i],
 | |
|         `Sanity check (${testName}): durations[${i}] >= durations[${i + 1}].`);
 | |
|     }
 | |
|   };
 | |
|   let iteration = 0;
 | |
|   let frameCheck = async function() {
 | |
|     if (isShuttingDown) {
 | |
|       window.clearInterval(interval);
 | |
|       return;
 | |
|     }
 | |
|     let name = `${testName}: ${iteration++}`;
 | |
|     let result = await source();
 | |
|     if (!result) {
 | |
|       // This can happen at the end of the test when we attempt
 | |
|       // to communicate too late with the content process.
 | |
|       window.clearInterval(interval);
 | |
|       return;
 | |
|     }
 | |
|     let {pid, snapshot} = result;
 | |
| 
 | |
|     // Sanity check on the process data.
 | |
|     sanityCheck(previous.processData, snapshot.processData);
 | |
|     SilentAssert.equal(snapshot.processData.isSystem, true, "Should be system");
 | |
|     SilentAssert.equal(snapshot.processData.name, "<process>", "Should have '<process>' name");
 | |
|     SilentAssert.equal(snapshot.processData.processId, pid, "Process id should match");
 | |
|     previous.procesData = snapshot.processData;
 | |
| 
 | |
|     // Sanity check on components data.
 | |
|     let map = new Map();
 | |
|     for (let item of snapshot.componentsData) {
 | |
|       let isCorrectPid = (item.processId == pid && !item.isChildProcess)
 | |
|         || (item.processId != pid && item.isChildProcess);
 | |
|       SilentAssert.ok(isCorrectPid, `Pid check (${name}): the item comes from the right process`);
 | |
| 
 | |
|       let key = item.groupId;
 | |
|       if (map.has(key)) {
 | |
|         let old = map.get(key);
 | |
|         Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.name}, previous: ${old.name}`);
 | |
|       }
 | |
|       map.set(key, item);
 | |
|     }
 | |
|     for (let item of snapshot.componentsData) {
 | |
|       if (!item.parentId) {
 | |
|         continue;
 | |
|       }
 | |
|       let parent = map.get(item.parentId);
 | |
|       SilentAssert.ok(parent, `The parent exists ${item.parentId}`);
 | |
| 
 | |
|       for (let [probe, k] of [
 | |
|         ["jank", "totalUserTime"],
 | |
|         ["jank", "totalSystemTime"],
 | |
|         ["cpow", "totalCPOWTime"]
 | |
|       ]) {
 | |
|         // Note that we cannot expect components data to be always smaller
 | |
|         // than parent data, as `getrusage` & co are not monotonic.
 | |
|         SilentAssert.leq(item[probe][k], 2 * parent[probe][k],
 | |
|           `Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`);
 | |
|       }
 | |
|     }
 | |
|     for (let [key, item] of map) {
 | |
|       sanityCheck(previous.componentsMap.get(key), item);
 | |
|       previous.componentsMap.set(key, item);
 | |
|     }
 | |
|   };
 | |
|   let interval = window.setInterval(frameCheck, 300);
 | |
|   registerCleanupFunction(() => {
 | |
|     window.clearInterval(interval);
 | |
|   });
 | |
| }
 | |
| 
 | |
| add_task(async function test() {
 | |
|   let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]);
 | |
| 
 | |
|   info("Extracting initial state");
 | |
|   let stats0 = await monitor.promiseSnapshot();
 | |
|   Assert.notEqual(stats0.componentsData.length, 0, "There is more than one component");
 | |
|   Assert.ok(!stats0.componentsData.find(stat => stat.name.includes(URL)),
 | |
|     "The url doesn't appear yet");
 | |
| 
 | |
|   let newTab = BrowserTestUtils.addTab(gBrowser);
 | |
|   let browser = newTab.linkedBrowser;
 | |
|   // Setup monitoring in the tab
 | |
|   info("Setting up monitoring in the tab");
 | |
|   await ContentTask.spawn(newTab.linkedBrowser, null, frameScript);
 | |
| 
 | |
|   info("Opening URL");
 | |
|   newTab.linkedBrowser.loadURI(URL);
 | |
| 
 | |
|   if (Services.sysinfo.getPropertyAsAString("name") == "Windows_NT") {
 | |
|     info("Deactivating sanity checks under Windows (bug 1151240)");
 | |
|   } else {
 | |
|     info("Setting up sanity checks");
 | |
|     monotinicity_tester(() => monitor.promiseSnapshot().then(snapshot => ({snapshot, pid: PARENT_PID})), "parent process");
 | |
|     monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
 | |
|   }
 | |
| 
 | |
|   let skipTotalUserTime = hasLowPrecision();
 | |
| 
 | |
| 
 | |
|   while (true) {
 | |
|     await 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");
 | |
|     await promiseContentResponse(browser, "compartments-test:setTitles", {
 | |
|       parent: PARENT_TITLE,
 | |
|       frames: FRAME_TITLE
 | |
|     });
 | |
|     info("Titles set");
 | |
| 
 | |
|     let {snapshot: stats} = (await promiseContentResponse(browser, "compartments-test:getStatistics", null));
 | |
| 
 | |
|     // Attach titles to components.
 | |
|     let titles = [];
 | |
|     let map = new Map();
 | |
|     let windows = Services.wm.getEnumerator("navigator:browser");
 | |
|     while (windows.hasMoreElements()) {
 | |
|       let window = windows.getNext();
 | |
|       let tabbrowser = window.gBrowser;
 | |
|       for (let browser of tabbrowser.browsers) {
 | |
|         let id = browser.outerWindowID; // May be `null` if the browser isn't loaded yet
 | |
|         if (id != null) {
 | |
|           map.set(id, browser);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     for (let stat of stats.componentsData) {
 | |
|       if (!stat.windowId) {
 | |
|         continue;
 | |
|       }
 | |
|       let browser = map.get(stat.windowId);
 | |
|       if (!browser) {
 | |
|         continue;
 | |
|       }
 | |
|       let title = browser.contentTitle;
 | |
|       if (title) {
 | |
|         stat.title = title;
 | |
|         titles.push(title);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // 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.
 | |
|     info(`Searching for frame title '${FRAME_TITLE}' in ${JSON.stringify(titles)} (I hope not to find it)`);
 | |
|     Assert.ok(!titles.includes(FRAME_TITLE), "Searching by title, the frames don't show up in the list of components");
 | |
| 
 | |
|     info(`Searching for window title '${PARENT_TITLE}' in ${JSON.stringify(titles)} (I hope to find it)`);
 | |
|     let parent = stats.componentsData.find(x => x.title == PARENT_TITLE);
 | |
|     if (!parent) {
 | |
|       info("Searching by title, we didn't find the main frame");
 | |
|       continue;
 | |
|     }
 | |
|     info("Found the main frame");
 | |
| 
 | |
|     if (skipTotalUserTime) {
 | |
|       info("Not looking for total user time on this platform, we're done");
 | |
|       break;
 | |
|     } else if (parent.jank.totalUserTime > 1000) {
 | |
|       info("Enough CPU time detected, we're done");
 | |
|       break;
 | |
|     } else {
 | |
|       info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`);
 | |
|     }
 | |
|   }
 | |
|   isShuttingDown = true;
 | |
| 
 | |
|   // Cleanup
 | |
|   gBrowser.removeTab(newTab, {skipPermitUnload: true});
 | |
| });
 |