import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,155 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { Constants } = require("devtools/client/performance/modules/constants");
const { once, times } = require("devtools/client/performance/test/helpers/event-utils");
const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
/**
* Starts a manual recording in the given performance tool panel and
* waits for it to finish starting.
*/
exports.startRecording = function (panel, options = {}) {
let controller = panel.panelWin.PerformanceController;
return Promise.all([
controller.startRecording(),
exports.waitForRecordingStartedEvents(panel, options)
]);
};
/**
* Stops the latest recording in the given performance tool panel and
* waits for it to finish stopping.
*/
exports.stopRecording = function (panel, options = {}) {
let controller = panel.panelWin.PerformanceController;
return Promise.all([
controller.stopRecording(),
exports.waitForRecordingStoppedEvents(panel, options)
]);
};
/**
* Waits for all the necessary events to be emitted after a recording starts.
*/
exports.waitForRecordingStartedEvents = function (panel, options = {}) {
options.expectedViewState = options.expectedViewState || /^(console-)?recording$/;
let EVENTS = panel.panelWin.EVENTS;
let controller = panel.panelWin.PerformanceController;
let view = panel.panelWin.PerformanceView;
let overview = panel.panelWin.OverviewView;
return Promise.all([
options.skipWaitingForBackendReady
? null
: once(controller, EVENTS.BACKEND_READY_AFTER_RECORDING_START),
options.skipWaitingForRecordingStarted
? null
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
expectedArgs: { "1": "recording-started" }
}),
options.skipWaitingForViewState
? null
: once(view, EVENTS.UI_STATE_CHANGED, {
expectedArgs: { "1": options.expectedViewState }
}),
options.skipWaitingForOverview
? null
: once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
}),
]);
};
/**
* Waits for all the necessary events to be emitted after a recording finishes.
*/
exports.waitForRecordingStoppedEvents = function (panel, options = {}) {
options.expectedViewClass = options.expectedViewClass || "WaterfallView";
options.expectedViewEvent = options.expectedViewEvent || "UI_WATERFALL_RENDERED";
options.expectedViewState = options.expectedViewState || "recorded";
let EVENTS = panel.panelWin.EVENTS;
let controller = panel.panelWin.PerformanceController;
let view = panel.panelWin.PerformanceView;
let overview = panel.panelWin.OverviewView;
let subview = panel.panelWin[options.expectedViewClass];
return Promise.all([
options.skipWaitingForBackendReady
? null
: once(controller, EVENTS.BACKEND_READY_AFTER_RECORDING_STOP),
options.skipWaitingForRecordingStop
? null
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
expectedArgs: { "1": "recording-stopping" }
}),
options.skipWaitingForRecordingStop
? null
: once(controller, EVENTS.RECORDING_STATE_CHANGE, {
expectedArgs: { "1": "recording-stopped" }
}),
options.skipWaitingForViewState
? null
: once(view, EVENTS.UI_STATE_CHANGED, {
expectedArgs: { "1": options.expectedViewState }
}),
options.skipWaitingForOverview
? null
: once(overview, EVENTS.UI_OVERVIEW_RENDERED, {
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_HIGH_RES_INTERVAL }
}),
options.skipWaitingForSubview
? null
: once(subview, EVENTS[options.expectedViewEvent]),
]);
};
/**
* Waits for rendering to happen once on all the performance tool's widgets.
*/
exports.waitForAllWidgetsRendered = (panel) => {
let { panelWin } = panel;
let { EVENTS } = panelWin;
return Promise.all([
once(panelWin.OverviewView, EVENTS.UI_MARKERS_GRAPH_RENDERED),
once(panelWin.OverviewView, EVENTS.UI_MEMORY_GRAPH_RENDERED),
once(panelWin.OverviewView, EVENTS.UI_FRAMERATE_GRAPH_RENDERED),
once(panelWin.OverviewView, EVENTS.UI_OVERVIEW_RENDERED),
once(panelWin.WaterfallView, EVENTS.UI_WATERFALL_RENDERED),
once(panelWin.JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED),
once(panelWin.JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED),
once(panelWin.MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED),
once(panelWin.MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED)
]);
};
/**
* Waits for rendering to happen on the performance tool's overview graph,
* making sure some markers were also rendered.
*/
exports.waitForOverviewRenderedWithMarkers = (panel, minTimes = 3, minMarkers = 1) => {
let { EVENTS, OverviewView, PerformanceController } = panel.panelWin;
return Promise.all([
times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, minTimes, {
expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
}),
waitUntil(() =>
PerformanceController.getCurrentRecording().getMarkers().length >= minMarkers
),
]);
};
/**
* Reloads the given tab target.
*/
exports.reload = (target) => {
target.activeTab.reload();
return once(target, "navigate");
};

View file

@ -0,0 +1,30 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Services = require("Services");
const { waitForMozAfterPaint } = require("devtools/client/performance/test/helpers/wait-utils");
/**
* Checks if a DOM node is considered visible.
*/
exports.isVisible = (element) => {
return !element.classList.contains("hidden") && !element.hidden;
};
/**
* Appends the provided element to the provided parent node. If run in e10s
* mode, will also wait for MozAfterPaint to make sure the tab is rendered.
* Should be reviewed if Bug 1240509 lands.
*/
exports.appendAndWaitForPaint = function (parent, element) {
let isE10s = Services.appinfo.browserTabsRemoteAutostart;
if (isE10s) {
let win = parent.ownerDocument.defaultView;
let onMozAfterPaint = waitForMozAfterPaint(win);
parent.appendChild(element);
return onMozAfterPaint;
}
parent.appendChild(element);
return null;
};

View file

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* globals dump */
const Services = require("Services");
const KNOWN_EE_APIS = [
["on", "off"],
["addEventListener", "removeEventListener"],
["addListener", "removeListener"]
];
/**
* Listens for any event for a single time on a target, no matter what kind of
* event emitter it is, returning a promise resolved with the passed arguments
* once the event is fired.
*/
exports.once = function (target, eventName, options = {}) {
return exports.times(target, eventName, 1, options);
};
/**
* Waits for any event to be fired a specified amount of times on a target, no
* matter what kind of event emitter.
* Possible options: `useCapture`, `spreadArgs`, `expectedArgs`
*/
exports.times = function (target, eventName, receiveCount, options = {}) {
let msg = `Waiting for event: '${eventName}' on ${target} for ${receiveCount} time(s)`;
if ("expectedArgs" in options) {
dump(`${msg} with arguments: ${JSON.stringify(options.expectedArgs)}.\n`);
} else {
dump(`${msg}.\n`);
}
return new Promise((resolve, reject) => {
if (typeof eventName != "string") {
reject(new Error(`Unexpected event name: ${eventName}.`));
}
let API = KNOWN_EE_APIS.find(([a, r]) => (a in target) && (r in target));
if (!API) {
reject(new Error("Target is not a supported event listener."));
return;
}
let [add, remove] = API;
target[add](eventName, function onEvent(...args) {
if ("expectedArgs" in options) {
for (let index of Object.keys(options.expectedArgs)) {
if (
// Expected argument matches this regexp.
(options.expectedArgs[index] instanceof RegExp &&
!options.expectedArgs[index].exec(args[index])) ||
// Expected argument is not a regexp and equal to the received arg.
(!(options.expectedArgs[index] instanceof RegExp) &&
options.expectedArgs[index] != args[index])
) {
dump(`Ignoring event '${eventName}' with unexpected argument at index ` +
`${index}: ${args[index]}\n`);
return;
}
}
}
if (--receiveCount > 0) {
dump(`Event: '${eventName}' on ${target} needs to be fired ${receiveCount} ` +
`more time(s).\n`);
} else if (!receiveCount) {
dump(`Event: '${eventName}' on ${target} received.\n`);
target[remove](eventName, onEvent, options.useCapture);
resolve(options.spreadArgs ? args : args[0]);
}
}, options.useCapture);
});
};
/**
* Like `once`, but for observer notifications.
*/
exports.observeOnce = function (notificationName, options = {}) {
return exports.observeTimes(notificationName, 1, options);
};
/**
* Like `times`, but for observer notifications.
* Possible options: `expectedSubject`
*/
exports.observeTimes = function (notificationName, receiveCount, options = {}) {
dump(`Waiting for notification: '${notificationName}' for ${receiveCount} time(s).\n`);
return new Promise((resolve, reject) => {
if (typeof notificationName != "string") {
reject(new Error(`Unexpected notification name: ${notificationName}.`));
}
Services.obs.addObserver(function onObserve(subject, topic, data) {
if ("expectedSubject" in options && options.expectedSubject != subject) {
dump(`Ignoring notification '${notificationName}' with unexpected subject: ` +
`${subject}\n`);
return;
}
if (--receiveCount > 0) {
dump(`Notification: '${notificationName}' needs to be fired ${receiveCount} ` +
`more time(s).\n`);
} else if (!receiveCount) {
dump(`Notification: '${notificationName}' received.\n`);
Services.obs.removeObserver(onObserve, topic);
resolve(data);
}
}, notificationName, false);
});
};

View file

@ -0,0 +1,75 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
exports.HORIZONTAL_AXIS = 1;
exports.VERTICAL_AXIS = 2;
/**
* Simulates a command event on an element.
*/
exports.command = (node) => {
let ev = node.ownerDocument.createEvent("XULCommandEvent");
ev.initCommandEvent("command", true, true, node.ownerDocument.defaultView, 0, false,
false, false, false, null);
node.dispatchEvent(ev);
};
/**
* Simulates a click event on a devtools canvas graph.
*/
exports.clickCanvasGraph = (graph, { x, y }) => {
x = x || 0;
y = y || 0;
x /= graph._window.devicePixelRatio;
y /= graph._window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
};
/**
* Simulates a drag start event on a devtools canvas graph.
*/
exports.dragStartCanvasGraph = (graph, { x, y }) => {
x = x || 0;
y = y || 0;
x /= graph._window.devicePixelRatio;
y /= graph._window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseDown({ testX: x, testY: y });
};
/**
* Simulates a drag stop event on a devtools canvas graph.
*/
exports.dragStopCanvasGraph = (graph, { x, y }) => {
x = x || 0;
y = y || 0;
x /= graph._window.devicePixelRatio;
y /= graph._window.devicePixelRatio;
graph._onMouseMove({ testX: x, testY: y });
graph._onMouseUp({ testX: x, testY: y });
};
/**
* Simulates a scroll event on a devtools canvas graph.
*/
exports.scrollCanvasGraph = (graph, { axis, wheel, x, y }) => {
x = x || 1;
y = y || 1;
x /= graph._window.devicePixelRatio;
y /= graph._window.devicePixelRatio;
graph._onMouseMove({
testX: x,
testY: y
});
graph._onMouseWheel({
testX: x,
testY: y,
axis: axis,
detail: wheel,
HORIZONTAL_AXIS: exports.HORIZONTAL_AXIS,
VERTICAL_AXIS: exports.VERTICAL_AXIS
});
};

View file

@ -0,0 +1,20 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'actions.js',
'dom-utils.js',
'event-utils.js',
'input-utils.js',
'panel-utils.js',
'prefs.js',
'profiler-mm-utils.js',
'recording-utils.js',
'synth-utils.js',
'tab-utils.js',
'urls.js',
'wait-utils.js',
)

View file

@ -0,0 +1,106 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* globals dump */
const { gDevTools } = require("devtools/client/framework/devtools");
const { TargetFactory } = require("devtools/client/framework/target");
const { addTab, removeTab } = require("devtools/client/performance/test/helpers/tab-utils");
const { once } = require("devtools/client/performance/test/helpers/event-utils");
/**
* Initializes a toolbox panel in a new tab.
*/
exports.initPanelInNewTab = function* ({ tool, url, win }, options = {}) {
let tab = yield addTab({ url, win }, options);
return (yield exports.initPanelInTab({ tool, tab }));
};
/**
* Initializes a toolbox panel in the specified tab.
*/
exports.initPanelInTab = function* ({ tool, tab }) {
dump(`Initializing a ${tool} panel.\n`);
let target = TargetFactory.forTab(tab);
yield target.makeRemote();
// Open a toolbox and wait for the connection to the performance actors
// to be opened. This is necessary because of the WebConsole's
// `profile` and `profileEnd` methods.
let toolbox = yield gDevTools.showToolbox(target, tool);
yield toolbox.initPerformance();
let panel = toolbox.getCurrentPanel();
return { target, toolbox, panel };
};
/**
* Initializes a performance panel in a new tab.
*/
exports.initPerformanceInNewTab = function* ({ url, win }, options = {}) {
let tab = yield addTab({ url, win }, options);
return (yield exports.initPerformanceInTab({ tab }));
};
/**
* Initializes a performance panel in the specified tab.
*/
exports.initPerformanceInTab = function* ({ tab }) {
return (yield exports.initPanelInTab({
tool: "performance",
tab: tab
}));
};
/**
* Initializes a webconsole panel in a new tab.
* Returns a console property that allows calls to `profile` and `profileEnd`.
*/
exports.initConsoleInNewTab = function* ({ url, win }, options = {}) {
let tab = yield addTab({ url, win }, options);
return (yield exports.initConsoleInTab({ tab }));
};
/**
* Initializes a webconsole panel in the specified tab.
* Returns a console property that allows calls to `profile` and `profileEnd`.
*/
exports.initConsoleInTab = function* ({ tab }) {
let { target, toolbox, panel } = yield exports.initPanelInTab({
tool: "webconsole",
tab: tab
});
let consoleMethod = function* (method, label, event) {
let recordingEventReceived = once(toolbox.performance, event);
if (label === undefined) {
yield panel.hud.jsterm.execute(`console.${method}()`);
} else {
yield panel.hud.jsterm.execute(`console.${method}("${label}")`);
}
yield recordingEventReceived;
};
let profile = function* (label) {
return yield consoleMethod("profile", label, "recording-started");
};
let profileEnd = function* (label) {
return yield consoleMethod("profileEnd", label, "recording-stopped");
};
return { target, toolbox, panel, console: { profile, profileEnd } };
};
/**
* Tears down a toolbox panel and removes an associated tab.
*/
exports.teardownToolboxAndRemoveTab = function* (panel, options) {
dump("Destroying panel.\n");
let tab = panel.target.tab;
yield panel.toolbox.destroy();
yield removeTab(tab, options);
};

View file

@ -0,0 +1,72 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Services = require("Services");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
// Prefs to revert to default once tests finish. Keep these in sync with
// all the preferences defined in devtools/client/preferences/devtools.js.
exports.MEMORY_SAMPLE_PROB_PREF = "devtools.performance.memory.sample-probability";
exports.MEMORY_MAX_LOG_LEN_PREF = "devtools.performance.memory.max-log-length";
exports.PROFILER_BUFFER_SIZE_PREF = "devtools.performance.profiler.buffer-size";
exports.PROFILER_SAMPLE_RATE_PREF = "devtools.performance.profiler.sample-frequency-khz";
exports.UI_EXPERIMENTAL_PREF = "devtools.performance.ui.experimental";
exports.UI_INVERT_CALL_TREE_PREF = "devtools.performance.ui.invert-call-tree";
exports.UI_INVERT_FLAME_PREF = "devtools.performance.ui.invert-flame-graph";
exports.UI_FLATTEN_RECURSION_PREF = "devtools.performance.ui.flatten-tree-recursion";
exports.UI_SHOW_PLATFORM_DATA_PREF = "devtools.performance.ui.show-platform-data";
exports.UI_SHOW_IDLE_BLOCKS_PREF = "devtools.performance.ui.show-idle-blocks";
exports.UI_ENABLE_FRAMERATE_PREF = "devtools.performance.ui.enable-framerate";
exports.UI_ENABLE_MEMORY_PREF = "devtools.performance.ui.enable-memory";
exports.UI_ENABLE_ALLOCATIONS_PREF = "devtools.performance.ui.enable-allocations";
exports.UI_ENABLE_MEMORY_FLAME_CHART = "devtools.performance.ui.enable-memory-flame";
exports.DEFAULT_PREF_VALUES = [
"devtools.debugger.log",
"devtools.performance.enabled",
"devtools.performance.timeline.hidden-markers",
exports.MEMORY_SAMPLE_PROB_PREF,
exports.MEMORY_MAX_LOG_LEN_PREF,
exports.PROFILER_BUFFER_SIZE_PREF,
exports.PROFILER_SAMPLE_RATE_PREF,
exports.UI_EXPERIMENTAL_PREF,
exports.UI_INVERT_CALL_TREE_PREF,
exports.UI_INVERT_FLAME_PREF,
exports.UI_FLATTEN_RECURSION_PREF,
exports.UI_SHOW_PLATFORM_DATA_PREF,
exports.UI_SHOW_IDLE_BLOCKS_PREF,
exports.UI_ENABLE_FRAMERATE_PREF,
exports.UI_ENABLE_MEMORY_PREF,
exports.UI_ENABLE_ALLOCATIONS_PREF,
exports.UI_ENABLE_MEMORY_FLAME_CHART,
"devtools.performance.ui.show-jit-optimizations",
"devtools.performance.ui.show-triggers-for-gc-types",
].reduce((prefValues, prefName) => {
prefValues[prefName] = Preferences.get(prefName);
return prefValues;
}, {});
/**
* Invokes callback when a pref which is not in the `DEFAULT_PREF_VALUES` store
* is changed. Returns a cleanup function.
*/
exports.whenUnknownPrefChanged = function (branch, callback) {
function onObserve(subject, topic, data) {
if (!(data in exports.DEFAULT_PREF_VALUES)) {
callback(data);
}
}
Services.prefs.addObserver(branch, onObserve, false);
return () => Services.prefs.removeObserver(branch, onObserve);
};
/**
* Reverts all known preferences to their default values.
*/
exports.rollbackPrefsToDefault = function () {
for (let prefName of Object.keys(exports.DEFAULT_PREF_VALUES)) {
Preferences.set(prefName, exports.DEFAULT_PREF_VALUES[prefName]);
}
};

View file

@ -0,0 +1,117 @@
/* 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";
/**
* The following functions are used in testing to control and inspect
* the nsIProfiler in child process content. These should be called from
* the parent process.
*/
const { Cc, Ci } = require("chrome");
const { Task } = require("devtools/shared/task");
const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
let gMM = null;
/**
* Loads the relevant frame scripts into the provided browser's message manager.
*/
exports.pmmLoadFrameScripts = (gBrowser) => {
gMM = gBrowser.selectedBrowser.messageManager;
gMM.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
};
/**
* Clears the cached message manager.
*/
exports.pmmClearFrameScripts = () => {
gMM = null;
};
/**
* Sends a message to the message listener, attaching an id to the payload data.
* Resolves a returned promise when the response is received from the message
* listener, with the same id as part of the response payload data.
*/
exports.pmmUniqueMessage = function (message, payload) {
if (!gMM) {
throw new Error("`pmmLoadFrameScripts()` must be called when using MessageManager.");
}
let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
payload.id = generateUUID().toString();
return new Promise(resolve => {
gMM.addMessageListener(message + ":response", function onHandler({ data }) {
if (payload.id == data.id) {
gMM.removeMessageListener(message + ":response", onHandler);
resolve(data.data);
}
});
gMM.sendAsyncMessage(message, payload);
});
};
/**
* Checks if the nsProfiler module is active.
*/
exports.pmmIsProfilerActive = () => {
return exports.pmmSendProfilerCommand("IsActive");
};
/**
* Starts the nsProfiler module.
*/
exports.pmmStartProfiler = Task.async(function* ({ entries, interval, features }) {
let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
if (!isActive) {
return exports.pmmSendProfilerCommand("StartProfiler", [entries, interval, features,
features.length]);
}
return null;
});
/**
* Stops the nsProfiler module.
*/
exports.pmmStopProfiler = Task.async(function* () {
let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
if (isActive) {
return exports.pmmSendProfilerCommand("StopProfiler");
}
return null;
});
/**
* Calls a method on the nsProfiler module.
*/
exports.pmmSendProfilerCommand = (method, args = []) => {
return exports.pmmUniqueMessage("devtools:test:profiler", { method, args });
};
/**
* Evaluates a script in content, returning a promise resolved with the
* returned result.
*/
exports.pmmEvalInDebuggee = (script) => {
return exports.pmmUniqueMessage("devtools:test:eval", { script });
};
/**
* Evaluates a console method in content.
*/
exports.pmmConsoleMethod = function (method, ...args) {
// Terrible ugly hack -- this gets stringified when it uses the
// message manager, so an undefined arg in `console.profileEnd()`
// turns into a stringified "null", which is terrible. This method
// is only used for test helpers, so swap out the argument if its undefined
// with an empty string. Differences between empty string and undefined are
// tested on the front itself.
if (args[0] == null) {
args[0] = "";
}
return exports.pmmUniqueMessage("devtools:test:console", { method, args });
};

View file

@ -0,0 +1,54 @@
/* 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";
/**
* These utilities provide a functional interface for accessing the particulars
* about the recording's details.
*/
/**
* Access the selected view from the panel's recording list.
*
* @param {object} panel - The current panel.
* @return {object} The recording model.
*/
exports.getSelectedRecording = function (panel) {
const view = panel.panelWin.RecordingsView;
return view.selected;
};
/**
* Set the selected index of the recording via the panel.
*
* @param {object} panel - The current panel.
* @return {number} index
*/
exports.setSelectedRecording = function (panel, index) {
const view = panel.panelWin.RecordingsView;
view.setSelectedByIndex(index);
return index;
};
/**
* Access the selected view from the panel's recording list.
*
* @param {object} panel - The current panel.
* @return {number} index
*/
exports.getSelectedRecordingIndex = function (panel) {
const view = panel.panelWin.RecordingsView;
return view.getSelectedIndex();
};
exports.getDurationLabelText = function (panel, elementIndex) {
const { $$ } = panel.panelWin;
const elements = $$(".recording-list-item-duration", panel.panelWin.document);
return elements[elementIndex].innerHTML;
};
exports.getRecordingsCount = function (panel) {
const { $$ } = panel.panelWin;
return $$(".recording-list-item", panel.panelWin.document).length;
};

View file

@ -0,0 +1,99 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Generates a generalized profile with some samples.
*/
exports.synthesizeProfile = () => {
const { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
const RecordingUtils = require("devtools/shared/performance/recording-utils");
return RecordingUtils.deflateProfile({
meta: { version: 2 },
threads: [{
samples: [{
time: 1,
frames: [
{ category: CATEGORY_MASK("other"), location: "(root)" },
{ category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
{ category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
{ category: CATEGORY_MASK("js"), location: "C (http://foo/bar/baz:56)" }
]
}, {
time: 1 + 1,
frames: [
{ category: CATEGORY_MASK("other"), location: "(root)" },
{ category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
{ category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
{ category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
]
}, {
time: 1 + 1 + 2,
frames: [
{ category: CATEGORY_MASK("other"), location: "(root)" },
{ category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
{ category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
{ category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
]
}, {
time: 1 + 1 + 2 + 3,
frames: [
{ category: CATEGORY_MASK("other"), location: "(root)" },
{ category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
{ category: CATEGORY_MASK("gc", 2), location: "E (http://foo/bar/baz:90)" },
{ category: CATEGORY_MASK("network"), location: "F (http://foo/bar/baz:99)" }
]
}]
}]
});
};
/**
* Generates a simple implementation for a tree class.
*/
exports.synthesizeCustomTreeClass = () => {
const { Cu } = require("chrome");
const { AbstractTreeItem } = Cu.import("resource://devtools/client/shared/widgets/AbstractTreeItem.jsm", {});
const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
function MyCustomTreeItem(dataSrc, properties) {
AbstractTreeItem.call(this, properties);
this.itemDataSrc = dataSrc;
}
MyCustomTreeItem.prototype = Heritage.extend(AbstractTreeItem.prototype, {
_displaySelf: function (document, arrowNode) {
let node = document.createElement("hbox");
node.style.marginInlineStart = (this.level * 10) + "px";
node.appendChild(arrowNode);
node.appendChild(document.createTextNode(this.itemDataSrc.label));
return node;
},
_populateSelf: function (children) {
for (let childDataSrc of this.itemDataSrc.children) {
children.push(new MyCustomTreeItem(childDataSrc, {
parent: this,
level: this.level + 1
}));
}
}
});
const myDataSrc = {
label: "root",
children: [{
label: "foo",
children: []
}, {
label: "bar",
children: [{
label: "baz",
children: []
}]
}]
};
return { MyCustomTreeItem, myDataSrc };
};

View file

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* globals dump */
const Services = require("Services");
const tabs = require("sdk/tabs");
const tabUtils = require("sdk/tabs/utils");
const { viewFor } = require("sdk/view/core");
const { waitForDelayedStartupFinished } = require("devtools/client/performance/test/helpers/wait-utils");
const { gDevTools } = require("devtools/client/framework/devtools");
/**
* Gets a random integer in between an interval. Used to uniquely identify
* added tabs by augmenting the URL.
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Adds a browser tab with the given url in the specified window and waits
* for it to load.
*/
exports.addTab = function ({ url, win }, options = {}) {
let id = getRandomInt(0, Number.MAX_SAFE_INTEGER - 1);
url += `#${id}`;
dump(`Adding tab with url: ${url}.\n`);
return new Promise(resolve => {
let tab;
tabs.on("ready", function onOpen(model) {
if (tab != viewFor(model)) {
return;
}
dump(`Tab added and finished loading: ${model.url}.\n`);
tabs.off("ready", onOpen);
resolve(tab);
});
win.focus();
tab = tabUtils.openTab(win, url);
if (options.dontWaitForTabReady) {
resolve(tab);
}
});
};
/**
* Removes a browser tab from the specified window and waits for it to close.
*/
exports.removeTab = function (tab, options = {}) {
dump(`Removing tab: ${tabUtils.getURI(tab)}.\n`);
return new Promise(resolve => {
tabs.on("close", function onClose(model) {
if (tab != viewFor(model)) {
return;
}
dump(`Tab removed and finished closing: ${model.url}.\n`);
tabs.off("close", onClose);
resolve(tab);
});
tabUtils.closeTab(tab);
if (options.dontWaitForTabClose) {
resolve(tab);
}
});
};
/**
* Adds a browser window with the provided options.
*/
exports.addWindow = function* (options) {
let { OpenBrowserWindow } = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
let win = OpenBrowserWindow(options);
yield waitForDelayedStartupFinished(win);
return win;
};

View file

@ -0,0 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
exports.EXAMPLE_URL = "http://example.com/browser/devtools/client/performance/test";
exports.SIMPLE_URL = `${exports.EXAMPLE_URL}/doc_simple-test.html`;

View file

@ -0,0 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* globals dump */
const { CC } = require("chrome");
const { Task } = require("devtools/shared/task");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { once, observeOnce } = require("devtools/client/performance/test/helpers/event-utils");
/**
* Blocks the main thread for the specified amount of time.
*/
exports.busyWait = function (time) {
dump(`Busy waiting for: ${time} milliseconds.\n`);
let start = Date.now();
/* eslint-disable no-unused-vars */
let stack;
while (Date.now() - start < time) {
stack = CC.stack;
}
/* eslint-enable no-unused-vars */
};
/**
* Idly waits for the specified amount of time.
*/
exports.idleWait = function (time) {
dump(`Idly waiting for: ${time} milliseconds.\n`);
return DevToolsUtils.waitForTime(time);
};
/**
* Waits until a predicate returns true.
*/
exports.waitUntil = function* (predicate, interval = 100, tries = 100) {
for (let i = 1; i <= tries; i++) {
if (yield Task.spawn(predicate)) {
dump(`Predicate returned true after ${i} tries.\n`);
return;
}
yield exports.idleWait(interval);
}
throw new Error(`Predicate returned false after ${tries} tries, aborting.\n`);
};
/**
* Waits for a `MozAfterPaint` event to be fired on the specified window.
*/
exports.waitForMozAfterPaint = function (window) {
return once(window, "MozAfterPaint");
};
/**
* Waits for the `browser-delayed-startup-finished` observer notification
* to be fired on the specified window.
*/
exports.waitForDelayedStartupFinished = function (window) {
return observeOnce("browser-delayed-startup-finished", { expectedSubject: window });
};