mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
155
devtools/client/performance/test/helpers/actions.js
Normal file
155
devtools/client/performance/test/helpers/actions.js
Normal 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");
|
||||
};
|
||||
30
devtools/client/performance/test/helpers/dom-utils.js
Normal file
30
devtools/client/performance/test/helpers/dom-utils.js
Normal 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;
|
||||
};
|
||||
114
devtools/client/performance/test/helpers/event-utils.js
Normal file
114
devtools/client/performance/test/helpers/event-utils.js
Normal 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);
|
||||
});
|
||||
};
|
||||
75
devtools/client/performance/test/helpers/input-utils.js
Normal file
75
devtools/client/performance/test/helpers/input-utils.js
Normal 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
|
||||
});
|
||||
};
|
||||
20
devtools/client/performance/test/helpers/moz.build
Normal file
20
devtools/client/performance/test/helpers/moz.build
Normal 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',
|
||||
)
|
||||
106
devtools/client/performance/test/helpers/panel-utils.js
Normal file
106
devtools/client/performance/test/helpers/panel-utils.js
Normal 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);
|
||||
};
|
||||
72
devtools/client/performance/test/helpers/prefs.js
Normal file
72
devtools/client/performance/test/helpers/prefs.js
Normal 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]);
|
||||
}
|
||||
};
|
||||
117
devtools/client/performance/test/helpers/profiler-mm-utils.js
Normal file
117
devtools/client/performance/test/helpers/profiler-mm-utils.js
Normal 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 });
|
||||
};
|
||||
54
devtools/client/performance/test/helpers/recording-utils.js
Normal file
54
devtools/client/performance/test/helpers/recording-utils.js
Normal 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;
|
||||
};
|
||||
99
devtools/client/performance/test/helpers/synth-utils.js
Normal file
99
devtools/client/performance/test/helpers/synth-utils.js
Normal 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 };
|
||||
};
|
||||
85
devtools/client/performance/test/helpers/tab-utils.js
Normal file
85
devtools/client/performance/test/helpers/tab-utils.js
Normal 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;
|
||||
};
|
||||
6
devtools/client/performance/test/helpers/urls.js
Normal file
6
devtools/client/performance/test/helpers/urls.js
Normal 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`;
|
||||
61
devtools/client/performance/test/helpers/wait-utils.js
Normal file
61
devtools/client/performance/test/helpers/wait-utils.js
Normal 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 });
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue