--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -321,25 +321,17 @@ var PingPicker = {
document.addEventListener("click", (ev) => {
if (ev.target.querySelector("#ping-picker")) {
document.getElementById("ping-picker").classList.add("hidden");
}
});
document.getElementById("choose-payload")
.addEventListener("change", () => displayPingData(gPingData));
- document.getElementById("scalars-processes")
- .addEventListener("change", () => displayPingData(gPingData));
- document.getElementById("keyed-scalars-processes")
- .addEventListener("change", () => displayPingData(gPingData));
- document.getElementById("histograms-processes")
- .addEventListener("change", () => displayPingData(gPingData));
- document.getElementById("keyed-histograms-processes")
- .addEventListener("change", () => displayPingData(gPingData));
- document.getElementById("events-processes")
+ document.getElementById("processes")
.addEventListener("change", () => displayPingData(gPingData));
Array.from(document.querySelectorAll(".change-ping")).forEach(el =>
el.addEventListener("click", () =>
document.getElementById("ping-picker").classList.remove("hidden"))
);
},
onPingSourceChanged() {
@@ -707,16 +699,17 @@ var EnvironmentData = {
let hasAddonData = Object.keys(ping.environment.addons).length > 0;
let s = GenericSubsection.renderSubsectionHeader("addons", hasAddonData, "environment-data-section");
s.appendChild(addonSection);
dataDiv.appendChild(s);
},
appendRow(table, id, value) {
let row = document.createElement("tr");
+ row.id = id;
this.appendColumn(row, "td", id);
this.appendColumn(row, "td", value);
table.appendChild(row);
},
/**
* Helper function for appending a column to the data table.
*
* @param aRowElement Parent row element
@@ -1357,82 +1350,131 @@ var Histogram = {
// Add bucket label
barDiv.appendChild(document.createTextNode(label));
aDiv.appendChild(barDiv);
}
return text.substr(EOL.length); // Trim the EOL before the first line
},
+};
- /**
- * Helper function for filtering histogram elements by their id
- * Adds the "filter-blocked" class to histogram nodes whose IDs don't match the filter.
- *
- * @param aContainerNode Container node containing the histogram class nodes to filter
- * @param aFilterText either text or /RegEx/. If text, case-insensitive and AND words
- */
- filterHistograms: function _filterHistograms(aContainerNode, aFilterText) {
- let filter = aFilterText.toString();
+
+var Search = {
- // Pass if: all non-empty array items match (case-sensitive)
- function isPassText(subject, filter) {
- for (let item of filter) {
- if (item.length && subject.indexOf(item) < 0) {
- return false; // mismatch and not a spurious space
- }
+ // Pass if: all non-empty array items match (case-sensitive)
+ isPassText(subject, filter) {
+ for (let item of filter) {
+ if (item.length && subject.indexOf(item) < 0) {
+ return false; // mismatch and not a spurious space
}
- return true;
}
+ return true;
+ },
- function isPassRegex(subject, filter) {
- return filter.test(subject);
- }
+ isPassRegex(subject, filter) {
+ return filter.test(subject);
+ },
+ chooseFilter(filterText) {
+ let filter = filterText.toString();
// Setup normalized filter string (trimmed, lower cased and split on spaces if not RegEx)
let isPassFunc; // filter function, set once, then applied to all elements
filter = filter.trim();
if (filter[0] != "/") { // Plain text: case insensitive, AND if multi-string
- isPassFunc = isPassText;
+ isPassFunc = this.isPassText;
filter = filter.toLowerCase().split(" ");
} else {
- isPassFunc = isPassRegex;
+ isPassFunc = this.isPassRegex;
var r = filter.match(/^\/(.*)\/(i?)$/);
try {
filter = RegExp(r[1], r[2]);
} catch (e) { // Incomplete or bad RegExp - always no match
isPassFunc = function() {
return false;
};
}
}
-
- let needLower = (isPassFunc === isPassText);
+ return {filter, isPassFunc}
+ },
- let histograms = aContainerNode.getElementsByClassName("histogram");
- for (let hist of histograms) {
- hist.classList[isPassFunc((needLower ? hist.id.toLowerCase() : hist.id), filter) ? "remove" : "add"]("filter-blocked");
+ filterElements(elements, filterText) {
+ let [isPassFunc, filter] = this.chooseFilter(filterText);
+
+ let needLowerCase = (isPassFunc === this.isPassText);
+ for (let element of elements) {
+ let subject = needLowerCase ? element.id.toLowerCase() : element.id;
+ element.hidden = !isPassFunc(subject, filter);
}
},
- /**
- * Event handler for change at histograms filter input
- *
- * When invoked, 'this' is expected to be the filter HTML node.
- */
- histogramFilterChanged: function _histogramFilterChanged() {
+ filterKeyedElements(keyedElements, filterText) {
+ let res = this.chooseFilter(filterText);
+ let isPassFunc = res.isPassFunc;
+ let filter = res.filter;
+
+ let needLowerCase = (isPassFunc === this.isPassText);
+ keyedElements.forEach((keyedElement) => {
+ let subject = needLowerCase ? keyedElement.key.id.toLowerCase() : keyedElement.key.id;
+ if (!isPassFunc(subject, filter)) { // If the keyedHistogram's name is not matched
+ let allElementHidden = true;
+ for (let element of keyedElement.datas) {
+ let subject = needLowerCase ? element.id.toLowerCase() : element.id;
+ let match = isPassFunc(subject, filter);
+ element.hidden = !match;
+ if (match) {
+ allElementHidden = false;
+ }
+ }
+ keyedElement.key.hidden = allElementHidden;
+ } else { // If the keyedHistogram's name is matched
+ keyedElement.key.hidden = false;
+ for (let element of keyedElement.datas) {
+ element.hidden = false;
+ }
+ }
+ });
+ },
+
+ searchHandler(e) {
if (this.idleTimeout) {
clearTimeout(this.idleTimeout);
}
+ this.idleTimeout = setTimeout(() => Search.search(e.target.value), FILTER_IDLE_TIMEOUT);
+ },
- this.idleTimeout = setTimeout( () => {
- Histogram.filterHistograms(document.getElementById(this.getAttribute("target_id")), this.value);
- }, FILTER_IDLE_TIMEOUT);
- }
-};
+ search(text) {
+ let selectedSection = document.querySelector(".data-section.active");
+ if (selectedSection.id === "histograms-section") {
+ let histograms = selectedSection.getElementsByClassName("histogram");
+ this.filterElements(histograms, text);
+ } else if (selectedSection.id === "keyed-histograms-section") {
+ let keyedElements = [];
+ let keyedHistograms = selectedSection.getElementsByClassName("keyed-histogram");
+ for (let key of keyedHistograms) {
+ let datas = key.getElementsByClassName("histogram");
+ keyedElements.push({key, datas});
+ }
+ this.filterKeyedElements(keyedElements, text);
+ } else if (selectedSection.id === "keyed-scalars-section") {
+ let keyedElements = [];
+ let keyedScalars = selectedSection.getElementsByClassName("keyed-scalar");
+ for (let key of keyedScalars) {
+ let datas = key.querySelector("table").rows;
+ keyedElements.push({key, datas});
+ }
+ this.filterKeyedElements(keyedElements, text);
+ } else {
+ let tables = selectedSection.querySelectorAll("table");
+ for (let table of tables) {
+ this.filterElements(table.rows, text);
+ }
+ }
+ },
+}
/*
* Helper function to render JS objects with white space between top level elements
* so that they look better in the browser
* @param aObject JavaScript object or array to render
* @return String
*/
function RenderObject(aObject) {
@@ -1572,16 +1614,17 @@ var GenericTable = {
(typeof value == "object") &&
(typeof value.valueOf() == "object")) {
return RenderObject(value);
}
return value;
});
let newRow = document.createElement("tr");
+ newRow.id = row[0];
table.appendChild(newRow);
for (let i = 0; i < row.length; ++i) {
let suffix = (i == (row.length - 1)) ? "\n" : "\t";
let field = document.createElement("td");
field.appendChild(document.createTextNode(row[i] + suffix));
newRow.appendChild(field);
}
@@ -1645,17 +1688,17 @@ var Scalars = {
/**
* Render the scalar data - if present - from the payload in a simple key-value table.
* @param aPayload A payload object to render the data from.
*/
render(aPayload) {
let scalarsSection = document.getElementById("scalars");
removeAllChildNodes(scalarsSection);
- let processesSelect = document.getElementById("scalars-processes");
+ let processesSelect = document.getElementById("processes");
let selectedProcess = processesSelect.selectedOptions.item(0).getAttribute("value");
if (!aPayload.processes ||
!selectedProcess ||
!(selectedProcess in aPayload.processes)) {
return;
}
@@ -1679,17 +1722,17 @@ var KeyedScalars = {
/**
* Render the keyed scalar data - if present - from the payload in a simple key-value table.
* @param aPayload A payload object to render the data from.
*/
render(aPayload) {
let scalarsSection = document.getElementById("keyed-scalars");
removeAllChildNodes(scalarsSection);
- let processesSelect = document.getElementById("keyed-scalars-processes");
+ let processesSelect = document.getElementById("processes");
let selectedProcess = processesSelect.selectedOptions.item(0).getAttribute("value");
if (!aPayload.processes ||
!selectedProcess ||
!(selectedProcess in aPayload.processes)) {
return;
}
@@ -1701,22 +1744,26 @@ var KeyedScalars = {
}
const headings = [
"namesHeader",
"valuesHeader",
].map(h => bundle.GetStringFromName(h));
for (let scalar in keyedScalars) {
// Add the name of the scalar.
+ let container = document.createElement("div");
+ container.classList.add("keyed-scalar");
+ container.id = scalar;
let scalarNameSection = document.createElement("h2");
scalarNameSection.appendChild(document.createTextNode(scalar));
- scalarsSection.appendChild(scalarNameSection);
+ container.appendChild(scalarNameSection);
// Populate the section with the key-value pairs from the scalar.
const table = GenericTable.render(explodeObject(keyedScalars[scalar]), headings);
- scalarsSection.appendChild(table);
+ container.appendChild(table);
+ scalarsSection.appendChild(container);
}
},
};
var Events = {
/**
* Render the event data - if present - from the payload in a simple table.
* @param aPayload A payload object to render the data from.
@@ -1724,17 +1771,17 @@ var Events = {
render(aPayload) {
let eventsSection = document.getElementById("events");
removeAllChildNodes(eventsSection);
if (!aPayload.processes || !aPayload.processes.parent) {
return;
}
- let processesSelect = document.getElementById("events-processes");
+ let processesSelect = document.getElementById("processes");
let selectedProcess = processesSelect.selectedOptions.item(0).getAttribute("value");
if (!aPayload.processes ||
!selectedProcess ||
!(selectedProcess in aPayload.processes)) {
return;
}
@@ -1802,37 +1849,65 @@ function setupPageHeader() {
let brandName = brandBundle.GetStringFromName("brandFullName");
let subtitleText = bundle.formatStringFromName(
"pageSubtitle", [serverOwner, brandName], 2);
let subtitleElement = document.getElementById("page-subtitle");
subtitleElement.appendChild(document.createTextNode(subtitleText));
}
+function displayProcessesSelector(selectedSection) {
+ let whitelist = [
+ "scalars-section",
+ "keyed-scalars-section",
+ "histograms-section",
+ "keyed-histograms-section",
+ "events-section"
+ ];
+ let processes = document.getElementById("processes");
+ processes.hidden = !whitelist.includes(selectedSection);
+}
+
+function displaySearch(selectedSection) {
+ let blacklist = [
+ "home",
+ ];
+ // TODO: Implement global search for the Home section
+ let search = document.getElementById("search");
+ search.hidden = blacklist.includes(selectedSection);
+}
+
/**
* Change the section displayed
*/
function show(selected) {
let current_button = document.querySelector(".category.selected");
current_button.classList.remove("selected");
selected.classList.add("selected");
// Hack because subsection text appear selected. See Bug 1375114.
document.getSelection().empty();
+ let selectedValue = selected.getAttribute("value");
let current_section = document.querySelector(".active");
- let selected_section = document.getElementById(selected.getAttribute("value"));
+ let selected_section = document.getElementById(selectedValue);
if (current_section == selected_section)
return;
current_section.classList.remove("active");
current_section.hidden = true;
selected_section.classList.add("active");
selected_section.hidden = false;
- let title = selected.querySelector(".category-name").textContent;
+ let title = selected.querySelector(".category-name").textContent.trim();
document.getElementById("sectionTitle").textContent = title;
+
+ let search = document.getElementById("search");
+ let placeholder = bundle.formatStringFromName("filterPlaceholder", [ title ], 1);
+ search.setAttribute("placeholder", placeholder);
+ displayProcessesSelector(selectedValue);
+ displaySearch(selectedValue);
}
function showSubSection(selected) {
let current_selection = document.querySelector(".category-subsection.selected");
if (current_selection)
current_selection.classList.remove("selected");
selected.classList.add("selected");
@@ -1856,16 +1931,19 @@ function setupListeners() {
let menu = document.getElementById("categories");
menu.addEventListener("click", (e) => {
if (e.target && e.target.parentNode == menu) {
show(e.target)
}
});
+ let search = document.getElementById("search");
+ search.addEventListener("input", Search.searchHandler);
+
// Clean up observers when page is closed
window.addEventListener("unload",
function(aEvent) {
Settings.detachObservers();
}, {once: true});
document.getElementById("chrome-hangs-fetch-symbols").addEventListener("click",
function() {
@@ -1992,17 +2070,17 @@ var LateWritesSingleton = {
var HistogramSection = {
render(aPayload) {
let hgramDiv = document.getElementById("histograms");
removeAllChildNodes(hgramDiv);
let histograms = aPayload.histograms;
- let hgramsSelect = document.getElementById("histograms-processes");
+ let hgramsSelect = document.getElementById("processes");
let hgramsOption = hgramsSelect.selectedOptions.item(0);
let hgramsProcess = hgramsOption.getAttribute("value");
// "parent" histograms/keyedHistograms aren't under "parent". Fix that up.
if (hgramsProcess === "parent") {
hgramsProcess = "";
}
if (hgramsProcess &&
"processes" in aPayload &&
@@ -2013,35 +2091,29 @@ var HistogramSection = {
let hasData = Object.keys(histograms).length > 0;
setHasData("histograms-section", hasData || hgramsSelect.options.length);
if (hasData) {
for (let [name, hgram] of Object.entries(histograms)) {
Histogram.render(hgramDiv, name, hgram, {unpacked: true});
}
- let filterBox = document.getElementById("histograms-filter");
- filterBox.addEventListener("input", Histogram.histogramFilterChanged);
- if (filterBox.value.trim() != "") { // on load, no need to filter if empty
- Histogram.filterHistograms(hgramDiv, filterBox.value);
- }
-
setHasData("histograms-section", true);
}
},
}
var KeyedHistogramSection = {
render(aPayload) {
let keyedDiv = document.getElementById("keyed-histograms");
removeAllChildNodes(keyedDiv);
let keyedHistograms = aPayload.keyedHistograms;
- let keyedHgramsSelect = document.getElementById("keyed-histograms-processes");
+ let keyedHgramsSelect = document.getElementById("processes");
let keyedHgramsOption = keyedHgramsSelect.selectedOptions.item(0);
let keyedHgramsProcess = keyedHgramsOption.getAttribute("value");
// "parent" histograms/keyedHistograms aren't under "parent". Fix that up.
if (keyedHgramsProcess === "parent") {
keyedHgramsProcess = "";
}
if (keyedHgramsProcess &&
"processes" in aPayload &&
@@ -2212,38 +2284,33 @@ function togglePingSections(isMainPing)
continue;
}
section.classList.toggle("has-data", isMainPing);
}
}
function displayPingData(ping, updatePayloadList = false) {
gPingData = ping;
-
// Render raw ping data.
RawPayload.render(ping);
try {
PingPicker.render();
displayRichPingData(ping, updatePayloadList);
} catch (err) {
console.log(err);
PingPicker._showRawPingData();
}
}
function displayRichPingData(ping, updatePayloadList) {
// Update the payload list and process lists
if (updatePayloadList) {
renderPayloadList(ping);
- renderProcessList(ping, document.getElementById("scalars-processes"));
- renderProcessList(ping, document.getElementById("keyed-scalars-processes"));
- renderProcessList(ping, document.getElementById("histograms-processes"));
- renderProcessList(ping, document.getElementById("keyed-histograms-processes"));
- renderProcessList(ping, document.getElementById("events-processes"));
+ renderProcessList(ping, document.getElementById("processes"));
}
// Show general data.
GeneralData.render(ping);
// Show environment data.
EnvironmentData.render(ping);