mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset e3a037fb152f (bug 1050386) for frequent WinXP mochitest-dt failures.
--HG-- rename : browser/devtools/shared/test/browser_graphs-07a.js => browser/devtools/shared/test/browser_graphs-07.js
This commit is contained in:
parent
cbf2ffcd5c
commit
39f8fdf1d2
@ -1372,9 +1372,8 @@ pref("devtools.debugger.ui.variables-sorting-enabled", true);
|
||||
pref("devtools.debugger.ui.variables-only-enum-visible", false);
|
||||
pref("devtools.debugger.ui.variables-searchbox-visible", false);
|
||||
|
||||
// Enable the Profiler and the Timeline
|
||||
// Enable the Profiler
|
||||
pref("devtools.profiler.enabled", true);
|
||||
pref("devtools.timeline.enabled", false);
|
||||
|
||||
// The default Profiler UI settings
|
||||
pref("devtools.profiler.ui.show-platform-data", false);
|
||||
|
@ -119,5 +119,3 @@ browser.jar:
|
||||
content/browser/devtools/eyedropper.xul (eyedropper/eyedropper.xul)
|
||||
content/browser/devtools/eyedropper/crosshairs.css (eyedropper/crosshairs.css)
|
||||
content/browser/devtools/eyedropper/nocursor.css (eyedropper/nocursor.css)
|
||||
content/browser/devtools/timeline/timeline.xul (timeline/timeline.xul)
|
||||
content/browser/devtools/timeline/timeline.js (timeline/timeline.js)
|
||||
|
@ -31,28 +31,25 @@ loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/shaderedito
|
||||
loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/canvasdebugger/panel").CanvasDebuggerPanel);
|
||||
loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/webaudioeditor/panel").WebAudioEditorPanel);
|
||||
loader.lazyGetter(this, "ProfilerPanel", () => require("devtools/profiler/panel").ProfilerPanel);
|
||||
loader.lazyGetter(this, "TimelinePanel", () => require("devtools/timeline/panel").TimelinePanel);
|
||||
loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/netmonitor/panel").NetMonitorPanel);
|
||||
loader.lazyGetter(this, "StoragePanel", () => require("devtools/storage/panel").StoragePanel);
|
||||
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/scratchpad/scratchpad-panel").ScratchpadPanel);
|
||||
loader.lazyGetter(this, "StoragePanel", () => require("devtools/storage/panel").StoragePanel);
|
||||
|
||||
// Strings
|
||||
const toolboxProps = "chrome://browser/locale/devtools/toolbox.properties";
|
||||
const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
|
||||
const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
const debuggerProps = "chrome://browser/locale/devtools/debugger.properties";
|
||||
const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties";
|
||||
const shaderEditorProps = "chrome://browser/locale/devtools/shadereditor.properties";
|
||||
const canvasDebuggerProps = "chrome://browser/locale/devtools/canvasdebugger.properties";
|
||||
const webAudioEditorProps = "chrome://browser/locale/devtools/webaudioeditor.properties";
|
||||
const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";
|
||||
const profilerProps = "chrome://browser/locale/devtools/profiler.properties";
|
||||
const timelineProps = "chrome://browser/locale/devtools/timeline.properties";
|
||||
const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties";
|
||||
const storageProps = "chrome://browser/locale/devtools/storage.properties";
|
||||
const scratchpadProps = "chrome://browser/locale/devtools/scratchpad.properties";
|
||||
const storageProps = "chrome://browser/locale/devtools/storage.properties";
|
||||
|
||||
loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(toolboxProps));
|
||||
loader.lazyGetter(this, "profilerStrings",() => Services.strings.createBundle(profilerProps));
|
||||
loader.lazyGetter(this, "webConsoleStrings", () => Services.strings.createBundle(webConsoleProps));
|
||||
loader.lazyGetter(this, "debuggerStrings", () => Services.strings.createBundle(debuggerProps));
|
||||
loader.lazyGetter(this, "styleEditorStrings", () => Services.strings.createBundle(styleEditorProps));
|
||||
@ -60,10 +57,10 @@ loader.lazyGetter(this, "shaderEditorStrings", () => Services.strings.createBund
|
||||
loader.lazyGetter(this, "canvasDebuggerStrings", () => Services.strings.createBundle(canvasDebuggerProps));
|
||||
loader.lazyGetter(this, "webAudioEditorStrings", () => Services.strings.createBundle(webAudioEditorProps));
|
||||
loader.lazyGetter(this, "inspectorStrings", () => Services.strings.createBundle(inspectorProps));
|
||||
loader.lazyGetter(this, "timelineStrings", () => Services.strings.createBundle(timelineProps));
|
||||
loader.lazyGetter(this, "profilerStrings",() => Services.strings.createBundle(profilerProps));
|
||||
loader.lazyGetter(this, "netMonitorStrings", () => Services.strings.createBundle(netMonitorProps));
|
||||
loader.lazyGetter(this, "storageStrings", () => Services.strings.createBundle(storageProps));
|
||||
loader.lazyGetter(this, "scratchpadStrings", () => Services.strings.createBundle(scratchpadProps));
|
||||
loader.lazyGetter(this, "storageStrings", () => Services.strings.createBundle(storageProps));
|
||||
|
||||
let Tools = {};
|
||||
exports.Tools = Tools;
|
||||
@ -81,11 +78,9 @@ Tools.options = {
|
||||
panelLabel: l10n("options.panelLabel", toolboxStrings),
|
||||
tooltip: l10n("optionsButton.tooltip", toolboxStrings),
|
||||
inMenu: false,
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
return true;
|
||||
},
|
||||
|
||||
build: function(iframeWindow, toolbox) {
|
||||
return new OptionsPanel(iframeWindow, toolbox);
|
||||
}
|
||||
@ -118,7 +113,6 @@ Tools.webConsole = {
|
||||
isTargetSupported: function(target) {
|
||||
return true;
|
||||
},
|
||||
|
||||
build: function(iframeWindow, toolbox) {
|
||||
return new WebConsolePanel(iframeWindow, toolbox);
|
||||
}
|
||||
@ -236,13 +230,11 @@ Tools.canvasDebugger = {
|
||||
label: l10n("ToolboxCanvasDebugger.label", canvasDebuggerStrings),
|
||||
panelLabel: l10n("ToolboxCanvasDebugger.panelLabel", canvasDebuggerStrings),
|
||||
tooltip: l10n("ToolboxCanvasDebugger.tooltip", canvasDebuggerStrings),
|
||||
|
||||
// Hide the Canvas Debugger in the Add-on Debugger and Browser Toolbox
|
||||
// (bug 1047520).
|
||||
isTargetSupported: function(target) {
|
||||
return !target.isAddon && !target.chrome;
|
||||
},
|
||||
|
||||
build: function (iframeWindow, toolbox) {
|
||||
return new CanvasDebuggerPanel(iframeWindow, toolbox);
|
||||
}
|
||||
@ -258,11 +250,9 @@ Tools.webAudioEditor = {
|
||||
label: l10n("ToolboxWebAudioEditor1.label", webAudioEditorStrings),
|
||||
panelLabel: l10n("ToolboxWebAudioEditor1.panelLabel", webAudioEditorStrings),
|
||||
tooltip: l10n("ToolboxWebAudioEditor1.tooltip", webAudioEditorStrings),
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
return !target.isAddon;
|
||||
},
|
||||
|
||||
build: function(iframeWindow, toolbox) {
|
||||
return new WebAudioEditorPanel(iframeWindow, toolbox);
|
||||
}
|
||||
@ -294,32 +284,11 @@ Tools.jsprofiler = {
|
||||
}
|
||||
};
|
||||
|
||||
Tools.timeline = {
|
||||
id: "timeline",
|
||||
ordinal: 8,
|
||||
visibilityswitch: "devtools.timeline.enabled",
|
||||
icon: "chrome://browser/skin/devtools/tool-network.svg",
|
||||
invertIconForLightTheme: true,
|
||||
url: "chrome://browser/content/devtools/timeline/timeline.xul",
|
||||
label: l10n("timeline.label", timelineStrings),
|
||||
panelLabel: l10n("timeline.panelLabel", timelineStrings),
|
||||
tooltip: l10n("timeline.tooltip", timelineStrings),
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
return !target.isAddon;
|
||||
},
|
||||
|
||||
build: function (iframeWindow, toolbox) {
|
||||
let panel = new TimelinePanel(iframeWindow, toolbox);
|
||||
return panel.open();
|
||||
}
|
||||
};
|
||||
|
||||
Tools.netMonitor = {
|
||||
id: "netmonitor",
|
||||
accesskey: l10n("netmonitor.accesskey", netMonitorStrings),
|
||||
key: l10n("netmonitor.commandkey", netMonitorStrings),
|
||||
ordinal: 9,
|
||||
ordinal: 8,
|
||||
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
|
||||
visibilityswitch: "devtools.netmonitor.enabled",
|
||||
icon: "chrome://browser/skin/devtools/tool-network.svg",
|
||||
@ -343,7 +312,7 @@ Tools.netMonitor = {
|
||||
Tools.storage = {
|
||||
id: "storage",
|
||||
key: l10n("storage.commandkey", storageStrings),
|
||||
ordinal: 10,
|
||||
ordinal: 9,
|
||||
accesskey: l10n("storage.accesskey", storageStrings),
|
||||
modifiers: "shift",
|
||||
visibilityswitch: "devtools.storage.enabled",
|
||||
@ -368,7 +337,7 @@ Tools.storage = {
|
||||
|
||||
Tools.scratchpad = {
|
||||
id: "scratchpad",
|
||||
ordinal: 11,
|
||||
ordinal: 10,
|
||||
visibilityswitch: "devtools.scratchpad.enabled",
|
||||
icon: "chrome://browser/skin/devtools/tool-scratchpad.svg",
|
||||
invertIconForLightTheme: true,
|
||||
@ -398,7 +367,6 @@ let defaultTools = [
|
||||
Tools.canvasDebugger,
|
||||
Tools.webAudioEditor,
|
||||
Tools.jsprofiler,
|
||||
Tools.timeline,
|
||||
Tools.netMonitor,
|
||||
Tools.storage,
|
||||
Tools.scratchpad
|
||||
|
@ -13,11 +13,11 @@ DIRS += [
|
||||
'fontinspector',
|
||||
'framework',
|
||||
'inspector',
|
||||
'projecteditor',
|
||||
'layoutview',
|
||||
'markupview',
|
||||
'netmonitor',
|
||||
'profiler',
|
||||
'projecteditor',
|
||||
'responsivedesign',
|
||||
'scratchpad',
|
||||
'shadereditor',
|
||||
@ -27,7 +27,6 @@ DIRS += [
|
||||
'styleeditor',
|
||||
'styleinspector',
|
||||
'tilt',
|
||||
'timeline',
|
||||
'webaudioeditor',
|
||||
'webconsole',
|
||||
'webide',
|
||||
|
@ -1,4 +1,4 @@
|
||||
s/* Any copyright is dedicated to the Public Domain.
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
|
@ -21,8 +21,7 @@ support-files =
|
||||
[browser_graphs-04.js]
|
||||
[browser_graphs-05.js]
|
||||
[browser_graphs-06.js]
|
||||
[browser_graphs-07a.js]
|
||||
[browser_graphs-07b.js]
|
||||
[browser_graphs-07.js]
|
||||
[browser_graphs-08.js]
|
||||
[browser_graphs-09.js]
|
||||
[browser_graphs-10a.js]
|
||||
|
@ -1,69 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests if selections can't be added via clicking, while not allowed.
|
||||
|
||||
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
|
||||
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
|
||||
let {DOMHelpers} = Cu.import("resource:///modules/devtools/DOMHelpers.jsm", {});
|
||||
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
let {Hosts} = devtools.require("devtools/framework/toolbox-hosts");
|
||||
|
||||
let test = Task.async(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let [host, win, doc] = yield createHost();
|
||||
let graph = new LineGraphWidget(doc.body, "fps");
|
||||
yield graph.once("ready");
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
graph.setData(TEST_DATA);
|
||||
graph.selectionEnabled = false;
|
||||
|
||||
info("Attempting to make a selection.");
|
||||
|
||||
dragStart(graph, 300);
|
||||
is(graph.hasSelection() || graph.hasSelectionInProgress(), false,
|
||||
"The graph shouldn't have a selection (1).");
|
||||
|
||||
hover(graph, 400);
|
||||
is(graph.hasSelection() || graph.hasSelectionInProgress(), false,
|
||||
"The graph shouldn't have a selection (2).");
|
||||
|
||||
dragStop(graph, 500);
|
||||
is(graph.hasSelection() || graph.hasSelectionInProgress(), false,
|
||||
"The graph shouldn't have a selection (3).");
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
}
|
@ -10,12 +10,7 @@ const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"AbstractCanvasGraph",
|
||||
"LineGraphWidget",
|
||||
"BarGraphWidget",
|
||||
"CanvasGraphUtils"
|
||||
];
|
||||
this.EXPORTED_SYMBOLS = ["LineGraphWidget", "BarGraphWidget", "CanvasGraphUtils"];
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const GRAPH_SRC = "chrome://browser/content/devtools/graphs-frame.xhtml";
|
||||
@ -499,12 +494,6 @@ AbstractCanvasGraph.prototype = {
|
||||
return this._selection.start != null && this._selection.end == null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies whether or not mouse selection is allowed.
|
||||
* @type boolean
|
||||
*/
|
||||
selectionEnabled: true,
|
||||
|
||||
/**
|
||||
* Sets the selection bounds.
|
||||
* Use `dropCursor` to hide the cursor.
|
||||
@ -966,9 +955,6 @@ AbstractCanvasGraph.prototype = {
|
||||
switch (this._canvas.getAttribute("input")) {
|
||||
case "hovering-background":
|
||||
case "hovering-region":
|
||||
if (!this.selectionEnabled) {
|
||||
break;
|
||||
}
|
||||
this._selection.start = mouseX;
|
||||
this._selection.end = null;
|
||||
this.emit("selecting");
|
||||
@ -1004,9 +990,6 @@ AbstractCanvasGraph.prototype = {
|
||||
switch (this._canvas.getAttribute("input")) {
|
||||
case "hovering-background":
|
||||
case "hovering-region":
|
||||
if (!this.selectionEnabled) {
|
||||
break;
|
||||
}
|
||||
if (this.getSelectionWidth() < 1) {
|
||||
let region = this.getHoveredRegion();
|
||||
if (region) {
|
||||
|
@ -1,13 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
EXTRA_JS_MODULES.devtools.timeline += [
|
||||
'panel.js',
|
||||
'widgets/global.js',
|
||||
'widgets/overview.js',
|
||||
'widgets/waterfall.js'
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
@ -1,63 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/toolkit/event-emitter");
|
||||
|
||||
loader.lazyRequireGetter(this, "TimelineFront",
|
||||
"devtools/server/actors/timeline", true);
|
||||
|
||||
function TimelinePanel(iframeWindow, toolbox) {
|
||||
this.panelWin = iframeWindow;
|
||||
this._toolbox = toolbox;
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
};
|
||||
|
||||
exports.TimelinePanel = TimelinePanel;
|
||||
|
||||
TimelinePanel.prototype = {
|
||||
/**
|
||||
* Open is effectively an asynchronous constructor.
|
||||
*
|
||||
* @return object
|
||||
* A promise that is resolved when the timeline completes opening.
|
||||
*/
|
||||
open: Task.async(function*() {
|
||||
// Local debugging needs to make the target remote.
|
||||
yield this.target.makeRemote();
|
||||
|
||||
this.panelWin.gToolbox = this._toolbox;
|
||||
this.panelWin.gTarget = this.target;
|
||||
this.panelWin.gFront = new TimelineFront(this.target.client, this.target.form);
|
||||
yield this.panelWin.startupTimeline();
|
||||
|
||||
this.isReady = true;
|
||||
this.emit("ready");
|
||||
return this;
|
||||
}),
|
||||
|
||||
// DevToolPanel API
|
||||
|
||||
get target() this._toolbox.target,
|
||||
|
||||
destroy: Task.async(function*() {
|
||||
// Make sure this panel is not already destroyed.
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield this.panelWin.shutdownTimeline();
|
||||
this.emit("destroyed");
|
||||
this._destroyed = true;
|
||||
})
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s # Bug 1065355 - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_simple-test.html
|
||||
head.js
|
||||
|
||||
[browser_timeline_aaa_run_first_leaktest.js]
|
||||
[browser_timeline_blueprint.js]
|
||||
[browser_timeline_overview-initial-selection-01.js]
|
||||
[browser_timeline_overview-initial-selection-02.js]
|
||||
[browser_timeline_overview-update.js]
|
||||
[browser_timeline_panels.js]
|
||||
[browser_timeline_recording.js]
|
||||
[browser_timeline_waterfall-background.js]
|
||||
[browser_timeline_waterfall-generic.js]
|
||||
[browser_timeline_waterfall-styles.js]
|
@ -1,22 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the timeline leaks on initialization and sudden destruction.
|
||||
* You can also use this initialization format as a template for other tests.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
|
||||
ok(target, "Should have a target available.");
|
||||
ok(debuggee, "Should have a debuggee available.");
|
||||
ok(panel, "Should have a panel available.");
|
||||
|
||||
ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window.");
|
||||
ok(panel.panelWin.gTarget, "Should have a target reference on the panel window.");
|
||||
ok(panel.panelWin.gFront, "Should have a front reference on the panel window.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,29 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the timeline blueprint has a correct structure.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { TIMELINE_BLUEPRINT } = devtools.require("devtools/timeline/global");
|
||||
|
||||
ok(TIMELINE_BLUEPRINT,
|
||||
"A timeline blueprint should be available.");
|
||||
|
||||
ok(Object.keys(TIMELINE_BLUEPRINT).length,
|
||||
"The timeline blueprint has at least one entry.");
|
||||
|
||||
for (let [key, value] of Iterator(TIMELINE_BLUEPRINT)) {
|
||||
ok("group" in value,
|
||||
"Each entry in the timeline blueprint contains a `group` key.");
|
||||
ok("fill" in value,
|
||||
"Each entry in the timeline blueprint contains a `fill` key.");
|
||||
ok("stroke" in value,
|
||||
"Each entry in the timeline blueprint contains a `stroke` key.");
|
||||
ok("label" in value,
|
||||
"Each entry in the timeline blueprint contains a `label` key.");
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the overview has an initial selection when recording has finished
|
||||
* and there is data available.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { EVENTS, TimelineView, TimelineController } = panel.panelWin;
|
||||
let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin;
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
let updated = 0;
|
||||
panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++);
|
||||
|
||||
ok((yield waitUntil(() => updated > 10)),
|
||||
"The overview graph was updated a bunch of times.");
|
||||
ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)),
|
||||
"There are some markers available.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
let markers = TimelineController.getMarkers();
|
||||
let selection = TimelineView.overview.getSelection();
|
||||
|
||||
is(selection.start, markers[0].start * TimelineView.overview.dataScaleX,
|
||||
"The initial selection start is correct.");
|
||||
is(selection.end - selection.start, TimelineView.overview.width * selectionRatio,
|
||||
"The initial selection end is correct.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,32 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the overview has no initial selection when recording has finished
|
||||
* and there is no data available.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { EVENTS, TimelineView, TimelineController } = panel.panelWin;
|
||||
let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin;
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
yield TimelineController._stopRecordingAndDiscardData();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
let markers = TimelineController.getMarkers();
|
||||
let selection = TimelineView.overview.getSelection();
|
||||
|
||||
is(markers.length, 0,
|
||||
"There are no markers available.");
|
||||
is(selection.start, null,
|
||||
"The initial selection start is correct.");
|
||||
is(selection.end, null,
|
||||
"The initial selection end is correct.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,48 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the overview graph is continuously updated.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel("about:blank");
|
||||
let { EVENTS, TimelineView, TimelineController } = panel.panelWin;
|
||||
|
||||
yield DevToolsUtils.waitForTime(1000);
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
ok("selectionEnabled" in TimelineView.overview,
|
||||
"The selection should not be enabled for the overview graph (1).");
|
||||
is(TimelineView.overview.selectionEnabled, false,
|
||||
"The selection should not be enabled for the overview graph (2).");
|
||||
is(TimelineView.overview.hasSelection(), false,
|
||||
"The overview graph shouldn't have a selection before recording.");
|
||||
|
||||
let updated = 0;
|
||||
panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++);
|
||||
|
||||
ok((yield waitUntil(() => updated > 10)),
|
||||
"The overview graph was updated a bunch of times.");
|
||||
|
||||
ok("selectionEnabled" in TimelineView.overview,
|
||||
"The selection should still not be enabled for the overview graph (3).");
|
||||
is(TimelineView.overview.selectionEnabled, false,
|
||||
"The selection should still not be enabled for the overview graph (4).");
|
||||
is(TimelineView.overview.hasSelection(), false,
|
||||
"The overview graph should not have a selection while recording.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
is(TimelineController.getMarkers().length, 0,
|
||||
"There are no markers available.");
|
||||
is(TimelineView.overview.selectionEnabled, true,
|
||||
"The selection should now be enabled for the overview graph.");
|
||||
is(TimelineView.overview.hasSelection(), false,
|
||||
"The overview graph should not have a selection after recording.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the timeline panels are correctly shown and hidden when
|
||||
* recording starts and stops.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { $, EVENTS } = panel.panelWin;
|
||||
|
||||
is($("#record-button").hasAttribute("checked"), false,
|
||||
"The record button should not be checked yet.");
|
||||
is($("#timeline-pane").selectedPanel, $("#empty-notice"),
|
||||
"An empty notice is initially displayed instead of the waterfall view.");
|
||||
|
||||
let whenRecStarted = panel.panelWin.once(EVENTS.RECORDING_STARTED);
|
||||
EventUtils.synthesizeMouseAtCenter($("#record-button"), {}, panel.panelWin);
|
||||
yield whenRecStarted;
|
||||
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
is($("#record-button").getAttribute("checked"), "true",
|
||||
"The record button should be checked now.");
|
||||
is($("#timeline-pane").selectedPanel, $("#recording-notice"),
|
||||
"A recording notice is now displayed instead of the waterfall view.");
|
||||
|
||||
let whenRecEnded = panel.panelWin.once(EVENTS.RECORDING_ENDED);
|
||||
EventUtils.synthesizeMouseAtCenter($("#record-button"), {}, panel.panelWin);
|
||||
yield whenRecEnded;
|
||||
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
is($("#record-button").hasAttribute("checked"), false,
|
||||
"The record button should be unchecked again.");
|
||||
is($("#timeline-pane").selectedPanel, $("#timeline-waterfall"),
|
||||
"A waterfall view is now displayed.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the timeline can properly start and stop a recording.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { gFront, TimelineController } = panel.panelWin;
|
||||
|
||||
is((yield gFront.isRecording()), false,
|
||||
"The timeline actor should not be recording when the tool starts.");
|
||||
is(TimelineController.getMarkers().length, 0,
|
||||
"There should be no markers available when the tool starts.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
|
||||
is((yield gFront.isRecording()), true,
|
||||
"The timeline actor should be recording now.");
|
||||
ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)),
|
||||
"There are some markers available now.");
|
||||
|
||||
ok("startTime" in TimelineController.getMarkers(),
|
||||
"A `startTime` field was set on the markers array.");
|
||||
ok("endTime" in TimelineController.getMarkers(),
|
||||
"An `endTime` field was set on the markers array.");
|
||||
ok(TimelineController.getMarkers().endTime >
|
||||
TimelineController.getMarkers().startTime,
|
||||
"Some time has passed since the recording started.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,47 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the waterfall background is a 1px high canvas stretching across
|
||||
* the container bounds.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
let updated = 0;
|
||||
panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++);
|
||||
|
||||
ok((yield waitUntil(() => updated > 0)),
|
||||
"The overview graph was updated a bunch of times.");
|
||||
ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)),
|
||||
"There are some markers available.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
// Test the waterfall background.
|
||||
|
||||
let parentWidth = $("#timeline-waterfall").getBoundingClientRect().width;
|
||||
let waterfallWidth = TimelineView.waterfall._waterfallWidth;
|
||||
let sidebarWidth = 150; // px
|
||||
is(waterfallWidth, parentWidth - sidebarWidth,
|
||||
"The waterfall width is correct.")
|
||||
|
||||
ok(TimelineView.waterfall._canvas,
|
||||
"A canvas should be created after the recording ended.");
|
||||
ok(TimelineView.waterfall._ctx,
|
||||
"A 2d context should be created after the recording ended.");
|
||||
|
||||
is(TimelineView.waterfall._canvas.width, waterfallWidth,
|
||||
"The canvas width is correct.");
|
||||
is(TimelineView.waterfall._canvas.height, 1,
|
||||
"The canvas height is correct.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,68 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the waterfall is properly built after finishing a recording.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { $, $$, EVENTS, TimelineController } = panel.panelWin;
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
let updated = 0;
|
||||
panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++);
|
||||
|
||||
ok((yield waitUntil(() => updated > 0)),
|
||||
"The overview graph was updated a bunch of times.");
|
||||
ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)),
|
||||
"There are some markers available.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
// Test the header container.
|
||||
|
||||
ok($(".timeline-header-container"),
|
||||
"A header container should have been created.");
|
||||
|
||||
// Test the header sidebar (left).
|
||||
|
||||
ok($(".timeline-header-sidebar"),
|
||||
"A header sidebar node should have been created.");
|
||||
ok($(".timeline-header-sidebar > .timeline-header-name"),
|
||||
"A header name label should have been created inside the sidebar.");
|
||||
|
||||
// Test the header ticks (right).
|
||||
|
||||
ok($(".timeline-header-ticks"),
|
||||
"A header ticks node should have been created.");
|
||||
ok($$(".timeline-header-ticks > .timeline-header-tick").length > 0,
|
||||
"Some header tick labels should have been created inside the tick node.");
|
||||
|
||||
// Test the markers container.
|
||||
|
||||
ok($(".timeline-marker-container"),
|
||||
"A marker container should have been created.");
|
||||
|
||||
// Test the markers sidebar (left).
|
||||
|
||||
ok($$(".timeline-marker-sidebar").length,
|
||||
"Some marker sidebar nodes should have been created.");
|
||||
ok($$(".timeline-marker-sidebar > .timeline-marker-bullet").length,
|
||||
"Some marker color bullets should have been created inside the sidebar.");
|
||||
ok($$(".timeline-marker-sidebar > .timeline-marker-name").length,
|
||||
"Some marker name labels should have been created inside the sidebar.");
|
||||
|
||||
// Test the markers waterfall (right).
|
||||
|
||||
ok($$(".timeline-marker-waterfall").length,
|
||||
"Some marker waterfall nodes should have been created.");
|
||||
ok($$(".timeline-marker-waterfall > .timeline-marker-bar").length,
|
||||
"Some marker color bars should have been created inside the waterfall.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,89 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the waterfall is properly built after making a selection
|
||||
* and the child nodes are styled correctly.
|
||||
*/
|
||||
|
||||
var gRGB_TO_HSL = {
|
||||
"rgb(193, 132, 214)": "hsl(285,50%,68%)",
|
||||
"rgb(152, 61, 183)": "hsl(285,50%,48%)",
|
||||
"rgb(161, 223, 138)": "hsl(104,57%,71%)",
|
||||
"rgb(96, 201, 58)": "hsl(104,57%,51%)",
|
||||
"rgb(240, 195, 111)": "hsl(39,82%,69%)",
|
||||
"rgb(227, 155, 22)": "hsl(39,82%,49%)",
|
||||
};
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let [target, debuggee, panel] = yield initTimelinePanel(SIMPLE_URL);
|
||||
let { TIMELINE_BLUEPRINT } = devtools.require("devtools/timeline/global");
|
||||
let { $, $$, EVENTS, TimelineController } = panel.panelWin;
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has started.");
|
||||
|
||||
let updated = 0;
|
||||
panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++);
|
||||
|
||||
ok((yield waitUntil(() => updated > 0)),
|
||||
"The overview graph was updated a bunch of times.");
|
||||
ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)),
|
||||
"There are some markers available.");
|
||||
|
||||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
// Test the table sidebars.
|
||||
|
||||
for (let sidebar of [
|
||||
...$$(".timeline-header-sidebar"),
|
||||
...$$(".timeline-marker-sidebar")
|
||||
]) {
|
||||
is(sidebar.getAttribute("width"), "150",
|
||||
"The table's sidebar width is correct.");
|
||||
}
|
||||
|
||||
// Test the table ticks.
|
||||
|
||||
for (let tick of $$(".timeline-header-tick")) {
|
||||
ok(tick.getAttribute("value").match(/^\d+ ms$/),
|
||||
"The table's timeline ticks appear to have correct labels.");
|
||||
ok(tick.style.transform.match(/^translateX\(.*px\)$/),
|
||||
"The table's timeline ticks appear to have proper translations.");
|
||||
}
|
||||
|
||||
// Test the marker bullets.
|
||||
|
||||
for (let bullet of $$(".timeline-marker-bullet")) {
|
||||
let type = bullet.getAttribute("type");
|
||||
|
||||
ok(type in TIMELINE_BLUEPRINT,
|
||||
"The bullet type is present in the timeline blueprint.");
|
||||
is(gRGB_TO_HSL[bullet.style.backgroundColor], TIMELINE_BLUEPRINT[type].fill,
|
||||
"The bullet's background color is correct.");
|
||||
is(gRGB_TO_HSL[bullet.style.borderColor], TIMELINE_BLUEPRINT[type].stroke,
|
||||
"The bullet's border color is correct.");
|
||||
}
|
||||
|
||||
// Test the marker bars.
|
||||
|
||||
for (let bar of $$(".timeline-marker-bar")) {
|
||||
let type = bar.getAttribute("type");
|
||||
|
||||
ok(type in TIMELINE_BLUEPRINT,
|
||||
"The bar type is present in the timeline blueprint.");
|
||||
is(gRGB_TO_HSL[bar.style.backgroundColor], TIMELINE_BLUEPRINT[type].fill,
|
||||
"The bar's background color is correct.");
|
||||
is(gRGB_TO_HSL[bar.style.borderColor], TIMELINE_BLUEPRINT[type].stroke,
|
||||
"The bar's border color is correct.");
|
||||
|
||||
ok(bar.getAttribute("width") > 0,
|
||||
"The bar appears to have a proper width.");
|
||||
ok(bar.style.transform.match(/^translateX\(.*px\)$/),
|
||||
"The bar appears to have proper translations.");
|
||||
}
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Timeline test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function test() {
|
||||
var a = "Hello world!";
|
||||
document.body.style.backgroundColor = "rgba(" +
|
||||
((Math.random() * 64)|0) + "," +
|
||||
((Math.random() * 16)|0) + "," +
|
||||
((Math.random() * 16)|0) + ",1)";
|
||||
}
|
||||
|
||||
// Prevent this script from being garbage collected.
|
||||
window.setInterval(test, 1);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,133 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
// Disable logging for all the tests. Both the debugger server and frontend will
|
||||
// be affected by this pref.
|
||||
let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", false);
|
||||
|
||||
// Enable the tool while testing.
|
||||
let gToolEnabled = Services.prefs.getBoolPref("devtools.timeline.enabled");
|
||||
Services.prefs.setBoolPref("devtools.timeline.enabled", true);
|
||||
|
||||
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
|
||||
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/timeline/test/";
|
||||
const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
|
||||
|
||||
// All tests are asynchronous.
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
info("finish() was called, cleaning up...");
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
|
||||
Services.prefs.setBoolPref("devtools.timeline.enabled", gToolEnabled);
|
||||
});
|
||||
|
||||
function addTab(url) {
|
||||
info("Adding tab: " + url);
|
||||
|
||||
let deferred = promise.defer();
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
let linkedBrowser = tab.linkedBrowser;
|
||||
|
||||
linkedBrowser.addEventListener("load", function onLoad() {
|
||||
linkedBrowser.removeEventListener("load", onLoad, true);
|
||||
info("Tab added and finished loading: " + url);
|
||||
deferred.resolve(tab);
|
||||
}, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeTab(tab) {
|
||||
info("Removing tab.");
|
||||
|
||||
let deferred = promise.defer();
|
||||
let tabContainer = gBrowser.tabContainer;
|
||||
|
||||
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
|
||||
tabContainer.removeEventListener("TabClose", onClose, false);
|
||||
info("Tab removed and finished closing.");
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a new tab and starts up a toolbox with the timeline panel
|
||||
* automatically selected.
|
||||
*
|
||||
* Must be used within a task.
|
||||
*
|
||||
* @param string url
|
||||
* The location of the new tab to spawn.
|
||||
* @return object
|
||||
* A promise resolved once the timeline is initialized, with the
|
||||
* [target, debuggee, panel] instances.
|
||||
*/
|
||||
function* initTimelinePanel(url) {
|
||||
info("Initializing a timeline pane.");
|
||||
|
||||
let tab = yield addTab(url);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
let toolbox = yield gDevTools.showToolbox(target, "timeline");
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
return [target, debuggee, panel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a tab and destroys the toolbox holding a timeline panel.
|
||||
*
|
||||
* Must be used within a task.
|
||||
*
|
||||
* @param object panel
|
||||
* The timeline panel, created by the toolbox.
|
||||
* @return object
|
||||
* A promise resolved once the timeline, toolbox and debuggee tab
|
||||
* are destroyed.
|
||||
*/
|
||||
function* teardown(panel) {
|
||||
info("Destroying the specified timeline.");
|
||||
|
||||
let tab = panel.target.tab;
|
||||
yield panel._toolbox.destroy();
|
||||
yield removeTab(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a predicate returns true.
|
||||
*
|
||||
* @param function predicate
|
||||
* Invoked once in a while until it returns true.
|
||||
* @param number interval [optional]
|
||||
* How often the predicate is invoked, in milliseconds.
|
||||
*/
|
||||
function waitUntil(predicate, interval = 10) {
|
||||
if (predicate()) {
|
||||
return promise.resolve(true);
|
||||
}
|
||||
let deferred = promise.defer();
|
||||
setTimeout(function() {
|
||||
waitUntil(predicate).then(() => deferred.resolve(true));
|
||||
}, interval);
|
||||
return deferred.promise;
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
/* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm");
|
||||
|
||||
devtools.lazyRequireGetter(this, "promise");
|
||||
devtools.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/toolkit/event-emitter");
|
||||
|
||||
devtools.lazyRequireGetter(this, "Overview",
|
||||
"devtools/timeline/overview", true);
|
||||
devtools.lazyRequireGetter(this, "Waterfall",
|
||||
"devtools/timeline/waterfall", true);
|
||||
|
||||
devtools.lazyImporter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
|
||||
const OVERVIEW_UPDATE_INTERVAL = 200;
|
||||
const OVERVIEW_INITIAL_SELECTION_RATIO = 0.15;
|
||||
|
||||
// The panel's window global is an EventEmitter firing the following events:
|
||||
const EVENTS = {
|
||||
// When a recording is started or stopped, via the `stopwatch` button.
|
||||
RECORDING_STARTED: "Timeline:RecordingStarted",
|
||||
RECORDING_ENDED: "Timeline:RecordingEnded",
|
||||
|
||||
// When the overview graph is populated with new markers.
|
||||
OVERVIEW_UPDATED: "Timeline:OverviewUpdated",
|
||||
|
||||
// When the waterfall view is populated with new markers.
|
||||
WATERFALL_UPDATED: "Timeline:WaterfallUpdated"
|
||||
};
|
||||
|
||||
/**
|
||||
* The current target and the timeline front, set by this tool's host.
|
||||
*/
|
||||
let gToolbox, gTarget, gFront;
|
||||
|
||||
/**
|
||||
* Initializes the timeline controller and views.
|
||||
*/
|
||||
let startupTimeline = Task.async(function*() {
|
||||
yield TimelineView.initialize();
|
||||
yield TimelineController.initialize();
|
||||
});
|
||||
|
||||
/**
|
||||
* Destroys the timeline controller and views.
|
||||
*/
|
||||
let shutdownTimeline = Task.async(function*() {
|
||||
yield TimelineView.destroy();
|
||||
yield TimelineController.destroy();
|
||||
yield gFront.stop();
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions handling the timeline frontend controller.
|
||||
*/
|
||||
let TimelineController = {
|
||||
/**
|
||||
* Permanent storage for the markers streamed by the backend.
|
||||
*/
|
||||
_markers: [],
|
||||
|
||||
/**
|
||||
* Initialization function, called when the tool is started.
|
||||
*/
|
||||
initialize: function() {
|
||||
this._onRecordingTick = this._onRecordingTick.bind(this);
|
||||
this._onMarkers = this._onMarkers.bind(this);
|
||||
gFront.on("markers", this._onMarkers);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the tool is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
gFront.off("markers", this._onMarkers);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the accumulated markers in this recording.
|
||||
* @return array.
|
||||
*/
|
||||
getMarkers: function() {
|
||||
return this._markers;
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts/stops the timeline recording and streaming.
|
||||
*/
|
||||
toggleRecording: Task.async(function*() {
|
||||
let isRecording = yield gFront.isRecording();
|
||||
if (isRecording == false) {
|
||||
yield this._startRecording();
|
||||
} else {
|
||||
yield this._stopRecording();
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the recording, updating the UI as needed.
|
||||
*/
|
||||
_startRecording: function*() {
|
||||
this._markers = [];
|
||||
this._markers.startTime = performance.now();
|
||||
this._markers.endTime = performance.now();
|
||||
this._updateId = setInterval(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
|
||||
|
||||
TimelineView.handleRecordingStarted();
|
||||
yield gFront.start();
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops the recording, updating the UI as needed.
|
||||
*/
|
||||
_stopRecording: function*() {
|
||||
clearInterval(this._updateId);
|
||||
|
||||
TimelineView.handleMarkersUpdate(this._markers);
|
||||
TimelineView.handleRecordingEnded();
|
||||
yield gFront.stop();
|
||||
},
|
||||
|
||||
/**
|
||||
* Used in tests. Stops the recording, discarding the accumulated markers and
|
||||
* updating the UI as needed.
|
||||
*/
|
||||
_stopRecordingAndDiscardData: function*() {
|
||||
this._markers.length = 0;
|
||||
yield this._stopRecording();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback handling the "markers" event on the timeline front.
|
||||
*
|
||||
* @param array markers
|
||||
* A list of new markers collected since the last time this
|
||||
* function was invoked.
|
||||
*/
|
||||
_onMarkers: function(markers) {
|
||||
Array.prototype.push.apply(this._markers, markers);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked at a fixed interval while recording.
|
||||
* Updates the markers store with the current time and the timeline overview.
|
||||
*/
|
||||
_onRecordingTick: function() {
|
||||
this._markers.endTime = performance.now();
|
||||
TimelineView.handleMarkersUpdate(this._markers);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Functions handling the timeline frontend view.
|
||||
*/
|
||||
let TimelineView = {
|
||||
/**
|
||||
* Initialization function, called when the tool is started.
|
||||
*/
|
||||
initialize: Task.async(function*() {
|
||||
this.overview = new Overview($("#timeline-overview"));
|
||||
this.waterfall = new Waterfall($("#timeline-waterfall"));
|
||||
|
||||
this._onSelecting = this._onSelecting.bind(this);
|
||||
this._onRefresh = this._onRefresh.bind(this);
|
||||
this.overview.on("selecting", this._onSelecting);
|
||||
this.overview.on("refresh", this._onRefresh);
|
||||
|
||||
yield this.overview.ready();
|
||||
yield this.waterfall.recalculateBounds();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Destruction function, called when the tool is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
this.overview.off("selecting", this._onSelecting);
|
||||
this.overview.off("refresh", this._onRefresh);
|
||||
this.overview.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* Signals that a recording session has started and triggers the appropriate
|
||||
* changes in the UI.
|
||||
*/
|
||||
handleRecordingStarted: function() {
|
||||
$("#record-button").setAttribute("checked", "true");
|
||||
$("#timeline-pane").selectedPanel = $("#recording-notice");
|
||||
|
||||
this.overview.selectionEnabled = false;
|
||||
this.overview.dropSelection();
|
||||
this.overview.setData([]);
|
||||
this.waterfall.clearView();
|
||||
|
||||
window.emit(EVENTS.RECORDING_STARTED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Signals that a recording session has ended and triggers the appropriate
|
||||
* changes in the UI.
|
||||
*/
|
||||
handleRecordingEnded: function() {
|
||||
$("#record-button").removeAttribute("checked");
|
||||
$("#timeline-pane").selectedPanel = $("#timeline-waterfall");
|
||||
|
||||
this.overview.selectionEnabled = true;
|
||||
|
||||
let markers = TimelineController.getMarkers();
|
||||
if (markers.length) {
|
||||
let start = markers[0].start * this.overview.dataScaleX;
|
||||
let end = start + this.overview.width * OVERVIEW_INITIAL_SELECTION_RATIO;
|
||||
this.overview.setSelection({ start, end });
|
||||
} else {
|
||||
let duration = markers.endTime - markers.startTime;
|
||||
this.waterfall.setData(markers, 0, duration);
|
||||
}
|
||||
|
||||
window.emit(EVENTS.RECORDING_ENDED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Signals that a new set of markers was made available by the controller,
|
||||
* or that the overview graph needs to be updated.
|
||||
*
|
||||
* @param array markers
|
||||
* A list of new markers collected since the recording has started.
|
||||
*/
|
||||
handleMarkersUpdate: function(markers) {
|
||||
this.overview.setData(markers);
|
||||
window.emit(EVENTS.OVERVIEW_UPDATED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback handling the "selecting" event on the timeline overview.
|
||||
*/
|
||||
_onSelecting: function() {
|
||||
if (!this.overview.hasSelection() &&
|
||||
!this.overview.hasSelectionInProgress()) {
|
||||
this.waterfall.clearView();
|
||||
return;
|
||||
}
|
||||
let selection = this.overview.getSelection();
|
||||
let start = selection.start / this.overview.dataScaleX;
|
||||
let end = selection.end / this.overview.dataScaleX;
|
||||
|
||||
let markers = TimelineController.getMarkers();
|
||||
let timeStart = Math.min(start, end);
|
||||
let timeEnd = Math.max(start, end);
|
||||
this.waterfall.setData(markers, timeStart, timeEnd);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback handling the "refresh" event on the timeline overview.
|
||||
*/
|
||||
_onRefresh: function() {
|
||||
this.waterfall.recalculateBounds();
|
||||
this._onSelecting();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the panel window.
|
||||
*/
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
/**
|
||||
* DOM query helpers.
|
||||
*/
|
||||
function $(selector, target = document) {
|
||||
return target.querySelector(selector);
|
||||
}
|
||||
function $$(selector, target = document) {
|
||||
return target.querySelectorAll(selector);
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/timeline.css" type="text/css"?>
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % timelineDTD SYSTEM "chrome://browser/locale/devtools/timeline.dtd">
|
||||
%timelineDTD;
|
||||
]>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||
<script type="application/javascript" src="timeline.js"/>
|
||||
|
||||
<vbox class="theme-body" flex="1">
|
||||
<toolbar id="timeline-toolbar"
|
||||
class="devtools-toolbar">
|
||||
<hbox id="recordings-controls"
|
||||
class="devtools-toolbarbutton-group"
|
||||
align="center">
|
||||
<toolbarbutton id="record-button"
|
||||
class="devtools-toolbarbutton"
|
||||
oncommand="TimelineController.toggleRecording()"
|
||||
tooltiptext="&timelineUI.recordButton.tooltip;"/>
|
||||
<spacer flex="1"/>
|
||||
<label id="record-label"
|
||||
value="&timelineUI.recordLabel;"/>
|
||||
</hbox>
|
||||
</toolbar>
|
||||
|
||||
<vbox id="timeline-overview"/>
|
||||
|
||||
<deck id="timeline-pane"
|
||||
flex="1">
|
||||
<hbox id="empty-notice"
|
||||
class="notice-container"
|
||||
align="center"
|
||||
pack="center"
|
||||
flex="1">
|
||||
<label value="&timelineUI.emptyNotice1;"/>
|
||||
<button id="profiling-notice-button"
|
||||
class="devtools-toolbarbutton"
|
||||
standalone="true"
|
||||
oncommand="TimelineController.toggleRecording()"/>
|
||||
<label value="&timelineUI.emptyNotice2;"/>
|
||||
</hbox>
|
||||
|
||||
<hbox id="recording-notice"
|
||||
class="notice-container"
|
||||
align="center"
|
||||
pack="center"
|
||||
flex="1">
|
||||
<label value="&timelineUI.stopNotice1;"/>
|
||||
<button id="profiling-notice-button"
|
||||
class="devtools-toolbarbutton"
|
||||
standalone="true"
|
||||
checked="true"
|
||||
oncommand="TimelineController.toggleRecording()"/>
|
||||
<label value="&timelineUI.stopNotice2;"/>
|
||||
</hbox>
|
||||
|
||||
<vbox id="timeline-waterfall" flex="1"/>
|
||||
</deck>
|
||||
</vbox>
|
||||
</window>
|
@ -1,51 +0,0 @@
|
||||
/* 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 {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
/**
|
||||
* Localization convenience methods.
|
||||
*/
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/timeline.properties";
|
||||
const L10N = new ViewHelpers.L10N(STRINGS_URI);
|
||||
|
||||
/**
|
||||
* A simple schema for mapping markers to the timeline UI. The keys correspond
|
||||
* to marker names, while the values are objects with the following format:
|
||||
* - group: the row index in the timeline overview graph; multiple markers
|
||||
* can be added on the same row. @see <overview.js/buildGraphImage>
|
||||
* - fill: a fill color used when drawing the marker
|
||||
* - stroke: a stroke color used when drawing the marker
|
||||
* - label: the label used in the waterfall to identify the marker
|
||||
*
|
||||
* Whenever this is changed, browser_timeline_waterfall-styles.js *must* be
|
||||
* updated as well.
|
||||
*/
|
||||
const TIMELINE_BLUEPRINT = {
|
||||
"Styles": {
|
||||
group: 0,
|
||||
fill: "hsl(285,50%,68%)",
|
||||
stroke: "hsl(285,50%,48%)",
|
||||
label: L10N.getStr("timeline.label.styles")
|
||||
},
|
||||
"Reflow": {
|
||||
group: 2,
|
||||
fill: "hsl(104,57%,71%)",
|
||||
stroke: "hsl(104,57%,51%)",
|
||||
label: L10N.getStr("timeline.label.reflow")
|
||||
},
|
||||
"Paint": {
|
||||
group: 1,
|
||||
fill: "hsl(39,82%,69%)",
|
||||
stroke: "hsl(39,82%,49%)",
|
||||
label: L10N.getStr("timeline.label.paint")
|
||||
}
|
||||
};
|
||||
|
||||
// Exported symbols.
|
||||
exports.L10N = L10N;
|
||||
exports.TIMELINE_BLUEPRINT = TIMELINE_BLUEPRINT;
|
@ -1,208 +0,0 @@
|
||||
/* 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 file contains the "overview" graph, which is a minimap of all the
|
||||
* timeline data. Regions inside it may be selected, determining which markers
|
||||
* are visible in the "waterfall".
|
||||
*/
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
|
||||
Cu.import("resource:///modules/devtools/Graphs.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
loader.lazyRequireGetter(this, "L10N",
|
||||
"devtools/timeline/global", true);
|
||||
loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
|
||||
"devtools/timeline/global", true);
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const OVERVIEW_HEADER_HEIGHT = 20; // px
|
||||
const OVERVIEW_BODY_HEIGHT = 50; // px
|
||||
|
||||
const OVERVIEW_BACKGROUND_COLOR = "#fff";
|
||||
const OVERVIEW_CLIPHEAD_LINE_COLOR = "#666";
|
||||
const OVERVIEW_SELECTION_LINE_COLOR = "#555";
|
||||
const OVERVIEW_SELECTION_BACKGROUND_COLOR = "rgba(76,158,217,0.25)";
|
||||
const OVERVIEW_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
|
||||
|
||||
const OVERVIEW_HEADER_TICKS_MULTIPLE = 100; // ms
|
||||
const OVERVIEW_HEADER_TICKS_SPACING_MIN = 75; // px
|
||||
const OVERVIEW_HEADER_SAFE_BOUNDS = 50; // px
|
||||
const OVERVIEW_HEADER_BACKGROUND = "#ebeced";
|
||||
const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
|
||||
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
|
||||
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
|
||||
const OVERVIEW_HEADER_TEXT_PADDING = 6; // px
|
||||
const OVERVIEW_TIMELINE_STROKES = "#aaa";
|
||||
const OVERVIEW_MARKERS_COLOR_STOPS = [0, 0.1, 0.75, 1];
|
||||
const OVERVIEW_MARKER_DURATION_MIN = 4; // ms
|
||||
const OVERVIEW_GROUP_VERTICAL_PADDING = 6; // px
|
||||
const OVERVIEW_GROUP_ALTERNATING_BACKGROUND = "rgba(0,0,0,0.05)";
|
||||
|
||||
/**
|
||||
* An overview for the timeline data.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the overview.
|
||||
*/
|
||||
function Overview(parent, ...args) {
|
||||
AbstractCanvasGraph.apply(this, [parent, "timeline-overview", ...args]);
|
||||
this.once("ready", () => {
|
||||
this.setBlueprint(TIMELINE_BLUEPRINT);
|
||||
|
||||
var preview = [];
|
||||
preview.startTime = 0;
|
||||
preview.endTime = 1000;
|
||||
this.setData(preview);
|
||||
});
|
||||
}
|
||||
|
||||
Overview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
||||
fixedHeight: OVERVIEW_HEADER_HEIGHT + OVERVIEW_BODY_HEIGHT,
|
||||
clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
|
||||
selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
|
||||
selectionBackgroundColor: OVERVIEW_SELECTION_BACKGROUND_COLOR,
|
||||
selectionStripesColor: OVERVIEW_SELECTION_STRIPES_COLOR,
|
||||
|
||||
/**
|
||||
* List of names and colors used to paint this overview.
|
||||
* @see TIMELINE_BLUEPRINT in timeline/widgets/global.js
|
||||
*/
|
||||
setBlueprint: function(blueprint) {
|
||||
this._paintBatches = new Map();
|
||||
this._lastGroup = 0;
|
||||
|
||||
for (let type in blueprint) {
|
||||
this._paintBatches.set(type, { style: blueprint[type], batch: [] });
|
||||
this._lastGroup = Math.max(this._lastGroup, blueprint[type].group);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the graph's data source.
|
||||
* @see AbstractCanvasGraph.prototype.buildGraphImage
|
||||
*/
|
||||
buildGraphImage: function() {
|
||||
let { canvas, ctx } = this._getNamedCanvas("overview-data");
|
||||
let canvasWidth = this._width;
|
||||
let canvasHeight = this._height;
|
||||
let safeBounds = OVERVIEW_HEADER_SAFE_BOUNDS * this._pixelRatio;
|
||||
let availableWidth = canvasWidth - safeBounds;
|
||||
|
||||
// Group markers into separate paint batches. This is necessary to
|
||||
// draw all markers sharing the same style at once.
|
||||
|
||||
for (let marker of this._data) {
|
||||
this._paintBatches.get(marker.name).batch.push(marker);
|
||||
}
|
||||
|
||||
// Calculate each group's height, and the time-based scaling.
|
||||
|
||||
let totalGroups = this._lastGroup + 1;
|
||||
let headerHeight = OVERVIEW_HEADER_HEIGHT * this._pixelRatio;
|
||||
let groupHeight = OVERVIEW_BODY_HEIGHT * this._pixelRatio / totalGroups;
|
||||
let groupPadding = OVERVIEW_GROUP_VERTICAL_PADDING * this._pixelRatio;
|
||||
|
||||
let totalTime = (this._data.endTime - this._data.startTime) || 0;
|
||||
let dataScale = this.dataScaleX = availableWidth / totalTime;
|
||||
|
||||
// Draw the header and overview background.
|
||||
|
||||
ctx.fillStyle = OVERVIEW_HEADER_BACKGROUND;
|
||||
ctx.fillRect(0, 0, canvasWidth, headerHeight);
|
||||
|
||||
ctx.fillStyle = OVERVIEW_BACKGROUND_COLOR;
|
||||
ctx.fillRect(0, headerHeight, canvasWidth, canvasHeight);
|
||||
|
||||
// Draw the alternating odd/even group backgrounds.
|
||||
|
||||
ctx.fillStyle = OVERVIEW_GROUP_ALTERNATING_BACKGROUND;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let i = 1; i < totalGroups; i += 2) {
|
||||
let top = headerHeight + i * groupHeight;
|
||||
ctx.rect(0, top, canvasWidth, groupHeight);
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
|
||||
// Draw the timeline header ticks.
|
||||
|
||||
ctx.textBaseline = "middle";
|
||||
let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
|
||||
let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
|
||||
ctx.font = fontSize + "px " + fontFamily;
|
||||
ctx.fillStyle = OVERVIEW_HEADER_TEXT_COLOR;
|
||||
ctx.strokeStyle = OVERVIEW_TIMELINE_STROKES;
|
||||
ctx.beginPath();
|
||||
|
||||
let tickInterval = this._findOptimalTickInterval(dataScale);
|
||||
let headerTextPadding = OVERVIEW_HEADER_TEXT_PADDING * this._pixelRatio;
|
||||
|
||||
for (let x = 0; x < availableWidth; x += tickInterval) {
|
||||
let left = x + headerTextPadding;
|
||||
let time = Math.round(x / dataScale);
|
||||
let label = L10N.getFormatStr("timeline.tick", time);
|
||||
ctx.fillText(label, left, headerHeight / 2 + 1);
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, canvasHeight);
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
// Draw the timeline markers.
|
||||
|
||||
for (let [, { style, batch }] of this._paintBatches) {
|
||||
let top = headerHeight + style.group * groupHeight + groupPadding / 2;
|
||||
let height = groupHeight - groupPadding;
|
||||
|
||||
let gradient = ctx.createLinearGradient(0, top, 0, top + height);
|
||||
gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[0], style.stroke);
|
||||
gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[1], style.fill);
|
||||
gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[2], style.fill);
|
||||
gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[3], style.stroke);
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.beginPath();
|
||||
|
||||
for (let { start, end } of batch) {
|
||||
let left = start * dataScale;
|
||||
let duration = Math.max(end - start, OVERVIEW_MARKER_DURATION_MIN);
|
||||
let width = Math.max(duration * dataScale, this._pixelRatio);
|
||||
ctx.rect(left, top, width, height);
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
|
||||
// Since all the markers in this batch (thus sharing the same style) have
|
||||
// been drawn, empty it. The next time new markers will be available,
|
||||
// they will be sorted and drawn again.
|
||||
batch.length = 0;
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the optimal tick interval between time markers in this overview.
|
||||
*/
|
||||
_findOptimalTickInterval: function(dataScale) {
|
||||
let timingStep = OVERVIEW_HEADER_TICKS_MULTIPLE;
|
||||
let spacingMin = OVERVIEW_HEADER_TICKS_SPACING_MIN * this._pixelRatio;
|
||||
|
||||
while (true) {
|
||||
let scaledStep = dataScale * timingStep;
|
||||
if (scaledStep < spacingMin) {
|
||||
timingStep <<= 1;
|
||||
continue;
|
||||
}
|
||||
return scaledStep;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.Overview = Overview;
|
@ -1,444 +0,0 @@
|
||||
/* 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 file contains the "waterfall" view, essentially a detailed list
|
||||
* of all the markers in the timeline data.
|
||||
*/
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
|
||||
loader.lazyRequireGetter(this, "L10N",
|
||||
"devtools/timeline/global", true);
|
||||
loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
|
||||
"devtools/timeline/global", true);
|
||||
|
||||
loader.lazyImporter(this, "setNamedTimeout",
|
||||
"resource:///modules/devtools/ViewHelpers.jsm");
|
||||
loader.lazyImporter(this, "clearNamedTimeout",
|
||||
"resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const TIMELINE_IMMEDIATE_DRAW_MARKERS_COUNT = 30;
|
||||
const TIMELINE_FLUSH_OUTSTANDING_MARKERS_DELAY = 75; // ms
|
||||
|
||||
const TIMELINE_HEADER_TICKS_MULTIPLE = 5; // ms
|
||||
const TIMELINE_HEADER_TICKS_SPACING_MIN = 50; // px
|
||||
const TIMELINE_HEADER_TEXT_PADDING = 3; // px
|
||||
|
||||
const TIMELINE_MARKER_SIDEBAR_WIDTH = 150; // px
|
||||
const TIMELINE_MARKER_BAR_WIDTH_MIN = 5; // px
|
||||
|
||||
const WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; // ms
|
||||
const WATERFALL_BACKGROUND_TICKS_SCALES = 3;
|
||||
const WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10; // px
|
||||
const WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
|
||||
const WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32; // byte
|
||||
const WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32; // byte
|
||||
|
||||
/**
|
||||
* A detailed waterfall view for the timeline data.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the waterfall.
|
||||
*/
|
||||
function Waterfall(parent) {
|
||||
this._parent = parent;
|
||||
this._document = parent.ownerDocument;
|
||||
this._fragment = this._document.createDocumentFragment();
|
||||
this._outstandingMarkers = [];
|
||||
|
||||
this._headerContents = this._document.createElement("hbox");
|
||||
this._headerContents.className = "timeline-header-contents";
|
||||
this._parent.appendChild(this._headerContents);
|
||||
|
||||
this._listContents = this._document.createElement("vbox");
|
||||
this._listContents.className = "timeline-list-contents";
|
||||
this._listContents.setAttribute("flex", "1");
|
||||
this._parent.appendChild(this._listContents);
|
||||
|
||||
this._isRTL = this._getRTL();
|
||||
|
||||
// Lazy require is a bit slow, and these are hot objects.
|
||||
this._l10n = L10N;
|
||||
this._blueprint = TIMELINE_BLUEPRINT;
|
||||
this._setNamedTimeout = setNamedTimeout;
|
||||
this._clearNamedTimeout = clearNamedTimeout;
|
||||
}
|
||||
|
||||
Waterfall.prototype = {
|
||||
/**
|
||||
* Populates this view with the provided data source.
|
||||
*
|
||||
* @param array markers
|
||||
* A list of markers received from the controller.
|
||||
* @param number timeStart
|
||||
* The delta time (in milliseconds) to start drawing from.
|
||||
* @param number timeEnd
|
||||
* The delta time (in milliseconds) to end drawing at.
|
||||
*/
|
||||
setData: function(markers, timeStart, timeEnd) {
|
||||
this.clearView();
|
||||
|
||||
let dataScale = this._waterfallWidth / (timeEnd - timeStart);
|
||||
this._drawWaterfallBackground(dataScale);
|
||||
this._buildHeader(this._headerContents, timeStart, dataScale);
|
||||
this._buildMarkers(this._listContents, markers, timeStart, timeEnd, dataScale);
|
||||
},
|
||||
|
||||
/**
|
||||
* Depopulates this view.
|
||||
*/
|
||||
clearView: function() {
|
||||
while (this._headerContents.hasChildNodes()) {
|
||||
this._headerContents.firstChild.remove();
|
||||
}
|
||||
while (this._listContents.hasChildNodes()) {
|
||||
this._listContents.firstChild.remove();
|
||||
}
|
||||
this._listContents.scrollTop = 0;
|
||||
this._outstandingMarkers.length = 0;
|
||||
this._clearNamedTimeout("flush-outstanding-markers");
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates and stores the available width for the waterfall.
|
||||
* This should be invoked every time the container window is resized.
|
||||
*/
|
||||
recalculateBounds: function() {
|
||||
let bounds = this._parent.getBoundingClientRect();
|
||||
this._waterfallWidth = bounds.width - TIMELINE_MARKER_SIDEBAR_WIDTH;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the header part of this view.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the header.
|
||||
* @param number timeStart
|
||||
* @see Waterfall.prototype.setData
|
||||
* @param number dataScale
|
||||
* The time scale of the data source.
|
||||
*/
|
||||
_buildHeader: function(parent, timeStart, dataScale) {
|
||||
let container = this._document.createElement("hbox");
|
||||
container.className = "timeline-header-container";
|
||||
container.setAttribute("flex", "1");
|
||||
|
||||
let sidebar = this._document.createElement("hbox");
|
||||
sidebar.className = "timeline-header-sidebar theme-sidebar";
|
||||
sidebar.setAttribute("width", TIMELINE_MARKER_SIDEBAR_WIDTH);
|
||||
sidebar.setAttribute("align", "center");
|
||||
container.appendChild(sidebar);
|
||||
|
||||
let name = this._document.createElement("label");
|
||||
name.className = "plain timeline-header-name";
|
||||
name.setAttribute("value", this._l10n.getStr("timeline.records"));
|
||||
sidebar.appendChild(name);
|
||||
|
||||
let ticks = this._document.createElement("hbox");
|
||||
ticks.className = "timeline-header-ticks";
|
||||
ticks.setAttribute("align", "center");
|
||||
ticks.setAttribute("flex", "1");
|
||||
container.appendChild(ticks);
|
||||
|
||||
let offset = this._isRTL ? this._waterfallWidth : 0;
|
||||
let direction = this._isRTL ? -1 : 1;
|
||||
let tickInterval = this._findOptimalTickInterval({
|
||||
ticksMultiple: TIMELINE_HEADER_TICKS_MULTIPLE,
|
||||
ticksSpacingMin: TIMELINE_HEADER_TICKS_SPACING_MIN,
|
||||
dataScale: dataScale
|
||||
});
|
||||
|
||||
for (let x = 0; x < this._waterfallWidth; x += tickInterval) {
|
||||
let start = x + direction * TIMELINE_HEADER_TEXT_PADDING;
|
||||
let time = Math.round(timeStart + x / dataScale);
|
||||
let label = this._l10n.getFormatStr("timeline.tick", time);
|
||||
|
||||
let node = this._document.createElement("label");
|
||||
node.className = "plain timeline-header-tick";
|
||||
node.style.transform = "translateX(" + (start - offset) + "px)";
|
||||
node.setAttribute("value", label);
|
||||
ticks.appendChild(node);
|
||||
}
|
||||
|
||||
parent.appendChild(container);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the markers part of this view.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the markers.
|
||||
* @param number timeStart
|
||||
* @see Waterfall.prototype.setData
|
||||
* @param number dataScale
|
||||
* The time scale of the data source.
|
||||
*/
|
||||
_buildMarkers: function(parent, markers, timeStart, timeEnd, dataScale) {
|
||||
let processed = 0;
|
||||
|
||||
for (let marker of markers) {
|
||||
if (!isMarkerInRange(marker, timeStart, timeEnd)) {
|
||||
continue;
|
||||
}
|
||||
// Only build and display a finite number of markers initially, to
|
||||
// preserve a snappy UI. After a certain delay, continue building the
|
||||
// outstanding markers while there's (hopefully) no user interaction.
|
||||
let arguments_ = [this._fragment, marker, timeStart, dataScale];
|
||||
if (processed++ < TIMELINE_IMMEDIATE_DRAW_MARKERS_COUNT) {
|
||||
this._buildMarker.apply(this, arguments_);
|
||||
} else {
|
||||
this._outstandingMarkers.push(arguments_);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no outstanding markers, add a dummy "spacer" at the end
|
||||
// to fill up any remaining available space in the UI.
|
||||
if (!this._outstandingMarkers.length) {
|
||||
this._buildMarker(this._fragment, null);
|
||||
}
|
||||
// Otherwise prepare flushing the outstanding markers after a small delay.
|
||||
else {
|
||||
this._setNamedTimeout("flush-outstanding-markers",
|
||||
TIMELINE_FLUSH_OUTSTANDING_MARKERS_DELAY,
|
||||
() => this._buildOutstandingMarkers(parent));
|
||||
}
|
||||
|
||||
parent.appendChild(this._fragment);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finishes building the outstanding markers in this view.
|
||||
* @see Waterfall.prototype._buildMarkers
|
||||
*/
|
||||
_buildOutstandingMarkers: function(parent) {
|
||||
if (!this._outstandingMarkers.length) {
|
||||
return;
|
||||
}
|
||||
for (let args of this._outstandingMarkers) {
|
||||
this._buildMarker.apply(this, args);
|
||||
}
|
||||
this._outstandingMarkers.length = 0;
|
||||
parent.appendChild(this._fragment);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a single marker in this view.
|
||||
*
|
||||
* @param nsIDOMNode parent
|
||||
* The parent node holding the marker.
|
||||
* @param object marker
|
||||
* The { name, start, end } marker in the data source.
|
||||
* @param timeStart
|
||||
* @see Waterfall.prototype.setData
|
||||
* @param number dataScale
|
||||
* @see Waterfall.prototype._buildMarkers
|
||||
*/
|
||||
_buildMarker: function(parent, marker, timeStart, dataScale) {
|
||||
let container = this._document.createElement("hbox");
|
||||
container.className = "timeline-marker-container";
|
||||
|
||||
if (marker) {
|
||||
this._buildMarkerSidebar(container, marker);
|
||||
this._buildMarkerWaterfall(container, marker, timeStart, dataScale);
|
||||
} else {
|
||||
this._buildMarkerSpacer(container);
|
||||
container.setAttribute("flex", "1");
|
||||
container.setAttribute("is-spacer", "");
|
||||
}
|
||||
|
||||
parent.appendChild(container);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the sidebar part of a marker in this view.
|
||||
*
|
||||
* @param nsIDOMNode container
|
||||
* The container node representing the marker in this view.
|
||||
* @param object marker
|
||||
* @see Waterfall.prototype._buildMarker
|
||||
*/
|
||||
_buildMarkerSidebar: function(container, marker) {
|
||||
let blueprint = this._blueprint[marker.name];
|
||||
|
||||
let sidebar = this._document.createElement("hbox");
|
||||
sidebar.className = "timeline-marker-sidebar theme-sidebar";
|
||||
sidebar.setAttribute("width", TIMELINE_MARKER_SIDEBAR_WIDTH);
|
||||
sidebar.setAttribute("align", "center");
|
||||
|
||||
let bullet = this._document.createElement("hbox");
|
||||
bullet.className = "timeline-marker-bullet";
|
||||
bullet.style.backgroundColor = blueprint.fill;
|
||||
bullet.style.borderColor = blueprint.stroke;
|
||||
bullet.setAttribute("type", marker.name);
|
||||
sidebar.appendChild(bullet);
|
||||
|
||||
let name = this._document.createElement("label");
|
||||
name.className = "plain timeline-marker-name";
|
||||
name.setAttribute("value", blueprint.label);
|
||||
sidebar.appendChild(name);
|
||||
|
||||
container.appendChild(sidebar);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the waterfall part of a marker in this view.
|
||||
*
|
||||
* @param nsIDOMNode container
|
||||
* The container node representing the marker.
|
||||
* @param object marker
|
||||
* @see Waterfall.prototype._buildMarker
|
||||
* @param timeStart
|
||||
* @see Waterfall.prototype.setData
|
||||
* @param number dataScale
|
||||
* @see Waterfall.prototype._buildMarkers
|
||||
*/
|
||||
_buildMarkerWaterfall: function(container, marker, timeStart, dataScale) {
|
||||
let blueprint = this._blueprint[marker.name];
|
||||
|
||||
let waterfall = this._document.createElement("hbox");
|
||||
waterfall.className = "timeline-marker-waterfall";
|
||||
waterfall.setAttribute("flex", "1");
|
||||
|
||||
let start = (marker.start - timeStart) * dataScale;
|
||||
let width = (marker.end - marker.start) * dataScale;
|
||||
let offset = this._isRTL ? this._waterfallWidth : 0;
|
||||
|
||||
let bar = this._document.createElement("hbox");
|
||||
bar.className = "timeline-marker-bar";
|
||||
bar.style.backgroundColor = blueprint.fill;
|
||||
bar.style.borderColor = blueprint.stroke;
|
||||
bar.style.transform = "translateX(" + (start - offset) + "px)";
|
||||
bar.setAttribute("type", marker.name);
|
||||
bar.setAttribute("width", Math.max(width, TIMELINE_MARKER_BAR_WIDTH_MIN));
|
||||
waterfall.appendChild(bar);
|
||||
|
||||
container.appendChild(waterfall);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a dummy spacer as an empty marker.
|
||||
*
|
||||
* @param nsIDOMNode container
|
||||
* The container node representing the marker.
|
||||
*/
|
||||
_buildMarkerSpacer: function(container) {
|
||||
let sidebarSpacer = this._document.createElement("spacer");
|
||||
sidebarSpacer.className = "timeline-marker-sidebar theme-sidebar";
|
||||
sidebarSpacer.setAttribute("width", TIMELINE_MARKER_SIDEBAR_WIDTH);
|
||||
|
||||
let waterfallSpacer = this._document.createElement("spacer");
|
||||
waterfallSpacer.className = "timeline-marker-waterfall";
|
||||
waterfallSpacer.setAttribute("flex", "1");
|
||||
|
||||
container.appendChild(sidebarSpacer);
|
||||
container.appendChild(waterfallSpacer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the background displayed on the marker's waterfall.
|
||||
*
|
||||
* @param number dataScale
|
||||
* @see Waterfall.prototype._buildMarkers
|
||||
*/
|
||||
_drawWaterfallBackground: function(dataScale) {
|
||||
if (!this._canvas || !this._ctx) {
|
||||
this._canvas = this._document.createElementNS(HTML_NS, "canvas");
|
||||
this._ctx = this._canvas.getContext("2d");
|
||||
}
|
||||
let canvas = this._canvas;
|
||||
let ctx = this._ctx;
|
||||
|
||||
// Nuke the context.
|
||||
let canvasWidth = canvas.width = this._waterfallWidth;
|
||||
let canvasHeight = canvas.height = 1; // Awww yeah, 1px, repeats on Y axis.
|
||||
|
||||
// Start over.
|
||||
let imageData = ctx.createImageData(canvasWidth, canvasHeight);
|
||||
let pixelArray = imageData.data;
|
||||
|
||||
let buf = new ArrayBuffer(pixelArray.length);
|
||||
let view8bit = new Uint8ClampedArray(buf);
|
||||
let view32bit = new Uint32Array(buf);
|
||||
|
||||
// Build new millisecond tick lines...
|
||||
let [r, g, b] = WATERFALL_BACKGROUND_TICKS_COLOR_RGB;
|
||||
let alphaComponent = WATERFALL_BACKGROUND_TICKS_OPACITY_MIN;
|
||||
let tickInterval = this._findOptimalTickInterval({
|
||||
ticksMultiple: WATERFALL_BACKGROUND_TICKS_MULTIPLE,
|
||||
ticksSpacingMin: WATERFALL_BACKGROUND_TICKS_SPACING_MIN,
|
||||
dataScale: dataScale
|
||||
});
|
||||
|
||||
// Insert one pixel for each division on each scale.
|
||||
for (let i = 1; i <= WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
|
||||
let increment = tickInterval * Math.pow(2, i);
|
||||
for (let x = 0; x < canvasWidth; x += increment) {
|
||||
let position = x | 0;
|
||||
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
alphaComponent += WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
|
||||
}
|
||||
|
||||
// Flush the image data and cache the waterfall background.
|
||||
pixelArray.set(view8bit);
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
this._document.mozSetImageElement("waterfall-background", canvas);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the optimal tick interval between time markers in this timeline.
|
||||
*
|
||||
* @param number ticksMultiple
|
||||
* @param number ticksSpacingMin
|
||||
* @param number dataScale
|
||||
* @return number
|
||||
*/
|
||||
_findOptimalTickInterval: function({ ticksMultiple, ticksSpacingMin, dataScale }) {
|
||||
let timingStep = ticksMultiple;
|
||||
|
||||
while (true) {
|
||||
let scaledStep = dataScale * timingStep;
|
||||
if (scaledStep < ticksSpacingMin) {
|
||||
timingStep <<= 1;
|
||||
continue;
|
||||
}
|
||||
return scaledStep;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if this is document is in RTL mode.
|
||||
* @return boolean
|
||||
*/
|
||||
_getRTL: function() {
|
||||
let win = this._document.defaultView;
|
||||
let doc = this._document.documentElement;
|
||||
return win.getComputedStyle(doc, null).direction == "rtl";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a given marker is in the specified time range.
|
||||
*
|
||||
* @param object e
|
||||
* The marker containing the { start, end } timestamps.
|
||||
* @param number start
|
||||
* The earliest allowed time.
|
||||
* @param number end
|
||||
* The latest allowed time.
|
||||
* @return boolean
|
||||
* True if the marker fits inside the specified time range.
|
||||
*/
|
||||
function isMarkerInRange(e, start, end) {
|
||||
return (e.start >= start && e.end <= end) || // bounds inside
|
||||
(e.start < start && e.end > end) || // bounds outside
|
||||
(e.start < start && e.end >= start && e.end <= end) || // overlap start
|
||||
(e.end > end && e.start >= start && e.start <= end); // overlap end
|
||||
}
|
||||
|
||||
exports.Waterfall = Waterfall;
|
@ -1,30 +0,0 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<!-- LOCALIZATION NOTE : FILE This file contains the Timeline strings -->
|
||||
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->
|
||||
|
||||
<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
|
||||
- keep it in English, or another language commonly spoken among web developers.
|
||||
- You want to make that choice consistent across the developer tools.
|
||||
- A good criteria is the language in which you'd find the best
|
||||
- documentation on web development on the web. -->
|
||||
|
||||
<!-- LOCALIZATION NOTE (timelineUI.recordButton): This string is displayed
|
||||
- on a button that starts a new recording. -->
|
||||
<!ENTITY timelineUI.recordButton.tooltip "Record timeline operations">
|
||||
|
||||
<!-- LOCALIZATION NOTE (timelineUI.recordButton): This string is displayed
|
||||
- as a label to signal that a recording is in progress. -->
|
||||
<!ENTITY timelineUI.recordLabel "Recording…">
|
||||
|
||||
<!-- LOCALIZATION NOTE (timelineUI.emptyNotice1/2): This is the label shown
|
||||
- in the timeline view when empty. -->
|
||||
<!ENTITY timelineUI.emptyNotice1 "Click on the">
|
||||
<!ENTITY timelineUI.emptyNotice2 "button to start recording timeline events.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (timelineUI.stopNotice1/2): This is the label shown
|
||||
- in the timeline view while recording. -->
|
||||
<!ENTITY timelineUI.stopNotice1 "Click on the">
|
||||
<!ENTITY timelineUI.stopNotice2 "button again to stop recording.">
|
@ -1,40 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE These strings are used inside the Timeline
|
||||
# which is available from the Web Developer sub-menu -> 'Timeline'.
|
||||
# The correct localization of this file might be to keep it in
|
||||
# English, or another language commonly spoken among web developers.
|
||||
# You want to make that choice consistent across the developer tools.
|
||||
# A good criteria is the language in which you'd find the best
|
||||
# documentation on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (timeline.label):
|
||||
# This string is displayed in the title of the tab when the timeline is
|
||||
# displayed inside the developer tools window and in the Developer Tools Menu.
|
||||
timeline.label=Timeline
|
||||
|
||||
# LOCALIZATION NOTE (timeline.panelLabel):
|
||||
# This is used as the label for the toolbox panel.
|
||||
timeline.panelLabel=Timeline Panel
|
||||
|
||||
# LOCALIZATION NOTE (timeline.tooltip):
|
||||
# This string is displayed in the tooltip of the tab when the timeline is
|
||||
# displayed inside the developer tools window.
|
||||
timeline.tooltip=Performance Timeline
|
||||
|
||||
# LOCALIZATION NOTE (timeline.tick):
|
||||
# This string is displayed in the timeline overview, for delimiting ticks
|
||||
# by time, in milliseconds.
|
||||
timeline.tick=%S ms
|
||||
|
||||
# LOCALIZATION NOTE (timeline.records):
|
||||
# This string is displayed in the timeline waterfall, as a title for the menu.
|
||||
timeline.records=RECORDS
|
||||
|
||||
# LOCALIZATION NOTE (timeline.label.*):
|
||||
# These strings are displayed in the timeline waterfall, identifying markers.
|
||||
timeline.label.styles=Styles
|
||||
timeline.label.reflow=Reflow
|
||||
timeline.label.paint=Paint
|
@ -58,8 +58,6 @@
|
||||
locale/browser/devtools/toolbox.dtd (%chrome/browser/devtools/toolbox.dtd)
|
||||
locale/browser/devtools/toolbox.properties (%chrome/browser/devtools/toolbox.properties)
|
||||
locale/browser/devtools/inspector.dtd (%chrome/browser/devtools/inspector.dtd)
|
||||
locale/browser/devtools/timeline.dtd (%chrome/browser/devtools/timeline.dtd)
|
||||
locale/browser/devtools/timeline.properties (%chrome/browser/devtools/timeline.properties)
|
||||
locale/browser/devtools/projecteditor.properties (%chrome/browser/devtools/projecteditor.properties)
|
||||
locale/browser/devtools/eyedropper.properties (%chrome/browser/devtools/eyedropper.properties)
|
||||
locale/browser/devtools/connection-screen.dtd (%chrome/browser/devtools/connection-screen.dtd)
|
||||
|
@ -1,5 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
%include ../../shared/devtools/timeline.inc.css
|
@ -247,7 +247,6 @@ browser.jar:
|
||||
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
|
||||
* skin/classic/browser/devtools/splitview.css (../shared/devtools/splitview.css)
|
||||
|
@ -1,6 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
%include ../shared.inc
|
||||
%include ../../shared/devtools/timeline.inc.css
|
@ -374,7 +374,6 @@ browser.jar:
|
||||
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
|
||||
* skin/classic/browser/devtools/splitview.css (../shared/devtools/splitview.css)
|
||||
|
@ -1,159 +0,0 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
#record-button {
|
||||
list-style-image: url(profiler-stopwatch.svg);
|
||||
}
|
||||
|
||||
#record-button[checked] {
|
||||
list-style-image: url(profiler-stopwatch-checked.svg);
|
||||
}
|
||||
|
||||
#record-button:not([checked]) ~ #record-label {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.notice-container {
|
||||
font-size: 120%;
|
||||
padding-bottom: 35vh;
|
||||
}
|
||||
|
||||
.theme-dark .notice-container {
|
||||
background: #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .notice-container {
|
||||
background: #f0f1f2; /* Toolbars */
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
#empty-notice button,
|
||||
#recording-notice button {
|
||||
min-width: 30px;
|
||||
min-height: 28px;
|
||||
margin: 0;
|
||||
list-style-image: url(profiler-stopwatch.svg);
|
||||
}
|
||||
|
||||
#empty-notice button[checked],
|
||||
#recording-notice button[checked] {
|
||||
list-style-image: url(profiler-stopwatch-checked.svg);
|
||||
}
|
||||
|
||||
#empty-notice button .button-text,
|
||||
#recording-notice button .button-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-dark #timeline-overview {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.theme-light #timeline-overview {
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.timeline-list-contents {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateZ(1px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.timeline-header-ticks,
|
||||
.timeline-marker-waterfall {
|
||||
/* Background created on a <canvas> in js. */
|
||||
/* @see browser/devtools/timeline/widgets/waterfall.js */
|
||||
background-image: -moz-element(#waterfall-background);
|
||||
background-repeat: repeat-y;
|
||||
background-position: -1px center;
|
||||
}
|
||||
|
||||
.timeline-marker-waterfall {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.timeline-marker-container[is-spacer] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.theme-dark .timeline-marker-container:not([is-spacer]):nth-child(2n) {
|
||||
background-color: rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.theme-light .timeline-marker-container:not([is-spacer]):nth-child(2n) {
|
||||
background-color: rgba(128,128,128,0.03);
|
||||
}
|
||||
|
||||
.theme-dark .timeline-marker-container:hover {
|
||||
background-color: rgba(255,255,255,0.1) !important;
|
||||
}
|
||||
|
||||
.theme-light .timeline-marker-container:hover {
|
||||
background-color: rgba(128,128,128,0.1) !important;
|
||||
}
|
||||
|
||||
.timeline-header-sidebar,
|
||||
.timeline-marker-sidebar {
|
||||
-moz-border-end: 1px solid;
|
||||
}
|
||||
|
||||
.theme-dark .timeline-header-sidebar,
|
||||
.theme-dark .timeline-marker-sidebar {
|
||||
-moz-border-end-color: #000;
|
||||
}
|
||||
|
||||
.theme-light .timeline-header-sidebar,
|
||||
.theme-light .timeline-marker-sidebar {
|
||||
-moz-border-end-color: #aaa;
|
||||
}
|
||||
|
||||
.timeline-header-sidebar {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.timeline-marker-sidebar {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.timeline-marker-container:hover > .timeline-marker-sidebar {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.timeline-header-tick {
|
||||
width: 100px;
|
||||
font-size: 9px;
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
.theme-dark .timeline-header-tick {
|
||||
color: #a9bacb;
|
||||
}
|
||||
|
||||
.theme-light .timeline-header-tick {
|
||||
color: #292e33;
|
||||
}
|
||||
|
||||
.timeline-header-tick:not(:first-child) {
|
||||
-moz-margin-start: -100px !important; /* Don't affect layout. */
|
||||
}
|
||||
|
||||
.timeline-marker-bullet {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
-moz-margin-start: 8px;
|
||||
-moz-margin-end: 6px;
|
||||
border: 1px solid;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.timeline-marker-bar {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
border: 1px solid;
|
||||
border-radius: 1px;
|
||||
transform-origin: left center;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
%include ../../shared/devtools/timeline.inc.css
|
@ -284,7 +284,6 @@ browser.jar:
|
||||
* skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
|
||||
skin/classic/browser/devtools/storage.css (../shared/devtools/storage.css)
|
||||
@ -705,7 +704,6 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
|
||||
* skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/aero/browser/devtools/timeline.css (devtools/timeline.css)
|
||||
* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
* skin/classic/aero/browser/devtools/shadereditor.css (devtools/shadereditor.css)
|
||||
* skin/classic/aero/browser/devtools/splitview.css (../shared/devtools/splitview.css)
|
||||
|
@ -28,7 +28,7 @@ const {method, Arg, RetVal} = protocol;
|
||||
const events = require("sdk/event/core");
|
||||
const {setTimeout, clearTimeout} = require("sdk/timers");
|
||||
|
||||
const DEFAULT_TIMELINE_DATA_PULL_TIMEOUT = 200; // ms
|
||||
const TIMELINE_DATA_PULL_TIMEOUT = 300;
|
||||
|
||||
exports.register = function(handle) {
|
||||
handle.addGlobalActor(TimelineActor, "timelineActor");
|
||||
@ -90,9 +90,8 @@ let TimelineActor = protocol.ActorClass({
|
||||
if (markers.length > 0) {
|
||||
events.emit(this, "markers", markers);
|
||||
}
|
||||
this._dataPullTimeout = setTimeout(() => {
|
||||
this._pullTimelineData();
|
||||
}, DEFAULT_TIMELINE_DATA_PULL_TIMEOUT);
|
||||
this._dataPullTimeout = setTimeout(() => this._pullTimelineData(),
|
||||
TIMELINE_DATA_PULL_TIMEOUT);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -115,14 +114,14 @@ let TimelineActor = protocol.ActorClass({
|
||||
this.docshell.recordProfileTimelineMarkers = true;
|
||||
this._pullTimelineData();
|
||||
}
|
||||
}, {}),
|
||||
}, {oneway: true}),
|
||||
|
||||
stop: method(function() {
|
||||
if (this.docshell.recordProfileTimelineMarkers) {
|
||||
this.docshell.recordProfileTimelineMarkers = false;
|
||||
clearTimeout(this._dataPullTimeout);
|
||||
}
|
||||
}, {}),
|
||||
}, {oneway: true}),
|
||||
});
|
||||
|
||||
exports.TimelineFront = protocol.FrontClass(TimelineActor, {
|
||||
|
Loading…
Reference in New Issue
Block a user