--- a/testing/talos/talos/tests/devtools/addon/content/damp.js
+++ b/testing/talos/talos/tests/devtools/addon/content/damp.js
@@ -1,40 +1,26 @@
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
-const gMgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
XPCOMUtils.defineLazyGetter(this, "require", function() {
let { require } =
ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
return require;
});
XPCOMUtils.defineLazyGetter(this, "gDevTools", function() {
let { gDevTools } = require("devtools/client/framework/devtools");
return gDevTools;
});
-XPCOMUtils.defineLazyGetter(this, "EVENTS", function() {
- let { EVENTS } = require("devtools/client/netmonitor/src/constants");
- return EVENTS;
-});
XPCOMUtils.defineLazyGetter(this, "TargetFactory", function() {
let { TargetFactory } = require("devtools/client/framework/target");
return TargetFactory;
});
-const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
-
-const PAGES_BASE_URL = webserver + "/tests/devtools/addon/content/pages/";
-const SIMPLE_URL = PAGES_BASE_URL + "simple.html";
-const COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
-const CUSTOM_URL = PAGES_BASE_URL + "custom/$TOOL/index.html";
-const PANELS_IN_BACKGROUND = PAGES_BASE_URL +
- "custom/panels-in-background/panels-in-background.html";
-
// Record allocation count in new subtests if DEBUG_DEVTOOLS_ALLOCATIONS is set to
// "normal". Print allocation sites to stdout if DEBUG_DEVTOOLS_ALLOCATIONS is set to
// "verbose".
const DEBUG_ALLOCATIONS = env.get("DEBUG_DEVTOOLS_ALLOCATIONS");
function getMostRecentBrowserWindow() {
return Services.wm.getMostRecentWindow("navigator:browser");
}
@@ -83,290 +69,44 @@ function awaitBrowserLoaded(browser, inc
isWanted(msg.data.url)) {
mm.removeMessageListener("browser-test-utils:loadEvent", onLoad);
resolve(msg.data.url);
}
});
});
}
-/* ************* Debugger Helper ***************/
-/*
- * These methods are used for working with debugger state changes in order
- * to make it easier to manipulate the ui and test different behavior. These
- * methods roughly reflect those found in debugger/new/test/mochi/head.js with
- * a few exceptions. The `dbg` object is not exactly the same, and the methods
- * have been simplified. We may want to consider unifying them in the future
- */
-
-const DEBUGGER_POLLING_INTERVAL = 50;
-
-const debuggerHelper = {
- waitForState(dbg, predicate, msg) {
- return new Promise(resolve => {
- if (msg) {
- dump(`Waiting for state change: ${msg}\n`);
- }
- if (predicate(dbg.store.getState())) {
- if (msg) {
- dump(`Finished waiting for state change: ${msg}\n`);
- }
- return resolve();
- }
-
- const unsubscribe = dbg.store.subscribe(() => {
- if (predicate(dbg.store.getState())) {
- if (msg) {
- dump(`Finished waiting for state change: ${msg}\n`);
- }
- unsubscribe();
- resolve();
- }
- });
- return false;
- });
- },
-
- waitForDispatch(dbg, type) {
- return new Promise(resolve => {
- dbg.store.dispatch({
- type: "@@service/waitUntil",
- predicate: action => {
- if (action.type === type) {
- return action.status
- ? action.status === "done" || action.status === "error"
- : true;
- }
- return false;
- },
- run: (dispatch, getState, action) => {
- resolve(action);
- }
- });
- });
- },
-
- async waitUntil(predicate, msg) {
- if (msg) {
- dump(`Waiting until: ${msg}\n`);
- }
- return new Promise(resolve => {
- const timer = setInterval(() => {
- if (predicate()) {
- clearInterval(timer);
- if (msg) {
- dump(`Finished Waiting until: ${msg}\n`);
- }
- resolve();
- }
- }, DEBUGGER_POLLING_INTERVAL);
- });
- },
-
- findSource(dbg, url) {
- const sources = dbg.selectors.getSources(dbg.getState());
- return sources.find(s => (s.get("url") || "").includes(url));
- },
-
- getCM(dbg) {
- const el = dbg.win.document.querySelector(".CodeMirror");
- return el.CodeMirror;
- },
-
- waitForText(dbg, url, text) {
- return this.waitUntil(() => {
- // the welcome box is removed once text is displayed
- const welcomebox = dbg.win.document.querySelector(".welcomebox");
- if (welcomebox) {
- return false;
- }
- const cm = this.getCM(dbg);
- const editorText = cm.doc.getValue();
- return editorText.includes(text);
- }, "text is visible");
- },
-
- waitForMetaData(dbg) {
- return this.waitUntil(
- () => {
- const state = dbg.store.getState();
- const source = dbg.selectors.getSelectedSource(state);
- // wait for metadata -- this involves parsing the file to determine its type.
- // if the object is empty, the data has not yet loaded
- const metaData = dbg.selectors.getSourceMetaData(state, source.get("id"));
- return !!Object.keys(metaData).length;
- },
- "has file metadata"
- );
- },
-
- waitForSources(dbg, expectedSources) {
- const { selectors } = dbg;
- function countSources(state) {
- const sources = selectors.getSources(state);
- return sources.size >= expectedSources;
- }
- return this.waitForState(dbg, countSources, "count sources");
- },
-
- async createContext(panel) {
- const { store, selectors, actions } = panel.getVarsForTests();
-
- return {
- actions,
- selectors,
- getState: store.getState,
- win: panel.panelWin,
- store
- };
- },
-
- selectSource(dbg, url) {
- dump(`Selecting source: ${url}\n`);
- const line = 1;
- const source = this.findSource(dbg, url);
- dbg.actions.selectLocation({ sourceId: source.get("id"), line });
- return this.waitForState(
- dbg,
- state => {
- const source = dbg.selectors.getSelectedSource(state);
- const isLoaded = source && source.get("loadedState") === "loaded";
- if (!isLoaded) {
- return false;
- }
-
- // wait for symbols -- a flat map of all named variables in a file -- to be calculated.
- // this is a slow process and becomes slower the larger the file is
- return dbg.selectors.hasSymbols(state, source.toJS());
- },
- "selected source"
- );
- },
-
- async addBreakpoint(dbg, line, url) {
- dump(`add breakpoint\n`);
- const source = this.findSource(dbg, url);
- const location = {
- sourceId: source.get("id"),
- line,
- column: 0
- };
- const onDispatched = debuggerHelper.waitForDispatch(dbg, "ADD_BREAKPOINT");
- dbg.actions.addBreakpoint(location);
- return onDispatched;
- },
-
- async removeBreakpoints(dbg, line, url) {
- dump(`remove all breakpoints\n`);
- const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
-
- const onBreakpointsCleared = this.waitForState(
- dbg,
- state => !dbg.selectors.getBreakpoints(state).length
- );
- await dbg.actions.removeBreakpoints(breakpoints);
- return onBreakpointsCleared;
- },
-
- async pauseDebugger(dbg, tab, testFunction, { line, file }) {
- await this.addBreakpoint(dbg, line, file);
- const onPaused = this.waitForPaused(dbg);
- await this.evalInContent(dbg, tab, testFunction);
- return onPaused;
- },
-
- async waitForPaused(dbg) {
- const onLoadedScope = this.waitForLoadedScopes(dbg);
- const onStateChange = this.waitForState(
- dbg,
- state => {
- return dbg.selectors.getSelectedScope(state) && dbg.selectors.isPaused(state);
- },
- );
- return Promise.all([onLoadedScope, onStateChange]);
- },
-
- async resume(dbg) {
- const onResumed = this.waitForResumed(dbg);
- dbg.actions.resume();
- return onResumed;
- },
-
- async waitForResumed(dbg) {
- return this.waitForState(
- dbg,
- state => !dbg.selectors.isPaused(state)
- );
- },
-
- evalInContent(dbg, tab, testFunction) {
- dump(`Run function in content process: ${testFunction}\n`);
- // Load a frame script using a data URI so we can run a script
- // inside of the content process and trigger debugger functionality
- // as needed
- const messageManager = tab.linkedBrowser.messageManager;
- return messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- content.window.eval("${testFunction}");
- }`
- ) + ")()", true);
- },
-
- async waitForElement(dbg, name) {
- await this.waitUntil(() => dbg.win.document.querySelector(name));
- return dbg.win.document.querySelector(name);
- },
-
- async waitForLoadedScopes(dbg) {
- const element = ".scopes-list .tree-node[aria-level=\"1\"]";
- return this.waitForElement(dbg, element);
- },
-
- async step(dbg, stepType) {
- const resumed = this.waitForResumed(dbg);
- dbg.actions[stepType]();
- await resumed;
- return this.waitForPaused(dbg);
- }
-};
-
-async function garbageCollect() {
- dump("Garbage collect\n");
-
- // Minimize memory usage
- // mimic miminizeMemoryUsage, by only flushing JS objects via GC.
- // We don't want to flush all the cache like minimizeMemoryUsage,
- // as it slow down next executions almost like a cold start.
-
- // See minimizeMemoryUsage code to justify the 3 iterations and the setTimeout:
- // https://searchfox.org/mozilla-central/source/xpcom/base/nsMemoryReporterManager.cpp#2574-2585
- for (let i = 0; i < 3; i++) {
- // See minimizeMemoryUsage code here to justify the GC+CC+GC:
- // https://searchfox.org/mozilla-central/rev/be78e6ea9b10b1f5b2b3b013f01d86e1062abb2b/dom/base/nsJSEnvironment.cpp#341-349
- Cu.forceGC();
- Cu.forceCC();
- Cu.forceGC();
- await new Promise(done => setTimeout(done, 0));
- }
-}
-
/* globals res:true */
function Damp() {
- // Path to the temp file where the heap snapshot file is saved. Set by
- // saveHeapSnapshot and read by readHeapSnapshot.
- this._heapSnapshotFilePath = null;
- // HeapSnapshot instance. Set by readHeapSnapshot, used by takeCensus.
- this._snapshot = null;
-
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
}
Damp.prototype = {
+ async garbageCollect() {
+ dump("Garbage collect\n");
+
+ // Minimize memory usage
+ // mimic miminizeMemoryUsage, by only flushing JS objects via GC.
+ // We don't want to flush all the cache like minimizeMemoryUsage,
+ // as it slow down next executions almost like a cold start.
+
+ // See minimizeMemoryUsage code to justify the 3 iterations and the setTimeout:
+ // https://searchfox.org/mozilla-central/source/xpcom/base/nsMemoryReporterManager.cpp#2574-2585
+ for (let i = 0; i < 3; i++) {
+ // See minimizeMemoryUsage code here to justify the GC+CC+GC:
+ // https://searchfox.org/mozilla-central/rev/be78e6ea9b10b1f5b2b3b013f01d86e1062abb2b/dom/base/nsJSEnvironment.cpp#341-349
+ Cu.forceGC();
+ Cu.forceCC();
+ Cu.forceGC();
+ await new Promise(done => setTimeout(done, 0));
+ }
+ },
+
/**
* Helper to tell when a test start and when it is finished.
* It helps recording its duration, but also put markers for perf-html when profiling
* DAMP.
*
* When this method is called, the test is considered to be starting immediately
* When the test is over, the returned object's `done` method should be called.
*
@@ -431,788 +171,65 @@ Damp.prototype = {
onReload().then(resolve);
} else {
resolve(awaitBrowserLoaded(browser));
}
browser.reload();
});
},
- async openToolbox(tool = "webconsole", onLoad) {
- let tab = getActiveTab(getMostRecentBrowserWindow());
- let target = TargetFactory.forTab(tab);
- let onToolboxCreated = gDevTools.once("toolbox-created");
- let showPromise = gDevTools.showToolbox(target, tool);
- let toolbox = await onToolboxCreated;
-
- if (typeof(onLoad) == "function") {
- let panel = await toolbox.getPanelWhenReady(tool);
- await onLoad(toolbox, panel);
- }
- await showPromise;
-
- return toolbox;
- },
-
- async closeToolbox() {
- let tab = getActiveTab(getMostRecentBrowserWindow());
- let target = TargetFactory.forTab(tab);
- await target.client.waitForRequestsToSettle();
- await gDevTools.closeToolbox(target);
- },
-
- async saveHeapSnapshot(label) {
- let tab = getActiveTab(getMostRecentBrowserWindow());
- let target = TargetFactory.forTab(tab);
- let toolbox = gDevTools.getToolbox(target);
- let panel = toolbox.getCurrentPanel();
- let memoryFront = panel.panelWin.gFront;
-
- let test = this.runTest(label + ".saveHeapSnapshot");
- this._heapSnapshotFilePath = await memoryFront.saveHeapSnapshot();
- test.done();
- },
-
- readHeapSnapshot(label) {
- let test = this.runTest(label + ".readHeapSnapshot");
- this._snapshot = ChromeUtils.readHeapSnapshot(this._heapSnapshotFilePath);
- test.done();
- return Promise.resolve();
- },
-
- async waitForNetworkRequests(label, toolbox, expectedRequests) {
- let test = this.runTest(label + ".requestsFinished.DAMP");
- await this.waitForAllRequestsFinished(expectedRequests);
- test.done();
- },
-
- async _consoleBulkLoggingTest() {
- let TOTAL_MESSAGES = 10;
- let tab = await this.testSetup(SIMPLE_URL);
- let messageManager = tab.linkedBrowser.messageManager;
- let toolbox = await this.openToolbox("webconsole");
- let webconsole = toolbox.getPanel("webconsole");
-
- // Resolve once the last message has been received.
- let allMessagesReceived = new Promise(resolve => {
- function receiveMessages(messages) {
- for (let m of messages) {
- if (m.node.textContent.includes("damp " + TOTAL_MESSAGES)) {
- webconsole.hud.ui.off("new-messages", receiveMessages);
- // Wait for the console to redraw
- requestAnimationFrame(resolve);
- }
- }
- }
- webconsole.hud.ui.on("new-messages", receiveMessages);
- });
-
- // Load a frame script using a data URI so we can do logs
- // from the page. So this is running in content.
- messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- addMessageListener("do-logs", function () {
- for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
- content.console.log('damp', i+1, content);
- }
- });
- }`
- ) + ")()", true);
-
- // Kick off the logging
- messageManager.sendAsyncMessage("do-logs");
-
- let test = this.runTest("console.bulklog");
- await allMessagesReceived;
- test.done();
-
- await this.closeToolbox();
- await this.testTeardown();
- },
-
- // Log a stream of console messages, 1 per rAF. Then record the average
- // time per rAF. The idea is that the console being slow can slow down
- // content (i.e. Bug 1237368).
- async _consoleStreamLoggingTest() {
- let TOTAL_MESSAGES = 100;
- let tab = await this.testSetup(SIMPLE_URL);
- let messageManager = tab.linkedBrowser.messageManager;
- await this.openToolbox("webconsole");
-
- // Load a frame script using a data URI so we can do logs
- // from the page. So this is running in content.
- messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- let count = 0;
- let startTime = content.performance.now();
- function log() {
- if (++count < ${TOTAL_MESSAGES}) {
- content.document.querySelector("h1").textContent += count + "\\n";
- content.console.log('damp', count,
- content,
- content.document,
- content.document.body,
- content.document.documentElement,
- new Array(100).join(" DAMP? DAMP! "));
- content.requestAnimationFrame(log);
- } else {
- let avgTime = (content.performance.now() - startTime) / ${TOTAL_MESSAGES};
- sendSyncMessage("done", Math.round(avgTime));
- }
- }
- log();
- }`
- ) + ")()", true);
-
- let avgTime = await new Promise(resolve => {
- messageManager.addMessageListener("done", (e) => {
- resolve(e.data);
- });
- });
-
- this._results.push({
- name: "console.streamlog",
- value: avgTime
- });
-
- await this.closeToolbox();
- await this.testTeardown();
- },
-
- async _consoleObjectExpansionTest() {
- let tab = await this.testSetup(SIMPLE_URL);
- let messageManager = tab.linkedBrowser.messageManager;
- let toolbox = await this.openToolbox("webconsole");
- let webconsole = toolbox.getPanel("webconsole");
-
- // Resolve once the first message is received.
- let onMessageReceived = new Promise(resolve => {
- function receiveMessages(messages) {
- for (let m of messages) {
- resolve(m);
- }
- }
- webconsole.hud.ui.once("new-messages", receiveMessages);
- });
-
- // Load a frame script using a data URI so we can do logs
- // from the page.
- messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- addMessageListener("do-dir", function () {
- content.console.dir(Array.from({length:1000}).reduce((res, _, i)=> {
- res["item_" + i] = i;
- return res;
- }, {}));
- });
- }`
- ) + ")()", true);
-
- // Kick off the logging
- messageManager.sendAsyncMessage("do-dir");
-
- let test = this.runTest("console.objectexpand");
- await onMessageReceived;
- const tree = webconsole.hud.ui.outputNode.querySelector(".dir.message .tree");
- // The tree can be collapsed since the properties are fetched asynchronously.
- if (tree.querySelectorAll(".node").length === 1) {
- // If this is the case, we wait for the properties to be fetched and displayed.
- await new Promise(resolve => {
- const observer = new MutationObserver(mutations => {
- resolve(mutations);
- observer.disconnect();
- });
- observer.observe(tree, {
- childList: true
- });
- });
- }
-
- test.done();
-
- await this.closeToolboxAndLog("console.objectexpanded", toolbox);
- await this.testTeardown();
- },
-
-async _consoleOpenWithCachedMessagesTest() {
- let TOTAL_MESSAGES = 100;
- let tab = await this.testSetup(SIMPLE_URL);
-
- // Load a frame script using a data URI so we can do logs
- // from the page. So this is running in content.
- tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(`
- function () {
- for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
- content.console.log('damp', i+1, content);
- }
- }`
- ) + ")()", true);
-
- await this.openToolboxAndLog("console.openwithcache", "webconsole");
-
- await this.closeToolbox();
- await this.testTeardown();
-},
-
- /**
- * Measure the time necessary to perform successive childList mutations in the content
- * page and update the markup-view accordingly.
- */
- async _inspectorMutationsTest() {
- let tab = await this.testSetup(SIMPLE_URL);
- let messageManager = tab.linkedBrowser.messageManager;
- let toolbox = await this.openToolbox("inspector");
- let inspector = toolbox.getPanel("inspector");
-
- // Test with n=LIMIT mutations, with t=DELAY ms between each one.
- const LIMIT = 100;
- const DELAY = 5;
-
- messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- const LIMIT = ${LIMIT};
- addMessageListener("start-mutations-test", function () {
- let addElement = function(index) {
- if (index == LIMIT) {
- // LIMIT was reached, stop adding elements.
- return;
- }
- let div = content.document.createElement("div");
- content.document.body.appendChild(div);
- content.setTimeout(() => addElement(index + 1), ${DELAY});
- };
- addElement(0);
- });
- }`
- ) + ")()", false);
-
- let test = this.runTest("inspector.mutations");
-
- await new Promise(resolve => {
- let childListMutationsCounter = 0;
- inspector.on("markupmutation", (evt, mutations) => {
- let childListMutations = mutations.filter(m => m.type === "childList");
- childListMutationsCounter += childListMutations.length;
- if (childListMutationsCounter === LIMIT) {
- // Wait until we received exactly n=LIMIT mutations in the markup view.
- resolve();
- }
- });
-
- messageManager.sendAsyncMessage("start-mutations-test");
- });
-
- test.done();
-
- await this.closeToolbox();
- await this.testTeardown();
- },
-
- /**
- * Measure the time to open toolbox on the inspector with the layout tab selected.
- */
- async _inspectorLayoutTest() {
- let tab = await this.testSetup(SIMPLE_URL);
- let messageManager = tab.linkedBrowser.messageManager;
-
- // Backup current sidebar tab preference
- let sidebarTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
-
- // Set layoutview as the current inspector sidebar tab.
- Services.prefs.setCharPref("devtools.inspector.activeSidebar", "layoutview");
-
- // Setup test page. It is a simple page containing 5000 regular nodes and 10 grid
- // containers.
- await new Promise(resolve => {
- messageManager.addMessageListener("setup-test-done", resolve);
-
- const NODES = 5000;
- const GRID_NODES = 10;
- messageManager.loadFrameScript("data:,(" + encodeURIComponent(
- `function () {
- let div = content.document.createElement("div");
- div.innerHTML =
- new Array(${NODES}).join("<div></div>") +
- new Array(${GRID_NODES}).join("<div style='display:grid'></div>");
- content.document.body.appendChild(div);
- sendSyncMessage("setup-test-done");
- }`
- ) + ")()", false);
- });
-
- // Record the time needed to open the toolbox.
- let test = this.runTest("inspector.layout.open");
- await this.openToolbox("inspector");
- test.done();
-
- await this.closeToolbox();
-
- // Restore sidebar tab preference.
- Services.prefs.setCharPref("devtools.inspector.activeSidebar", sidebarTab);
-
- await this.testTeardown();
- },
-
- takeCensus(label) {
- let test = this.runTest(label + ".takeCensus");
-
- this._snapshot.takeCensus({
- breakdown: {
- by: "coarseType",
- objects: {
- by: "objectClass",
- then: { by: "count", bytes: true, count: true },
- other: { by: "count", bytes: true, count: true }
- },
- strings: {
- by: "internalType",
- then: { by: "count", bytes: true, count: true }
- },
- scripts: {
- by: "internalType",
- then: { by: "count", bytes: true, count: true }
- },
- other: {
- by: "internalType",
- then: { by: "count", bytes: true, count: true }
- }
- }
- });
-
- test.done();
-
- return Promise.resolve();
- },
-
- /**
- * Wait for any pending paint.
- * The tool may have touched the DOM elements at the very end of the current test.
- * We should ensure waiting for the reflow related to these changes.
- */
- async waitForPendingPaints(toolbox) {
- let panel = toolbox.getCurrentPanel();
- // All panels have its own way of exposing their window object...
- let window = panel.panelWin || panel._frameWindow || panel.panelWindow;
-
- let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- window.performance.mark("pending paints.start");
- while (utils.isMozAfterPaintPending) {
- await new Promise(done => {
- window.addEventListener("MozAfterPaint", function listener() {
- window.performance.mark("pending paint");
- done();
- }, { once: true });
- });
- }
- window.performance.measure("pending paints", "pending paints.start");
- },
-
- async openToolboxAndLog(name, tool, onLoad) {
- dump("Open toolbox on '" + name + "'\n");
- let test = this.runTest(name + ".open.DAMP");
- let toolbox = await this.openToolbox(tool, onLoad);
- test.done();
-
- test = this.runTest(name + ".open.settle.DAMP");
- await this.waitForPendingPaints(toolbox);
- test.done();
-
- // Force freeing memory after toolbox open as it creates a lot of objects
- // and for complex documents, it introduces a GC that runs during 'reload' test.
- await garbageCollect();
-
- return toolbox;
- },
-
- async closeToolboxAndLog(name, toolbox) {
- let { target } = toolbox;
- dump("Close toolbox on '" + name + "'\n");
- await target.client.waitForRequestsToSettle();
-
- let test = this.runTest(name + ".close.DAMP");
- await gDevTools.closeToolbox(target);
- test.done();
- },
-
- async reloadPageAndLog(name, toolbox, onReload) {
- dump("Reload page on '" + name + "'\n");
- let test = this.runTest(name + ".reload.DAMP");
- await this.reloadPage(onReload);
- test.done();
-
- test = this.runTest(name + ".reload.settle.DAMP");
- await this.waitForPendingPaints(toolbox);
- test.done();
- },
-
- async exportHar(label, toolbox) {
- let test = this.runTest(label + ".exportHar");
-
- // Export HAR from the Network panel.
- await toolbox.getHARFromNetMonitor();
-
- test.done();
- },
-
- async _coldInspectorOpen() {
- await this.testSetup(SIMPLE_URL);
- await this.openToolboxAndLog("cold.inspector", "inspector");
- await this.closeToolbox();
- await this.testTeardown();
- },
-
- async _panelsInBackgroundReload() {
- await this.testSetup(PANELS_IN_BACKGROUND);
-
- // Make sure the Console and Network panels are initialized
- let toolbox = await this.openToolbox("webconsole");
- let monitor = await toolbox.selectTool("netmonitor");
-
- // Select the options panel to make both the Console and Network
- // panel be in background.
- // Options panel should not do anything on page reload.
- await toolbox.selectTool("options");
-
- // Reload the page and wait for all HTTP requests
- // to finish (1 doc + 600 XHRs).
- let payloadReady = this.waitForPayload(601, monitor.panelWin);
- await this.reloadPageAndLog("panelsInBackground", toolbox);
- await payloadReady;
-
- // Clean up
- await this.closeToolbox();
- await this.testTeardown();
- },
-
- waitForPayload(count, panelWin) {
- return new Promise(resolve => {
- let payloadReady = 0;
-
- function onPayloadReady(_, id) {
- payloadReady++;
- maybeResolve();
- }
-
- function maybeResolve() {
- if (payloadReady >= count) {
- panelWin.off(EVENTS.PAYLOAD_READY, onPayloadReady);
- resolve();
- }
- }
-
- panelWin.on(EVENTS.PAYLOAD_READY, onPayloadReady);
- });
- },
-
- async reloadInspectorAndLog(label, toolbox) {
- let onReload = async function() {
- let inspector = toolbox.getPanel("inspector");
- // First wait for markup view to be loaded against the new root node
- await inspector.once("new-root");
- // Then wait for inspector to be updated
- await inspector.once("inspector-updated");
- };
- await this.reloadPageAndLog(label + ".inspector", toolbox, onReload);
- },
-
- async customInspector() {
- let url = CUSTOM_URL.replace(/\$TOOL/, "inspector");
- await this.testSetup(url);
- let toolbox = await this.openToolboxAndLog("custom.inspector", "inspector");
-
- await this.reloadInspectorAndLog("custom", toolbox);
- await this.selectNodeWithManyRulesAndLog(toolbox);
- await this.closeToolboxAndLog("custom.inspector", toolbox);
- await this.testTeardown();
- },
-
- /**
- * Measure the time necessary to select a node and display the rule view when many rules
- * match the element.
- */
- async selectNodeWithManyRulesAndLog(toolbox) {
- let inspector = toolbox.getPanel("inspector");
-
- // Local helper to select a node front and wait for the ruleview to be refreshed.
- let selectNodeFront = (nodeFront) => {
- let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
- inspector.selection.setNodeFront(nodeFront);
- return onRuleViewRefreshed;
- };
-
- let initialNodeFront = inspector.selection.nodeFront;
-
- // Retrieve the node front for the test node.
- let root = await inspector.walker.getRootNode();
- let referenceNodeFront = await inspector.walker.querySelector(root, ".no-css-rules");
- let testNodeFront = await inspector.walker.querySelector(root, ".many-css-rules");
-
- // Select test node and measure the time to display the rule view with many rules.
- dump("Selecting .many-css-rules test node front\n");
- let test = this.runTest("custom.inspector.manyrules.selectnode");
- await selectNodeFront(testNodeFront);
- test.done();
-
- // Select reference node and measure the time to empty the rule view.
- dump("Move the selection to a node with no rules\n");
- test = this.runTest("custom.inspector.manyrules.deselectnode");
- await selectNodeFront(referenceNodeFront);
- test.done();
-
- await selectNodeFront(initialNodeFront);
- },
-
- async openDebuggerAndLog(label, { expectedSources, selectedFile, expectedText }) {
- const onLoad = async (toolbox, panel) => {
- const dbg = await debuggerHelper.createContext(panel);
- await debuggerHelper.waitForSources(dbg, expectedSources);
- await debuggerHelper.selectSource(dbg, selectedFile);
- await debuggerHelper.waitForText(dbg, selectedFile, expectedText);
- await debuggerHelper.waitForMetaData(dbg);
- };
- const toolbox = await this.openToolboxAndLog(label + ".jsdebugger", "jsdebugger", onLoad);
- return toolbox;
- },
-
- async pauseDebuggerAndLog(tab, toolbox, { testFunction }) {
- const panel = await toolbox.getPanelWhenReady("jsdebugger");
- const dbg = await debuggerHelper.createContext(panel);
- const pauseLocation = { line: 22, file: "App.js" };
-
- dump("Pausing debugger\n");
- let test = this.runTest("custom.jsdebugger.pause.DAMP");
- await debuggerHelper.pauseDebugger(dbg, tab, testFunction, pauseLocation);
- test.done();
-
- await debuggerHelper.removeBreakpoints(dbg);
- await debuggerHelper.resume(dbg);
- await garbageCollect();
- },
-
- async stepDebuggerAndLog(tab, toolbox, { testFunction }) {
- const panel = await toolbox.getPanelWhenReady("jsdebugger");
- const dbg = await debuggerHelper.createContext(panel);
- const stepCount = 2;
-
- /*
- * Each Step test has a max step count of at least 200;
- * see https://github.com/codehag/debugger-talos-example/blob/master/src/ and the specific test
- * file for more information
- */
-
- const stepTests = [
- {
- location: { line: 10194, file: "step-in-test.js" },
- key: "stepIn"
- },
- {
- location: { line: 16, file: "step-over-test.js" },
- key: "stepOver"
- },
- {
- location: { line: 998, file: "step-out-test.js" },
- key: "stepOut"
- }
- ];
-
- for (const stepTest of stepTests) {
- await debuggerHelper.pauseDebugger(dbg, tab, testFunction, stepTest.location);
- const test = this.runTest(`custom.jsdebugger.${stepTest.key}.DAMP`);
- for (let i = 0; i < stepCount; i++) {
- await debuggerHelper.step(dbg, stepTest.key);
- }
- test.done();
- await debuggerHelper.removeBreakpoints(dbg);
- await debuggerHelper.resume(dbg);
- await garbageCollect();
- }
- },
-
- async reloadDebuggerAndLog(label, toolbox, { expectedSources, selectedFile, expectedText }) {
- const onReload = async () => {
- const panel = await toolbox.getPanelWhenReady("jsdebugger");
- const dbg = await debuggerHelper.createContext(panel);
- await debuggerHelper.waitForDispatch(dbg, "NAVIGATE");
- await debuggerHelper.waitForSources(dbg, expectedSources);
- await debuggerHelper.waitForText(dbg, selectedFile, expectedText);
- await debuggerHelper.waitForMetaData(dbg);
- };
- await this.reloadPageAndLog(`${label}.jsdebugger`, toolbox, onReload);
- },
-
- async customDebugger() {
- const label = "custom";
- let url = CUSTOM_URL.replace(/\$TOOL/, "debugger");
-
- const tab = await this.testSetup(url);
- const debuggerTestData = {
- expectedSources: 7,
- testFunction: "window.hitBreakpoint()",
- selectedFile: "App.js",
- expectedText: "import React, { Component } from 'react';"
- };
- const toolbox = await this.openDebuggerAndLog(label, debuggerTestData);
- await this.reloadDebuggerAndLog(label, toolbox, debuggerTestData);
-
- // these tests are only run on custom.jsdebugger
- await this.pauseDebuggerAndLog(tab, toolbox, debuggerTestData);
- await this.stepDebuggerAndLog(tab, toolbox, debuggerTestData);
-
- await this.closeToolboxAndLog("custom.jsdebugger", toolbox);
- await this.testTeardown();
- },
-
- async reloadConsoleAndLog(label, toolbox, expectedMessages) {
- let onReload = async function() {
- let webconsole = toolbox.getPanel("webconsole");
- await new Promise(done => {
- let messages = 0;
- let receiveMessages = () => {
- if (++messages == expectedMessages) {
- webconsole.hud.ui.off("new-messages", receiveMessages);
- done();
- }
- };
- webconsole.hud.ui.on("new-messages", receiveMessages);
- });
- };
- await this.reloadPageAndLog(label + ".webconsole", toolbox, onReload);
- },
-
- async customConsole() {
- // These numbers controls the number of console api calls we do in the test
- let sync = 250, stream = 250, async = 250;
- let params = `?sync=${sync}&stream=${stream}&async=${async}`;
- let url = CUSTOM_URL.replace(/\$TOOL/, "console") + params;
- await this.testSetup(url);
- let toolbox = await this.openToolboxAndLog("custom.webconsole", "webconsole");
- await this.reloadConsoleAndLog("custom", toolbox, sync + stream + async);
- await this.closeToolboxAndLog("custom.webconsole", toolbox);
- await this.testTeardown();
- },
-
- _getToolLoadingTests(url, label, {
- expectedMessages,
- expectedRequests,
- debuggerTestData
- }) {
- let tests = {
- async inspector() {
- await this.testSetup(url);
- let toolbox = await this.openToolboxAndLog(label + ".inspector", "inspector");
- await this.reloadInspectorAndLog(label, toolbox);
- await this.closeToolboxAndLog(label + ".inspector", toolbox);
- await this.testTeardown();
- },
-
- async webconsole() {
- await this.testSetup(url);
- let toolbox = await this.openToolboxAndLog(label + ".webconsole", "webconsole");
- await this.reloadConsoleAndLog(label, toolbox, expectedMessages);
- await this.closeToolboxAndLog(label + ".webconsole", toolbox);
- await this.testTeardown();
- },
-
- async debugger() {
- await this.testSetup(url);
- let toolbox = await this.openDebuggerAndLog(label, debuggerTestData);
- await this.reloadDebuggerAndLog(label, toolbox, debuggerTestData);
- await this.closeToolboxAndLog(label + ".jsdebugger", toolbox);
- await this.testTeardown();
- },
-
- async styleeditor() {
- await this.testSetup(url);
- const toolbox = await this.openToolboxAndLog(label + ".styleeditor", "styleeditor");
- await this.reloadPageAndLog(label + ".styleeditor", toolbox);
- await this.closeToolboxAndLog(label + ".styleeditor", toolbox);
- await this.testTeardown();
- },
-
- async performance() {
- await this.testSetup(url);
- const toolbox = await this.openToolboxAndLog(label + ".performance", "performance");
- await this.reloadPageAndLog(label + ".performance", toolbox);
- await this.closeToolboxAndLog(label + ".performance", toolbox);
- await this.testTeardown();
- },
-
- async netmonitor() {
- await this.testSetup(url);
- const toolbox = await this.openToolboxAndLog(label + ".netmonitor", "netmonitor");
- const requestsDone = this.waitForNetworkRequests(label + ".netmonitor", toolbox, expectedRequests);
- await this.reloadPageAndLog(label + ".netmonitor", toolbox);
- await requestsDone;
- await this.exportHar(label + ".netmonitor", toolbox);
- await this.closeToolboxAndLog(label + ".netmonitor", toolbox);
- await this.testTeardown();
- },
-
- async saveAndReadHeapSnapshot() {
- await this.testSetup(url);
- const toolbox = await this.openToolboxAndLog(label + ".memory", "memory");
- await this.reloadPageAndLog(label + ".memory", toolbox);
- await this.saveHeapSnapshot(label);
- await this.readHeapSnapshot(label);
- await this.takeCensus(label);
- await this.closeToolboxAndLog(label + ".memory", toolbox);
- await this.testTeardown();
- },
- };
- // Prefix all tests with the page type (simple or complicated)
- for (let name in tests) {
- tests[label + "." + name] = tests[name];
- delete tests[name];
- }
- return tests;
- },
-
async testSetup(url) {
let tab = await this.addTab(url);
await new Promise(resolve => {
setTimeout(resolve, this._config.rest);
});
return tab;
},
- async testTeardown(url) {
+ async testTeardown() {
this.closeCurrentTab();
// Force freeing memory now so that it doesn't happen during the next test
- await garbageCollect();
+ await this.garbageCollect();
- this._nextCommand();
+ this._runNextTest();
},
// Everything below here are common pieces needed for the test runner to function,
// just copy and pasted from Tart with /s/TART/DAMP
_win: undefined,
_dampTab: undefined,
_results: [],
_config: {subtests: [], repeat: 1, rest: 100},
- _nextCommandIx: 0,
- _commands: [],
+ _nextTestIndex: 0,
+ _tests: [],
_onSequenceComplete: 0,
- _nextCommand() {
- if (this._nextCommandIx >= this._commands.length) {
+ _runNextTest() {
+ if (this._nextTestIndex >= this._tests.length) {
this._onSequenceComplete();
return;
}
- this._commands[this._nextCommandIx++].call(this);
+
+ let test = this._tests[this._nextTestIndex++];
+
+ dump("Loading test : " + test + "\n");
+ let testMethod = require("chrome://damp/content/tests/" + test);
+
+ dump("Executing test : " + test + "\n");
+ testMethod();
},
// Each command at the array a function which must call nextCommand once it's done
- _doSequence(commands, onComplete) {
- this._commands = commands;
+ _doSequence(tests, onComplete) {
+ this._tests = tests;
this._onSequenceComplete = onComplete;
this._results = [];
- this._nextCommandIx = 0;
+ this._nextTestIndex = 0;
- this._nextCommand();
+ this._runNextTest();
},
_log(str) {
if (window.MozillaFileLogger && window.MozillaFileLogger.log)
window.MozillaFileLogger.log(str);
window.dump(str);
},
@@ -1256,74 +273,26 @@ async _consoleOpenWithCachedMessagesTest
this._reportAllResults();
this._win.gBrowser.selectedTab = this._dampTab;
if (this._onTestComplete) {
this._onTestComplete(JSON.parse(JSON.stringify(this._results))); // Clone results
}
},
- /**
- * Start monitoring all incoming update events about network requests and wait until
- * a complete info about all requests is received. (We wait for the timings info
- * explicitly, because that's always the last piece of information that is received.)
- *
- * This method is designed to wait for network requests that are issued during a page
- * load, when retrieving page resources (scripts, styles, images). It has certain
- * assumptions that can make it unsuitable for other types of network communication:
- * - it waits for at least one network request to start and finish before returning
- * - it waits only for request that were issued after it was called. Requests that are
- * already in mid-flight will be ignored.
- * - the request start and end times are overlapping. If a new request starts a moment
- * after the previous one was finished, the wait will be ended in the "interim"
- * period.
- * @returns a promise that resolves when the wait is done.
- */
- waitForAllRequestsFinished(expectedRequests) {
- let tab = getActiveTab(getMostRecentBrowserWindow());
- let target = TargetFactory.forTab(tab);
- let toolbox = gDevTools.getToolbox(target);
- let window = toolbox.getCurrentPanel().panelWin;
-
- return new Promise(resolve => {
- // Explicitly waiting for specific number of requests arrived
- let payloadReady = 0;
- let timingsUpdated = 0;
-
- function onPayloadReady(_, id) {
- payloadReady++;
- maybeResolve();
- }
-
- function onTimingsUpdated(_, id) {
- timingsUpdated++;
- maybeResolve();
- }
-
- function maybeResolve() {
- // Have all the requests finished yet?
- if (payloadReady >= expectedRequests && timingsUpdated >= expectedRequests) {
- // All requests are done - unsubscribe from events and resolve!
- window.off(EVENTS.PAYLOAD_READY, onPayloadReady);
- window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
- resolve();
- }
- }
-
- window.on(EVENTS.PAYLOAD_READY, onPayloadReady);
- window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
- });
- },
-
startAllocationTracker() {
const { allocationTracker } = require("devtools/shared/test-helpers/allocation-tracker");
return allocationTracker();
},
startTest(doneCallback, config) {
+ dump("Initialize the head file with a reference to this DAMP instance\n");
+ let head = require("chrome://damp/content/tests/head.js");
+ head.initialize(this);
+
this._onTestComplete = function(results) {
TalosParentProfiler.pause("DAMP - end");
doneCallback(results);
};
this._config = config;
this._win = Services.wm.getMostRecentWindow("navigator:browser");
this._dampTab = this._win.gBrowser.selectedTab;
@@ -1332,55 +301,51 @@ async _consoleOpenWithCachedMessagesTest
TalosParentProfiler.resume("DAMP - start");
let tests = {};
// Run cold test only once
let topWindow = getMostRecentBrowserWindow();
if (!topWindow.coldRunDAMP) {
topWindow.coldRunDAMP = true;
- tests["cold.inspector"] = this._coldInspectorOpen;
+ tests["cold.inspector"] = "inspector/cold-open.js";
}
- tests["panelsInBackground.reload"] = this._panelsInBackgroundReload;
+ tests["panelsInBackground.reload"] = "toolbox/panels-in-background.js";
// Run all tests against "simple" document
- Object.assign(tests, this._getToolLoadingTests(SIMPLE_URL, "simple", {
- expectedMessages: 1,
- expectedRequests: 1,
- debuggerTestData: {
- expectedSources: 1,
- selectedFile: "simple.html",
- expectedText: "This is a simple page"
- }
- }));
+ tests["simple.inspector"] = "inspector/simple.js";
+ tests["simple.webconsole"] = "webconsole/simple.js";
+ tests["simple.debugger"] = "debugger/simple.js";
+ tests["simple.styleeditor"] = "styleeditor/simple.js";
+ tests["simple.performance"] = "performance/simple.js";
+ tests["simple.netmonitor"] = "netmonitor/simple.js";
+ tests["simple.saveAndReadHeapSnapshot"] = "memory/simple.js";
// Run all tests against "complicated" document
- Object.assign(tests, this._getToolLoadingTests(COMPLICATED_URL, "complicated", {
- expectedMessages: 7,
- expectedRequests: 280,
- debuggerTestData: {
- expectedSources: 14,
- selectedFile: "ga.js",
- expectedText: "Math;function ga(a,b){return a.name=b}"
- }
- }));
+ tests["complicated.inspector"] = "inspector/complicated.js";
+ tests["complicated.webconsole"] = "webconsole/complicated.js";
+ tests["complicated.debugger"] = "debugger/complicated.js";
+ tests["complicated.styleeditor"] = "styleeditor/complicated.js";
+ tests["complicated.performance"] = "performance/complicated.js";
+ tests["complicated.netmonitor"] = "netmonitor/complicated.js";
+ tests["complicated.saveAndReadHeapSnapshot"] = "memory/complicated.js";
// Run all tests against a document specific to each tool
- tests["custom.inspector"] = this.customInspector;
- tests["custom.debugger"] = this.customDebugger;
- tests["custom.webconsole"] = this.customConsole;
+ tests["custom.inspector"] = "inspector/custom.js";
+ tests["custom.debugger"] = "debugger/custom.js";
+ tests["custom.webconsole"] = "webconsole/custom.js";
// Run individual tests covering a very precise tool feature.
- tests["console.bulklog"] = this._consoleBulkLoggingTest;
- tests["console.streamlog"] = this._consoleStreamLoggingTest;
- tests["console.objectexpand"] = this._consoleObjectExpansionTest;
- tests["console.openwithcache"] = this._consoleOpenWithCachedMessagesTest;
- tests["inspector.mutations"] = this._inspectorMutationsTest;
- tests["inspector.layout"] = this._inspectorLayoutTest;
+ tests["console.bulklog"] = "webconsole/bulklog.js";
+ tests["console.streamlog"] = "webconsole/streamlog.js";
+ tests["console.objectexpand"] = "webconsole/objectexpand.js";
+ tests["console.openwithcache"] = "webconsole/openwithcache.js";
+ tests["inspector.mutations"] = "inspector/mutations.js";
+ tests["inspector.layout"] = "inspector/layout.js";
// âš Adding new individual tests slows down DAMP execution âš
// âš Consider contributing to custom.${tool} rather than adding isolated tests âš
// âš See http://docs.firefox-dev.tools/tests/writing-perf-tests.html âš
// Filter tests via `./mach --subtests filter` command line argument
let filter = Services.prefs.getCharPref("talos.subtests", "");
if (filter) {
for (let name in tests) {
@@ -1397,21 +362,22 @@ async _consoleOpenWithCachedMessagesTest
// Construct the sequence array while filtering tests
let sequenceArray = [];
for (var i in config.subtests) {
for (var r = 0; r < config.repeat; r++) {
if (!config.subtests[i] || !tests[config.subtests[i]]) {
continue;
}
+
sequenceArray.push(tests[config.subtests[i]]);
}
}
// Free memory before running the first test, otherwise we may have a GC
// related to Firefox startup or DAMP setup during the first test.
- garbageCollect().then(() => {
+ this.garbageCollect().then(() => {
this._doSequence(sequenceArray, this._doneInternal);
}).catch(e => {
dump("Exception while running DAMP tests: " + e + "\n" + e.stack + "\n");
});
}
};
rename from testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/panels-in-background.html
rename to testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/index.html
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/.eslintrc.js
@@ -0,0 +1,33 @@
+"use strict";
+
+module.exports = {
+ "plugins": [
+ "react"
+ ],
+ "globals": {
+ "exports": true,
+ "isWorker": true,
+ "loader": true,
+ "module": true,
+ "reportError": true,
+ "require": true,
+ },
+ "rules": {
+ "no-unused-vars": ["error", {"args": "none", "vars": "all"}],
+ // These are the rules that have been configured so far to match the
+ // devtools coding style.
+
+ // Rules from the mozilla plugin
+ "mozilla/no-aArgs": "error",
+ "mozilla/no-cpows-in-tests": "error",
+ "mozilla/no-single-arg-cu-import": "error",
+ // See bug 1224289.
+ "mozilla/reject-importGlobalProperties": "error",
+ // devtools/shared/platform is special; see the README.md in that
+ // directory for details. We reject requires using explicit
+ // subdirectories of this directory.
+ "mozilla/reject-some-requires": ["error", "^devtools/shared/platform/(chome|content)/"],
+ "mozilla/var-only-at-top-level": "error",
+ "mozilla/use-chromeutils-import": ["error", {allowCu: true}]
+ }
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/complicated.js
@@ -0,0 +1,24 @@
+/* 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 { closeToolboxAndLog, testSetup, testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+const { openDebuggerAndLog, reloadDebuggerAndLog } = require("chrome://damp/content/tests/debugger/debugger-helpers");
+
+const EXPECTED = {
+ sources: 14,
+ file: "ga.js",
+ text: "Math;function ga(a,b){return a.name=b}"
+};
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+
+ let toolbox = await openDebuggerAndLog("complicated", EXPECTED);
+ await reloadDebuggerAndLog("complicated", toolbox, EXPECTED);
+ await closeToolboxAndLog("complicated.jsdebugger", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/custom.js
@@ -0,0 +1,91 @@
+/* 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 { closeToolboxAndLog, garbageCollect, runTest, testSetup,
+ testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
+const { createContext, openDebuggerAndLog, pauseDebugger, reloadDebuggerAndLog,
+ removeBreakpoints, resume, step } = require("chrome://damp/content/tests/debugger/debugger-helpers");
+
+const EXPECTED = {
+ sources: 7,
+ file: "App.js",
+ text: "import React, { Component } from 'react';"
+};
+
+const EXPECTED_FUNCTION = "window.hitBreakpoint()";
+
+module.exports = async function() {
+ const tab = await testSetup(PAGES_BASE_URL + "custom/debugger/index.html");
+
+ const toolbox = await openDebuggerAndLog("custom", EXPECTED);
+ await reloadDebuggerAndLog("custom", toolbox, EXPECTED);
+
+ // these tests are only run on custom.jsdebugger
+ await pauseDebuggerAndLog(tab, toolbox, EXPECTED_FUNCTION);
+ await stepDebuggerAndLog(tab, toolbox, EXPECTED_FUNCTION);
+
+ await closeToolboxAndLog("custom.jsdebugger", toolbox);
+
+ await testTeardown();
+};
+
+async function pauseDebuggerAndLog(tab, toolbox, testFunction) {
+ dump("Waiting for debugger panel\n");
+ const panel = await toolbox.getPanelWhenReady("jsdebugger");
+
+ dump("Creating context\n");
+ const dbg = await createContext(panel);
+
+ const pauseLocation = { line: 22, file: "App.js" };
+
+ dump("Pausing debugger\n");
+ let test = runTest("custom.jsdebugger.pause.DAMP");
+ await pauseDebugger(dbg, tab, testFunction, pauseLocation);
+ test.done();
+
+ await removeBreakpoints(dbg);
+ await resume(dbg);
+ await garbageCollect();
+}
+
+async function stepDebuggerAndLog(tab, toolbox, testFunction) {
+ const panel = await toolbox.getPanelWhenReady("jsdebugger");
+ const dbg = await createContext(panel);
+ const stepCount = 2;
+
+ /*
+ * Each Step test has a max step count of at least 200;
+ * see https://github.com/codehag/debugger-talos-example/blob/master/src/ and the specific test
+ * file for more information
+ */
+
+ const stepTests = [
+ {
+ location: { line: 10194, file: "step-in-test.js" },
+ key: "stepIn"
+ },
+ {
+ location: { line: 16, file: "step-over-test.js" },
+ key: "stepOver"
+ },
+ {
+ location: { line: 998, file: "step-out-test.js" },
+ key: "stepOut"
+ }
+ ];
+
+ for (const stepTest of stepTests) {
+ await pauseDebugger(dbg, tab, testFunction, stepTest.location);
+ const test = runTest(`custom.jsdebugger.${stepTest.key}.DAMP`);
+ for (let i = 0; i < stepCount; i++) {
+ await step(dbg, stepTest.key);
+ }
+ test.done();
+ await removeBreakpoints(dbg);
+ await resume(dbg);
+ await garbageCollect();
+ }
+}
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js
@@ -0,0 +1,277 @@
+/* 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 { openToolboxAndLog, reloadPageAndLog } = require("chrome://damp/content/tests/head");
+
+/*
+ * These methods are used for working with debugger state changes in order
+ * to make it easier to manipulate the ui and test different behavior. These
+ * methods roughly reflect those found in debugger/new/test/mochi/head.js with
+ * a few exceptions. The `dbg` object is not exactly the same, and the methods
+ * have been simplified. We may want to consider unifying them in the future
+ */
+
+const DEBUGGER_POLLING_INTERVAL = 50;
+
+function waitForState(dbg, predicate, msg) {
+ return new Promise(resolve => {
+ if (msg) {
+ dump(`Waiting for state change: ${msg}\n`);
+ }
+ if (predicate(dbg.store.getState())) {
+ if (msg) {
+ dump(`Finished waiting for state change: ${msg}\n`);
+ }
+ return resolve();
+ }
+
+ const unsubscribe = dbg.store.subscribe(() => {
+ if (predicate(dbg.store.getState())) {
+ if (msg) {
+ dump(`Finished waiting for state change: ${msg}\n`);
+ }
+ unsubscribe();
+ resolve();
+ }
+ });
+ return false;
+ });
+}
+
+function waitForDispatch(dbg, type) {
+ return new Promise(resolve => {
+ dbg.store.dispatch({
+ type: "@@service/waitUntil",
+ predicate: action => {
+ if (action.type === type) {
+ return action.status
+ ? action.status === "done" || action.status === "error"
+ : true;
+ }
+ return false;
+ },
+ run: (dispatch, getState, action) => {
+ resolve(action);
+ }
+ });
+ });
+}
+
+async function waitUntil(predicate, msg) {
+ if (msg) {
+ dump(`Waiting until: ${msg}\n`);
+ }
+ return new Promise(resolve => {
+ const timer = setInterval(() => {
+ if (predicate()) {
+ clearInterval(timer);
+ if (msg) {
+ dump(`Finished Waiting until: ${msg}\n`);
+ }
+ resolve();
+ }
+ }, DEBUGGER_POLLING_INTERVAL);
+ });
+}
+
+function findSource(dbg, url) {
+ const sources = dbg.selectors.getSources(dbg.getState());
+ return sources.find(s => (s.get("url") || "").includes(url));
+}
+
+function getCM(dbg) {
+ const el = dbg.win.document.querySelector(".CodeMirror");
+ return el.CodeMirror;
+}
+
+function waitForText(dbg, url, text) {
+ return waitUntil(() => {
+ // the welcome box is removed once text is displayed
+ const welcomebox = dbg.win.document.querySelector(".welcomebox");
+ if (welcomebox) {
+ return false;
+ }
+ const cm = getCM(dbg);
+ const editorText = cm.doc.getValue();
+ return editorText.includes(text);
+ }, "text is visible");
+}
+
+function waitForMetaData(dbg) {
+ return waitUntil(
+ () => {
+ const state = dbg.store.getState();
+ const source = dbg.selectors.getSelectedSource(state);
+ // wait for metadata -- this involves parsing the file to determine its type.
+ // if the object is empty, the data has not yet loaded
+ const metaData = dbg.selectors.getSourceMetaData(state, source.get("id"));
+ return !!Object.keys(metaData).length;
+ },
+ "has file metadata"
+ );
+}
+
+function waitForSources(dbg, expectedSources) {
+ const { selectors } = dbg;
+ function countSources(state) {
+ const sources = selectors.getSources(state);
+ return sources.size >= expectedSources;
+ }
+ return waitForState(dbg, countSources, "count sources");
+}
+
+async function waitForPaused(dbg) {
+ const onLoadedScope = waitForLoadedScopes(dbg);
+ const onStateChange = waitForState(
+ dbg,
+ state => {
+ return dbg.selectors.getSelectedScope(state) && dbg.selectors.isPaused(state);
+ },
+ );
+ return Promise.all([onLoadedScope, onStateChange]);
+}
+
+async function waitForResumed(dbg) {
+ return waitForState(
+ dbg,
+ state => !dbg.selectors.isPaused(state)
+ );
+}
+
+async function waitForElement(dbg, name) {
+ await waitUntil(() => dbg.win.document.querySelector(name));
+ return dbg.win.document.querySelector(name);
+}
+
+async function waitForLoadedScopes(dbg) {
+ const element = ".scopes-list .tree-node[aria-level=\"1\"]";
+ return waitForElement(dbg, element);
+}
+
+function createContext(panel) {
+ const { store, selectors, actions } = panel.getVarsForTests();
+
+ return {
+ actions,
+ selectors,
+ getState: store.getState,
+ win: panel.panelWin,
+ store
+ };
+}
+exports.createContext = createContext;
+
+function selectSource(dbg, url) {
+ dump(`Selecting source: ${url}\n`);
+ const line = 1;
+ const source = findSource(dbg, url);
+ dbg.actions.selectLocation({ sourceId: source.get("id"), line });
+ return waitForState(
+ dbg,
+ state => {
+ const source = dbg.selectors.getSelectedSource(state);
+ const isLoaded = source && source.get("loadedState") === "loaded";
+ if (!isLoaded) {
+ return false;
+ }
+
+ // wait for symbols -- a flat map of all named variables in a file -- to be calculated.
+ // this is a slow process and becomes slower the larger the file is
+ return dbg.selectors.hasSymbols(state, source.toJS());
+ },
+ "selected source"
+ );
+}
+
+function evalInContent(dbg, tab, testFunction) {
+ dump(`Run function in content process: ${testFunction}\n`);
+ // Load a frame script using a data URI so we can run a script
+ // inside of the content process and trigger debugger functionality
+ // as needed
+ const messageManager = tab.linkedBrowser.messageManager;
+ return messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ content.window.eval("${testFunction}");
+ }`
+ ) + ")()", true);
+}
+
+async function openDebuggerAndLog(label, expected) {
+ const onLoad = async (toolbox, panel) => {
+ const dbg = await createContext(panel);
+ await waitForSources(dbg, expected.sources);
+ await selectSource(dbg, expected.file);
+ await waitForText(dbg, expected.file, expected.text);
+ await waitForMetaData(dbg);
+ };
+
+ const toolbox = await openToolboxAndLog(label + ".jsdebugger", "jsdebugger", onLoad);
+ return toolbox;
+}
+exports.openDebuggerAndLog = openDebuggerAndLog;
+
+async function reloadDebuggerAndLog(label, toolbox, expected) {
+ const onReload = async () => {
+ const panel = await toolbox.getPanelWhenReady("jsdebugger");
+ const dbg = await createContext(panel);
+ await waitForDispatch(dbg, "NAVIGATE");
+ await waitForSources(dbg, expected.sources);
+ await waitForText(dbg, expected.file, expected.text);
+ await waitForMetaData(dbg);
+ };
+ await reloadPageAndLog(`${label}.jsdebugger`, toolbox, onReload);
+}
+exports.reloadDebuggerAndLog = reloadDebuggerAndLog;
+
+async function addBreakpoint(dbg, line, url) {
+ dump(`add breakpoint\n`);
+ const source = findSource(dbg, url);
+ const location = {
+ sourceId: source.get("id"),
+ line,
+ column: 0
+ };
+ const onDispatched = waitForDispatch(dbg, "ADD_BREAKPOINT");
+ dbg.actions.addBreakpoint(location);
+ return onDispatched;
+}
+exports.addBreakpoint = addBreakpoint;
+
+async function removeBreakpoints(dbg, line, url) {
+ dump(`remove all breakpoints\n`);
+ const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
+
+ const onBreakpointsCleared = waitForState(
+ dbg,
+ state => !dbg.selectors.getBreakpoints(state).length
+ );
+ await dbg.actions.removeBreakpoints(breakpoints);
+ return onBreakpointsCleared;
+}
+exports.removeBreakpoints = removeBreakpoints;
+
+async function pauseDebugger(dbg, tab, testFunction, { line, file }) {
+ await addBreakpoint(dbg, line, file);
+ const onPaused = waitForPaused(dbg);
+ await evalInContent(dbg, tab, testFunction);
+ return onPaused;
+}
+exports.pauseDebugger = pauseDebugger;
+
+async function resume(dbg) {
+ const onResumed = waitForResumed(dbg);
+ dbg.actions.resume();
+ return onResumed;
+}
+exports.resume = resume;
+
+async function step(dbg, stepType) {
+ const resumed = waitForResumed(dbg);
+ dbg.actions[stepType]();
+ await resumed;
+ return waitForPaused(dbg);
+}
+exports.step = step;
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/simple.js
@@ -0,0 +1,24 @@
+/* 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 { closeToolboxAndLog, testSetup, testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+const { openDebuggerAndLog, reloadDebuggerAndLog } = require("chrome://damp/content/tests/debugger/debugger-helpers");
+
+const EXPECTED = {
+ sources: 1,
+ file: "simple.html",
+ text: "This is a simple page"
+};
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+
+ let toolbox = await openDebuggerAndLog("simple", EXPECTED);
+ await reloadDebuggerAndLog("simple", toolbox, EXPECTED);
+ await closeToolboxAndLog("simple.jsdebugger", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
@@ -0,0 +1,157 @@
+/* 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 helper contains the public API that can be used by DAMP tests.
+ */
+
+// eslint-disable-next-line mozilla/no-define-cc-etc
+const { Ci } = require("chrome");
+const Services = require("Services");
+const { gDevTools } = require("devtools/client/framework/devtools");
+const { TargetFactory } = require("devtools/client/framework/target");
+
+const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
+
+const PAGES_BASE_URL = webserver + "/tests/devtools/addon/content/pages/";
+
+exports.PAGES_BASE_URL = PAGES_BASE_URL;
+exports.SIMPLE_URL = PAGES_BASE_URL + "simple.html";
+exports.COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
+
+let damp = null;
+/*
+ * This method should be called by js before starting the tests.
+ */
+exports.initialize = function(_damp) {
+ damp = _damp;
+};
+
+function garbageCollect() {
+ return damp.garbageCollect();
+}
+exports.garbageCollect = garbageCollect;
+
+function runTest(label) {
+ return damp.runTest(label);
+}
+exports.runTest = runTest;
+
+exports.testSetup = function(url) {
+ return damp.testSetup(url);
+};
+
+exports.testTeardown = function() {
+ return damp.testTeardown();
+};
+
+exports.logTestResult = function(name, value) {
+ damp._results.push(name, value);
+};
+
+function getBrowserWindow() {
+ return Services.wm.getMostRecentWindow("navigator:browser");
+}
+exports.getBrowserWindow = getBrowserWindow;
+
+function getActiveTab() {
+ return getBrowserWindow().gBrowser.selectedTab;
+}
+exports.getActiveTab = getActiveTab;
+
+exports.getToolbox = function() {
+ let tab = getActiveTab();
+ let target = TargetFactory.forTab(tab);
+ return gDevTools.getToolbox(target);
+};
+
+/**
+ * Wait for any pending paint.
+ * The tool may have touched the DOM elements at the very end of the current test.
+ * We should ensure waiting for the reflow related to these changes.
+ */
+async function waitForPendingPaints(toolbox) {
+ let panel = toolbox.getCurrentPanel();
+ // All panels have its own way of exposing their window object...
+ let window = panel.panelWin || panel._frameWindow || panel.panelWindow;
+
+ let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ window.performance.mark("pending paints.start");
+ while (utils.isMozAfterPaintPending) {
+ await new Promise(done => {
+ window.addEventListener("MozAfterPaint", function listener() {
+ window.performance.mark("pending paint");
+ done();
+ }, { once: true });
+ });
+ }
+ window.performance.measure("pending paints", "pending paints.start");
+}
+
+const openToolbox = async function(tool = "webconsole", onLoad) {
+ let tab = getActiveTab();
+ let target = TargetFactory.forTab(tab);
+ let onToolboxCreated = gDevTools.once("toolbox-created");
+ let showPromise = gDevTools.showToolbox(target, tool);
+ let toolbox = await onToolboxCreated;
+
+ if (typeof(onLoad) == "function") {
+ let panel = await toolbox.getPanelWhenReady(tool);
+ await onLoad(toolbox, panel);
+ }
+ await showPromise;
+
+ return toolbox;
+};
+exports.openToolbox = openToolbox;
+
+exports.closeToolbox = async function() {
+ let tab = getActiveTab();
+ let target = TargetFactory.forTab(tab);
+ await target.client.waitForRequestsToSettle();
+ await gDevTools.closeToolbox(target);
+};
+
+exports.openToolboxAndLog = async function(name, tool, onLoad) {
+let test = runTest(name + ".open.DAMP");
+ let toolbox = await openToolbox(tool, onLoad);
+ test.done();
+
+ test = runTest(name + ".open.settle.DAMP");
+ await waitForPendingPaints(toolbox);
+ test.done();
+
+ // Force freeing memory after toolbox open as it creates a lot of objects
+ // and for complex documents, it introduces a GC that runs during 'reload' test.
+ await garbageCollect();
+
+ return toolbox;
+};
+
+exports.closeToolboxAndLog = async function(name, toolbox) {
+ let { target } = toolbox;
+ dump("Close toolbox on '" + name + "'\n");
+ await target.client.waitForRequestsToSettle();
+
+ let test = runTest(name + ".close.DAMP");
+ await gDevTools.closeToolbox(target);
+ test.done();
+};
+
+exports.reloadPageAndLog = async function(name, toolbox, onReload) {
+ dump("Reload page on '" + name + "'\n");
+ let test = runTest(name + ".reload.DAMP");
+ await damp.reloadPage(onReload);
+ test.done();
+
+ dump("Wait for pending paints on '" + name + "'\n");
+ test = runTest(name + ".reload.settle.DAMP");
+ await waitForPendingPaints(toolbox);
+ test.done();
+};
+
+dump("Required HEAD successfully\n");
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/cold-open.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { openToolboxAndLog, closeToolbox, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+// This simple test is only called once using the flag coldRun
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+ await openToolboxAndLog("cold.inspector", "inspector");
+ await closeToolbox();
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/complicated.js
@@ -0,0 +1,19 @@
+/* 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 { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
+const { openToolboxAndLog, closeToolboxAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+
+ let toolbox = await openToolboxAndLog("complicated.inspector", "inspector");
+ await reloadInspectorAndLog("complicated", toolbox);
+ await closeToolboxAndLog("complicated.inspector", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
@@ -0,0 +1,56 @@
+/* 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 { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
+const { openToolboxAndLog, closeToolboxAndLog, runTest, testSetup,
+ testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(PAGES_BASE_URL + "custom/inspector/index.html");
+
+ let toolbox = await openToolboxAndLog("custom.inspector", "inspector");
+ await reloadInspectorAndLog("custom", toolbox);
+ await selectNodeWithManyRulesAndLog(toolbox);
+ await closeToolboxAndLog("custom.inspector", toolbox);
+
+ await testTeardown();
+};
+
+/**
+ * Measure the time necessary to select a node and display the rule view when many rules
+ * match the element.
+ */
+async function selectNodeWithManyRulesAndLog(toolbox) {
+ let inspector = toolbox.getPanel("inspector");
+
+ // Local helper to select a node front and wait for the ruleview to be refreshed.
+ let selectNodeFront = (nodeFront) => {
+ let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
+ inspector.selection.setNodeFront(nodeFront);
+ return onRuleViewRefreshed;
+ };
+
+ let initialNodeFront = inspector.selection.nodeFront;
+
+ // Retrieve the node front for the test node.
+ let root = await inspector.walker.getRootNode();
+ let referenceNodeFront = await inspector.walker.querySelector(root, ".no-css-rules");
+ let testNodeFront = await inspector.walker.querySelector(root, ".many-css-rules");
+
+ // Select test node and measure the time to display the rule view with many rules.
+ dump("Selecting .many-css-rules test node front\n");
+ let test = runTest("custom.inspector.manyrules.selectnode");
+ await selectNodeFront(testNodeFront);
+ test.done();
+
+ // Select reference node and measure the time to empty the rule view.
+ dump("Move the selection to a node with no rules\n");
+ test = runTest("custom.inspector.manyrules.deselectnode");
+ await selectNodeFront(referenceNodeFront);
+ test.done();
+
+ await selectNodeFront(initialNodeFront);
+}
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
@@ -0,0 +1,19 @@
+/* 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 { reloadPageAndLog } = require("chrome://damp/content/tests/head");
+
+exports.reloadInspectorAndLog = async function(label, toolbox) {
+ let onReload = async function() {
+ let inspector = toolbox.getPanel("inspector");
+ // First wait for markup view to be loaded against the new root node
+ await inspector.once("new-root");
+ // Then wait for inspector to be updated
+ await inspector.once("inspector-updated");
+ };
+
+ await reloadPageAndLog(label + ".inspector", toolbox, onReload);
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/layout.js
@@ -0,0 +1,52 @@
+/* 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 Services = require("Services");
+
+const { openToolbox, closeToolbox, runTest, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ let tab = await testSetup(SIMPLE_URL);
+ let messageManager = tab.linkedBrowser.messageManager;
+
+ // Backup current sidebar tab preference
+ let sidebarTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
+
+ // Set layoutview as the current inspector sidebar tab.
+ Services.prefs.setCharPref("devtools.inspector.activeSidebar", "layoutview");
+
+ // Setup test page. It is a simple page containing 5000 regular nodes and 10 grid
+ // containers.
+ await new Promise(resolve => {
+ messageManager.addMessageListener("setup-test-done", resolve);
+
+ const NODES = 5000;
+ const GRID_NODES = 10;
+ messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ let div = content.document.createElement("div");
+ div.innerHTML =
+ new Array(${NODES}).join("<div></div>") +
+ new Array(${GRID_NODES}).join("<div style='display:grid'></div>");
+ content.document.body.appendChild(div);
+ sendSyncMessage("setup-test-done");
+ }`
+ ) + ")()", false);
+ });
+
+ // Record the time needed to open the toolbox.
+ let test = runTest("inspector.layout.open");
+ await openToolbox("inspector");
+ test.done();
+
+ await closeToolbox();
+
+ // Restore sidebar tab preference.
+ Services.prefs.setCharPref("devtools.inspector.activeSidebar", sidebarTab);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
@@ -0,0 +1,63 @@
+/* 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 { openToolbox, closeToolbox, runTest, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+/**
+ * Measure the time necessary to perform successive childList mutations in the content
+ * page and update the markup-view accordingly.
+ */
+module.exports = async function() {
+ let tab = await testSetup(SIMPLE_URL);
+ let messageManager = tab.linkedBrowser.messageManager;
+
+ let toolbox = await openToolbox("inspector");
+ let inspector = toolbox.getPanel("inspector");
+
+ // Test with n=LIMIT mutations, with t=DELAY ms between each one.
+ const LIMIT = 100;
+ const DELAY = 5;
+
+ messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ const LIMIT = ${LIMIT};
+ addMessageListener("start-mutations-test", function () {
+ let addElement = function(index) {
+ if (index == LIMIT) {
+ // LIMIT was reached, stop adding elements.
+ return;
+ }
+ let div = content.document.createElement("div");
+ content.document.body.appendChild(div);
+ content.setTimeout(() => addElement(index + 1), ${DELAY});
+ };
+ addElement(0);
+ });
+ }`
+ ) + ")()", false);
+
+ let test = runTest("inspector.mutations");
+
+ await new Promise(resolve => {
+ let childListMutationsCounter = 0;
+ inspector.on("markupmutation", (evt, mutations) => {
+ let childListMutations = mutations.filter(m => m.type === "childList");
+ childListMutationsCounter += childListMutations.length;
+ if (childListMutationsCounter === LIMIT) {
+ // Wait until we received exactly n=LIMIT mutations in the markup view.
+ resolve();
+ }
+ });
+
+ messageManager.sendAsyncMessage("start-mutations-test");
+ });
+
+ test.done();
+ await closeToolbox();
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/simple.js
@@ -0,0 +1,19 @@
+/* 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 { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
+const { openToolboxAndLog, closeToolboxAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+
+ let toolbox = await openToolboxAndLog("simple.inspector", "inspector");
+ await reloadInspectorAndLog("simple", toolbox);
+ await closeToolboxAndLog("simple.inspector", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/complicated.js
@@ -0,0 +1,24 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+const { saveHeapSnapshot, readHeapSnapshot, takeCensus } = require("chrome://damp/content/tests/memory/memory-helpers");
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+
+ const toolbox = await openToolboxAndLog("complicated.memory", "memory");
+ await reloadPageAndLog("complicated.memory", toolbox);
+
+ let heapSnapshotFilePath = await saveHeapSnapshot("complicated");
+ let snapshot = await readHeapSnapshot("complicated", heapSnapshotFilePath);
+ await takeCensus("complicated", snapshot);
+
+ await closeToolboxAndLog("complicated.memory", toolbox);
+ await testTeardown();
+};
+
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/memory-helpers.js
@@ -0,0 +1,58 @@
+/* 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 { getToolbox, runTest } = require("chrome://damp/content/tests/head");
+
+exports.saveHeapSnapshot = async function(label) {
+ let toolbox = getToolbox();
+ let panel = toolbox.getCurrentPanel();
+ let memoryFront = panel.panelWin.gFront;
+
+ let test = runTest(label + ".saveHeapSnapshot");
+ let heapSnapshotFilePath = await memoryFront.saveHeapSnapshot();
+ test.done();
+
+ return heapSnapshotFilePath;
+};
+
+exports.readHeapSnapshot = function(label, snapshotFilePath) {
+ const ChromeUtils = require("ChromeUtils");
+ let test = runTest(label + ".readHeapSnapshot");
+ let snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
+ test.done();
+
+ return Promise.resolve(snapshot);
+};
+
+exports.takeCensus = function(label, snapshot) {
+ let test = runTest("complicated.takeCensus");
+ snapshot.takeCensus({
+ breakdown: {
+ by: "coarseType",
+ objects: {
+ by: "objectClass",
+ then: { by: "count", bytes: true, count: true },
+ other: { by: "count", bytes: true, count: true }
+ },
+ strings: {
+ by: "internalType",
+ then: { by: "count", bytes: true, count: true }
+ },
+ scripts: {
+ by: "internalType",
+ then: { by: "count", bytes: true, count: true }
+ },
+ other: {
+ by: "internalType",
+ then: { by: "count", bytes: true, count: true }
+ }
+ }
+ });
+
+ test.done();
+
+ return Promise.resolve();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/simple.js
@@ -0,0 +1,24 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+const { saveHeapSnapshot, readHeapSnapshot, takeCensus } = require("chrome://damp/content/tests/memory/memory-helpers");
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+
+ const toolbox = await openToolboxAndLog("simple.memory", "memory");
+ await reloadPageAndLog("simple.memory", toolbox);
+
+ let heapSnapshotFilePath = await saveHeapSnapshot("simple");
+ let snapshot = await readHeapSnapshot("simple", heapSnapshotFilePath);
+ await takeCensus("simple", snapshot);
+
+ await closeToolboxAndLog("simple.memory", toolbox);
+ await testTeardown();
+};
+
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/complicated.js
@@ -0,0 +1,26 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+const { exportHar, waitForNetworkRequests } = require("chrome://damp/content/tests/netmonitor/netmonitor-helpers");
+
+const EXPECTED_REQUESTS = 280;
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+ const toolbox = await openToolboxAndLog("complicated.netmonitor", "netmonitor");
+
+ const requestsDone = waitForNetworkRequests("complicated.netmonitor", toolbox,
+ EXPECTED_REQUESTS);
+ await reloadPageAndLog("complicated.netmonitor", toolbox);
+ await requestsDone;
+
+ await exportHar("complicated.netmonitor", toolbox);
+
+ await closeToolboxAndLog("complicated.netmonitor", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/netmonitor-helpers.js
@@ -0,0 +1,73 @@
+/* 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 { EVENTS } = require("devtools/client/netmonitor/src/constants");
+const { getToolbox, runTest } = require("chrome://damp/content/tests/head");
+
+/**
+ * Start monitoring all incoming update events about network requests and wait until
+ * a complete info about all requests is received. (We wait for the timings info
+ * explicitly, because that's always the last piece of information that is received.)
+ *
+ * This method is designed to wait for network requests that are issued during a page
+ * load, when retrieving page resources (scripts, styles, images). It has certain
+ * assumptions that can make it unsuitable for other types of network communication:
+ * - it waits for at least one network request to start and finish before returning
+ * - it waits only for request that were issued after it was called. Requests that are
+ * already in mid-flight will be ignored.
+ * - the request start and end times are overlapping. If a new request starts a moment
+ * after the previous one was finished, the wait will be ended in the "interim"
+ * period.
+ * @returns a promise that resolves when the wait is done.
+ */
+function waitForAllRequestsFinished(expectedRequests) {
+ let toolbox = getToolbox();
+ let window = toolbox.getCurrentPanel().panelWin;
+
+ return new Promise(resolve => {
+ // Explicitly waiting for specific number of requests arrived
+ let payloadReady = 0;
+ let timingsUpdated = 0;
+
+ function onPayloadReady(_, id) {
+ payloadReady++;
+ maybeResolve();
+ }
+
+ function onTimingsUpdated(_, id) {
+ timingsUpdated++;
+ maybeResolve();
+ }
+
+ function maybeResolve() {
+ // Have all the requests finished yet?
+ if (payloadReady >= expectedRequests && timingsUpdated >= expectedRequests) {
+ // All requests are done - unsubscribe from events and resolve!
+ window.off(EVENTS.PAYLOAD_READY, onPayloadReady);
+ window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
+ resolve();
+ }
+ }
+
+ window.on(EVENTS.PAYLOAD_READY, onPayloadReady);
+ window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
+ });
+}
+
+exports.waitForNetworkRequests = async function(label, toolbox, expectedRequests) {
+ let test = runTest(label + ".requestsFinished.DAMP");
+ await waitForAllRequestsFinished(expectedRequests);
+ test.done();
+};
+
+exports.exportHar = async function(label, toolbox) {
+ let test = runTest(label + ".exportHar");
+
+ // Export HAR from the Network panel.
+ await toolbox.getHARFromNetMonitor();
+
+ test.done();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/simple.js
@@ -0,0 +1,26 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+const { exportHar, waitForNetworkRequests } = require("chrome://damp/content/tests/netmonitor/netmonitor-helpers");
+
+const EXPECTED_REQUESTS = 1;
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+ const toolbox = await openToolboxAndLog("simple.netmonitor", "netmonitor");
+
+ const requestsDone = waitForNetworkRequests("simple.netmonitor", toolbox,
+ EXPECTED_REQUESTS);
+ await reloadPageAndLog("simple.netmonitor", toolbox);
+ await requestsDone;
+
+ await exportHar("simple.netmonitor", toolbox);
+
+ await closeToolboxAndLog("simple.netmonitor", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/performance/complicated.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+ const toolbox = await openToolboxAndLog("complicated.performance", "performance");
+ await reloadPageAndLog("complicated.performance", toolbox);
+ await closeToolboxAndLog("complicated.performance", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/performance/simple.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+ const toolbox = await openToolboxAndLog("simple.performance", "performance");
+ await reloadPageAndLog("simple.performance", toolbox);
+ await closeToolboxAndLog("simple.performance", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/complicated.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+ const toolbox = await openToolboxAndLog("complicated.styleeditor", "styleeditor");
+ await reloadPageAndLog("complicated.styleeditor", toolbox);
+ await closeToolboxAndLog("complicated.styleeditor", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/simple.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+ const toolbox = await openToolboxAndLog("simple.styleeditor", "styleeditor");
+ await reloadPageAndLog("simple.styleeditor", toolbox);
+ await closeToolboxAndLog("simple.styleeditor", toolbox);
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/toolbox/panels-in-background.js
@@ -0,0 +1,51 @@
+/* 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 { EVENTS } = require("devtools/client/netmonitor/src/constants");
+const { openToolbox, closeToolbox, reloadPageAndLog, testSetup,
+ testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ await testSetup(PAGES_BASE_URL + "custom/panels-in-background/index.html");
+
+ // Make sure the Console and Network panels are initialized
+ let toolbox = await openToolbox("webconsole");
+ let monitor = await toolbox.selectTool("netmonitor");
+
+ // Select the options panel to make both the Console and Network
+ // panel be in background.
+ // Options panel should not do anything on page reload.
+ await toolbox.selectTool("options");
+
+ // Reload the page and wait for all HTTP requests
+ // to finish (1 doc + 600 XHRs).
+ let payloadReady = waitForPayload(601, monitor.panelWin);
+ await reloadPageAndLog("panelsInBackground", toolbox);
+ await payloadReady;
+
+ await closeToolbox();
+ await testTeardown();
+};
+
+function waitForPayload(count, panelWin) {
+ return new Promise(resolve => {
+ let payloadReady = 0;
+
+ function onPayloadReady(_, id) {
+ payloadReady++;
+ maybeResolve();
+ }
+
+ function maybeResolve() {
+ if (payloadReady >= count) {
+ panelWin.off(EVENTS.PAYLOAD_READY, onPayloadReady);
+ resolve();
+ }
+ }
+
+ panelWin.on(EVENTS.PAYLOAD_READY, onPayloadReady);
+ });
+}
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/bulklog.js
@@ -0,0 +1,52 @@
+/* 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 { openToolbox, closeToolbox, getBrowserWindow, runTest, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ let TOTAL_MESSAGES = 10;
+ let tab = await testSetup(SIMPLE_URL);
+ let messageManager = tab.linkedBrowser.messageManager;
+ let toolbox = await openToolbox("webconsole");
+ let webconsole = toolbox.getPanel("webconsole");
+
+ // Resolve once the last message has been received.
+ let allMessagesReceived = new Promise(resolve => {
+ function receiveMessages(messages) {
+ for (let m of messages) {
+ if (m.node.textContent.includes("damp " + TOTAL_MESSAGES)) {
+ webconsole.hud.ui.off("new-messages", receiveMessages);
+ // Wait for the console to redraw
+ getBrowserWindow().requestAnimationFrame(resolve);
+ }
+ }
+ }
+ webconsole.hud.ui.on("new-messages", receiveMessages);
+ });
+
+ // Load a frame script using a data URI so we can do logs
+ // from the page. So this is running in content.
+ messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ addMessageListener("do-logs", function () {
+ for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
+ content.console.log('damp', i+1, content);
+ }
+ });
+ }`
+ ) + ")()", true);
+
+ // Kick off the logging
+ messageManager.sendAsyncMessage("do-logs");
+
+ let test = runTest("console.bulklog");
+ await allMessagesReceived;
+ test.done();
+
+ await closeToolbox();
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/complicated.js
@@ -0,0 +1,21 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, testSetup,
+ testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
+const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
+
+const EXPECTED_MESSAGES = 7;
+
+module.exports = async function() {
+ await testSetup(COMPLICATED_URL);
+
+ let toolbox = await openToolboxAndLog("complicated.webconsole", "webconsole");
+ await reloadConsoleAndLog("complicated", toolbox, EXPECTED_MESSAGES);
+ await closeToolboxAndLog("complicated.webconsole", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/custom.js
@@ -0,0 +1,24 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, testSetup,
+ testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
+const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
+
+module.exports = async function() {
+ // These numbers controls the number of console api calls we do in the test
+ let sync = 250, stream = 250, async = 250;
+ let params = `?sync=${sync}&stream=${stream}&async=${async}`;
+ let url = PAGES_BASE_URL + "custom/console/index.html" + params;
+
+ await testSetup(url);
+
+ let toolbox = await openToolboxAndLog("custom.webconsole", "webconsole");
+ await reloadConsoleAndLog("custom", toolbox, sync + stream + async);
+ await closeToolboxAndLog("custom.webconsole", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/objectexpand.js
@@ -0,0 +1,68 @@
+/* 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 { openToolbox, closeToolboxAndLog, getBrowserWindow, runTest, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+module.exports = async function() {
+ let tab = await testSetup(SIMPLE_URL);
+
+ let messageManager = tab.linkedBrowser.messageManager;
+ let toolbox = await openToolbox("webconsole");
+ let webconsole = toolbox.getPanel("webconsole");
+
+ // Resolve once the first message is received.
+ let onMessageReceived = new Promise(resolve => {
+ function receiveMessages(messages) {
+ for (let m of messages) {
+ resolve(m);
+ }
+ }
+ webconsole.hud.ui.once("new-messages", receiveMessages);
+ });
+
+ // Load a frame script using a data URI so we can do logs
+ // from the page.
+ messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ addMessageListener("do-dir", function () {
+ content.console.dir(Array.from({length:1000}).reduce((res, _, i)=> {
+ res["item_" + i] = i;
+ return res;
+ }, {}));
+ });
+ }`
+ ) + ")()", true);
+
+ // Kick off the logging
+ messageManager.sendAsyncMessage("do-dir");
+
+ let test = runTest("console.objectexpand");
+ await onMessageReceived;
+ const tree = webconsole.hud.ui.outputNode.querySelector(".dir.message .tree");
+
+ // The tree can be collapsed since the properties are fetched asynchronously.
+ if (tree.querySelectorAll(".node").length === 1) {
+ // If this is the case, we wait for the properties to be fetched and displayed.
+ await new Promise(resolve => {
+ const observer = new (getBrowserWindow().MutationObserver)(mutations => {
+ resolve(mutations);
+ observer.disconnect();
+ });
+ observer.observe(tree, {
+ childList: true
+ });
+ });
+ }
+
+ test.done();
+
+ await closeToolboxAndLog("console.objectexpanded", toolbox);
+
+ await testTeardown();
+};
+
+
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/openwithcache.js
@@ -0,0 +1,31 @@
+/* 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 { openToolboxAndLog, closeToolbox, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+const TOTAL_MESSAGES = 100;
+
+module.exports = async function() {
+ let tab = await testSetup(SIMPLE_URL);
+
+ // Load a frame script using a data URI so we can do logs
+ // from the page. So this is running in content.
+ tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(`
+ function () {
+ for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
+ content.console.log('damp', i+1, content);
+ }
+ }`
+ ) + ")()", true);
+
+ await openToolboxAndLog("console.openwithcache", "webconsole");
+ await closeToolbox();
+
+ await testTeardown();
+};
+
+
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/simple.js
@@ -0,0 +1,21 @@
+/* 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 { openToolboxAndLog, closeToolboxAndLog, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
+
+const EXPECTED_MESSAGES = 1;
+
+module.exports = async function() {
+ await testSetup(SIMPLE_URL);
+
+ let toolbox = await openToolboxAndLog("simple.webconsole", "webconsole");
+ await reloadConsoleAndLog("simple", toolbox, EXPECTED_MESSAGES);
+ await closeToolboxAndLog("simple.webconsole", toolbox);
+
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/streamlog.js
@@ -0,0 +1,55 @@
+/* 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 { openToolbox, closeToolbox, logTestResult, testSetup,
+ testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
+
+// Log a stream of console messages, 1 per rAF. Then record the average
+// time per rAF. The idea is that the console being slow can slow down
+// content (i.e. Bug 1237368).
+module.exports = async function() {
+ let TOTAL_MESSAGES = 100;
+ let tab = await testSetup(SIMPLE_URL);
+ let messageManager = tab.linkedBrowser.messageManager;
+
+ await openToolbox("webconsole");
+
+ // Load a frame script using a data URI so we can do logs
+ // from the page. So this is running in content.
+ messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+ `function () {
+ let count = 0;
+ let startTime = content.performance.now();
+ function log() {
+ if (++count < ${TOTAL_MESSAGES}) {
+ content.document.querySelector("h1").textContent += count + "\\n";
+ content.console.log('damp', count,
+ content,
+ content.document,
+ content.document.body,
+ content.document.documentElement,
+ new Array(100).join(" DAMP? DAMP! "));
+ content.requestAnimationFrame(log);
+ } else {
+ let avgTime = (content.performance.now() - startTime) / ${TOTAL_MESSAGES};
+ sendSyncMessage("done", Math.round(avgTime));
+ }
+ }
+ log();
+ }`
+ ) + ")()", true);
+
+ let avgTime = await new Promise(resolve => {
+ messageManager.addMessageListener("done", (e) => {
+ resolve(e.data);
+ });
+ });
+
+ logTestResult("console.streamlog", avgTime);
+
+ await closeToolbox();
+ await testTeardown();
+};
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/webconsole-helpers.js
@@ -0,0 +1,24 @@
+/* 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 { reloadPageAndLog } = require("chrome://damp/content/tests/head");
+
+exports.reloadConsoleAndLog = async function(label, toolbox, expectedMessages) {
+ let onReload = async function() {
+ let webconsole = toolbox.getPanel("webconsole");
+ await new Promise(done => {
+ let messages = 0;
+ let receiveMessages = () => {
+ if (++messages == expectedMessages) {
+ webconsole.hud.ui.off("new-messages", receiveMessages);
+ done();
+ }
+ };
+ webconsole.hud.ui.on("new-messages", receiveMessages);
+ });
+ };
+ await reloadPageAndLog(label + ".webconsole", toolbox, onReload);
+};