mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-27 08:39:05 +00:00
258 lines
7.9 KiB
JavaScript
258 lines
7.9 KiB
JavaScript
/* 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 utilities for creating DOM nodes for markers
|
|
* to be displayed in the UI.
|
|
*/
|
|
|
|
const { L10N, PREFS } = require("devtools/client/performance/modules/global");
|
|
const { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");
|
|
const { getSourceNames } = require("devtools/client/shared/source-utils");
|
|
|
|
/**
|
|
* Utilites for creating elements for markers.
|
|
*/
|
|
exports.MarkerDOMUtils = {
|
|
/**
|
|
* Builds all the fields possible for the given marker. Returns an
|
|
* array of elements to be appended to a parent element.
|
|
*
|
|
* @param document doc
|
|
* @param object marker
|
|
* @return array<nsIDOMNode>
|
|
*/
|
|
buildFields: function (doc, marker) {
|
|
let fields = MarkerBlueprintUtils.getMarkerFields(marker);
|
|
return fields.map(({ label, value }) => this.buildNameValueLabel(doc, label, value));
|
|
},
|
|
|
|
/**
|
|
* Builds the label representing the marker's type.
|
|
*
|
|
* @param document doc
|
|
* @param object marker
|
|
* @return nsIDOMNode
|
|
*/
|
|
buildTitle: function (doc, marker) {
|
|
let blueprint = MarkerBlueprintUtils.getBlueprintFor(marker);
|
|
|
|
let hbox = doc.createElement("hbox");
|
|
hbox.setAttribute("align", "center");
|
|
|
|
let bullet = doc.createElement("hbox");
|
|
bullet.className = `marker-details-bullet marker-color-${blueprint.colorName}`;
|
|
|
|
let title = MarkerBlueprintUtils.getMarkerLabel(marker);
|
|
let label = doc.createElement("label");
|
|
label.className = "marker-details-type";
|
|
label.setAttribute("value", title);
|
|
|
|
hbox.appendChild(bullet);
|
|
hbox.appendChild(label);
|
|
|
|
return hbox;
|
|
},
|
|
|
|
/**
|
|
* Builds the label representing the marker's duration.
|
|
*
|
|
* @param document doc
|
|
* @param object marker
|
|
* @return nsIDOMNode
|
|
*/
|
|
buildDuration: function (doc, marker) {
|
|
let label = L10N.getStr("marker.field.duration");
|
|
let start = L10N.getFormatStrWithNumbers("timeline.tick", marker.start);
|
|
let end = L10N.getFormatStrWithNumbers("timeline.tick", marker.end);
|
|
let duration = L10N.getFormatStrWithNumbers("timeline.tick",
|
|
marker.end - marker.start);
|
|
|
|
let el = this.buildNameValueLabel(doc, label, duration);
|
|
el.classList.add("marker-details-duration");
|
|
el.setAttribute("tooltiptext", `${start} → ${end}`);
|
|
|
|
return el;
|
|
},
|
|
|
|
/**
|
|
* Builds labels for name:value pairs.
|
|
* E.g. "Start: 100ms", "Duration: 200ms", ...
|
|
*
|
|
* @param document doc
|
|
* @param string field
|
|
* @param string value
|
|
* @return nsIDOMNode
|
|
*/
|
|
buildNameValueLabel: function (doc, field, value) {
|
|
let hbox = doc.createElement("hbox");
|
|
hbox.className = "marker-details-labelcontainer";
|
|
|
|
let nameLabel = doc.createElement("label");
|
|
nameLabel.className = "plain marker-details-name-label";
|
|
nameLabel.setAttribute("value", field);
|
|
hbox.appendChild(nameLabel);
|
|
|
|
let valueLabel = doc.createElement("label");
|
|
valueLabel.className = "plain marker-details-value-label";
|
|
valueLabel.setAttribute("value", value);
|
|
hbox.appendChild(valueLabel);
|
|
|
|
return hbox;
|
|
},
|
|
|
|
/**
|
|
* Builds a stack trace in an element.
|
|
*
|
|
* @param document doc
|
|
* @param object params
|
|
* An options object with the following members:
|
|
* - string type: string identifier for type of stack ("stack", "startStack"
|
|
or "endStack"
|
|
* - number frameIndex: the index of the topmost stack frame
|
|
* - array frames: array of stack frames
|
|
*/
|
|
buildStackTrace: function (doc, { type, frameIndex, frames }) {
|
|
let container = doc.createElement("vbox");
|
|
container.className = "marker-details-stack";
|
|
container.setAttribute("type", type);
|
|
|
|
let nameLabel = doc.createElement("label");
|
|
nameLabel.className = "plain marker-details-name-label";
|
|
nameLabel.setAttribute("value", L10N.getStr(`marker.field.${type}`));
|
|
container.appendChild(nameLabel);
|
|
|
|
// Workaround for profiles that have looping stack traces. See
|
|
// bug 1246555.
|
|
let wasAsyncParent = false;
|
|
let seen = new Set();
|
|
|
|
while (frameIndex > 0) {
|
|
if (seen.has(frameIndex)) {
|
|
break;
|
|
}
|
|
seen.add(frameIndex);
|
|
|
|
let frame = frames[frameIndex];
|
|
let url = frame.source;
|
|
let displayName = frame.functionDisplayName;
|
|
let line = frame.line;
|
|
|
|
// If the previous frame had an async parent, then the async
|
|
// cause is in this frame and should be displayed.
|
|
if (wasAsyncParent) {
|
|
let asyncStr = L10N.getFormatStr("marker.field.asyncStack", frame.asyncCause);
|
|
let asyncBox = doc.createElement("hbox");
|
|
let asyncLabel = doc.createElement("label");
|
|
asyncLabel.className = "devtools-monospace";
|
|
asyncLabel.setAttribute("value", asyncStr);
|
|
asyncBox.appendChild(asyncLabel);
|
|
container.appendChild(asyncBox);
|
|
wasAsyncParent = false;
|
|
}
|
|
|
|
let hbox = doc.createElement("hbox");
|
|
|
|
if (displayName) {
|
|
let functionLabel = doc.createElement("label");
|
|
functionLabel.className = "devtools-monospace";
|
|
functionLabel.setAttribute("value", displayName);
|
|
hbox.appendChild(functionLabel);
|
|
}
|
|
|
|
if (url) {
|
|
let linkNode = doc.createElement("a");
|
|
linkNode.className = "waterfall-marker-location devtools-source-link";
|
|
linkNode.href = url;
|
|
linkNode.draggable = false;
|
|
linkNode.setAttribute("title", url);
|
|
|
|
let urlLabel = doc.createElement("label");
|
|
urlLabel.className = "filename";
|
|
urlLabel.setAttribute("value", getSourceNames(url).short);
|
|
linkNode.appendChild(urlLabel);
|
|
|
|
let lineLabel = doc.createElement("label");
|
|
lineLabel.className = "line-number";
|
|
lineLabel.setAttribute("value", `:${line}`);
|
|
linkNode.appendChild(lineLabel);
|
|
|
|
hbox.appendChild(linkNode);
|
|
|
|
// Clicking here will bubble up to the parent,
|
|
// which handles the view source.
|
|
linkNode.setAttribute("data-action", JSON.stringify({
|
|
url: url,
|
|
line: line,
|
|
action: "view-source"
|
|
}));
|
|
}
|
|
|
|
if (!displayName && !url) {
|
|
let unknownLabel = doc.createElement("label");
|
|
unknownLabel.setAttribute("value", L10N.getStr("marker.value.unknownFrame"));
|
|
hbox.appendChild(unknownLabel);
|
|
}
|
|
|
|
container.appendChild(hbox);
|
|
|
|
if (frame.asyncParent) {
|
|
frameIndex = frame.asyncParent;
|
|
wasAsyncParent = true;
|
|
} else {
|
|
frameIndex = frame.parent;
|
|
}
|
|
}
|
|
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* Builds any custom fields specific to the marker.
|
|
*
|
|
* @param document doc
|
|
* @param object marker
|
|
* @param object options
|
|
* @return array<nsIDOMNode>
|
|
*/
|
|
buildCustom: function (doc, marker, options) {
|
|
let elements = [];
|
|
|
|
if (options.allocations && shouldShowAllocationsTrigger(marker)) {
|
|
let hbox = doc.createElement("hbox");
|
|
hbox.className = "marker-details-customcontainer";
|
|
|
|
let label = doc.createElement("label");
|
|
label.className = "custom-button devtools-button";
|
|
label.setAttribute("value", "Show allocation triggers");
|
|
label.setAttribute("type", "show-allocations");
|
|
label.setAttribute("data-action", JSON.stringify({
|
|
endTime: marker.start,
|
|
action: "show-allocations"
|
|
}));
|
|
|
|
hbox.appendChild(label);
|
|
elements.push(hbox);
|
|
}
|
|
|
|
return elements;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Takes a marker and determines if this marker should display
|
|
* the allocations trigger button.
|
|
*
|
|
* @param object marker
|
|
* @return boolean
|
|
*/
|
|
function shouldShowAllocationsTrigger(marker) {
|
|
if (marker.name == "GarbageCollection") {
|
|
let showTriggers = PREFS["show-triggers-for-gc-types"];
|
|
return showTriggers.split(" ").indexOf(marker.causeName) !== -1;
|
|
}
|
|
return false;
|
|
}
|