summaryrefslogtreecommitdiffstats
path: root/devtools/client/performance/modules/marker-dom-utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/performance/modules/marker-dom-utils.js')
-rw-r--r--devtools/client/performance/modules/marker-dom-utils.js257
1 files changed, 257 insertions, 0 deletions
diff --git a/devtools/client/performance/modules/marker-dom-utils.js b/devtools/client/performance/modules/marker-dom-utils.js
new file mode 100644
index 000000000..006b13171
--- /dev/null
+++ b/devtools/client/performance/modules/marker-dom-utils.js
@@ -0,0 +1,257 @@
+/* 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;
+}