Bug 661881: Add an about:telemetry page to Firefox. r=ttaubert

This commit is contained in:
Vladan Djeric 2012-11-08 15:36:06 -05:00
Родитель 9aac74be0b
Коммит e71ad7f6b1
9 изменённых файлов: 1043 добавлений и 0 удалений

Просмотреть файл

@ -64,6 +64,8 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "support", "chrome://global/content/aboutSupport.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "telemetry", "chrome://global/content/aboutTelemetry.xhtml",
nsIAboutModule::ALLOW_SCRIPT }
};
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);

Просмотреть файл

@ -182,6 +182,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
{ NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
{ NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },

Просмотреть файл

@ -0,0 +1,130 @@
/* 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/. */
.hidden {
display: none;
}
html {
background-color: -moz-Field;
color: -moz-FieldText;
font: message-box;
}
body {
padding: 0px;
margin: 0px;
}
h2 {
font-size: medium;
}
#page-description {
background-color: LightGray;
border: 1px solid threedshadow;
margin: 0px;
padding: 10px;
}
#description-enabled > span {
color: green;
}
#description-disabled > span {
color: red;
}
.data-section {
background-color: WhiteSmoke;
border-top: 1px solid threedshadow;
border-bottom: 1px solid threedshadow;
margin: 0px;
padding: 10px;
}
.section-name {
font-size: x-large;
display: inline;
cursor: pointer;
}
.data {
margin: 15px;
}
.toggle-caption {
font-style: italic;
cursor: pointer;
}
.empty-caption {
font-style: italic;
}
.hang-title {
font-size: medium;
font-weight: bold;
text-decoration: underline;
}
#histograms, #addon-histograms {
overflow: hidden;
}
.histogram {
float: left;
border: 1px solid gray;
white-space: nowrap;
padding: 10px;
}
body[dir="rtl"] .histogram {
float: right;
}
.histogram-title {
text-overflow: ellipsis;
width: 100%;
white-space: nowrap;
overflow: hidden;
}
.bar {
width: 2em;
margin: 2px;
text-align: center;
float: left;
font-family: monospace;
}
body[dir="rtl"] .bar {
float: right;
}
.bar-inner {
background-color: DeepSkyBlue;
border: 1px solid #0000b0;
}
th {
font-weight: bold;
white-space: nowrap;
text-align: left;
}
body[dir="rtl"] th {
text-align: right;
}
caption {
font-weight: bold;
white-space: nowrap;
text-align: left;
font-size: large;
}
body[dir="rtl"] caption {
text-align: right;
}

Просмотреть файл

@ -0,0 +1,694 @@
/* 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 Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
const Telemetry = Services.telemetry;
const bundle = Services.strings.createBundle(
"chrome://global/locale/aboutTelemetry.properties");
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].
getService(Ci.nsIObserver);
// Maximum height of a histogram bar (in em)
const MAX_BAR_HEIGHT = 18;
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
const PREF_DEBUG_SLOW_SQL = "toolkit.telemetry.debugSlowSql";
const PREF_SYMBOL_SERVER_URI = "profiler.symbolicationUrl";
const DEFAULT_SYMBOL_SERVER_URI = "http://symbolapi.mozilla.org";
// Cached value of document's RTL mode
let documentRTLMode = "";
/**
* Helper function for fetching a config pref
*
* @param aPrefName Name of config pref to fetch.
* @param aDefault Default value to return if pref isn't set.
* @return Value of pref
*/
function getPref(aPrefName, aDefault) {
let result = aDefault;
try {
let prefType = Services.prefs.getPrefType(aPrefName);
if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
result = Services.prefs.getBoolPref(aPrefName);
} else if (prefType == Ci.nsIPrefBranch.PREF_STRING) {
result = Services.prefs.getCharPref(aPrefName);
}
} catch (e) {
// Return default if Prefs service throws exception
}
return result;
}
/**
* Helper function for determining whether the document direction is RTL.
* Caches result of check on first invocation.
*/
function isRTL() {
if (!documentRTLMode)
documentRTLMode = window.getComputedStyle(document.body).direction;
return (documentRTLMode == "rtl");
}
let observer = {
enableTelemetry: bundle.GetStringFromName("enableTelemetry"),
disableTelemetry: bundle.GetStringFromName("disableTelemetry"),
/**
* Observer is called whenever Telemetry is enabled or disabled
*/
observe: function observe(aSubject, aTopic, aData) {
if (aData == PREF_TELEMETRY_ENABLED) {
this.updatePrefStatus();
}
},
/**
* Updates the button & text at the top of the page to reflect Telemetry state.
*/
updatePrefStatus: function updatePrefStatus() {
// Notify user whether Telemetry is enabled
let enabledElement = document.getElementById("description-enabled");
let disabledElement = document.getElementById("description-disabled");
let toggleElement = document.getElementById("toggle-telemetry");
if (getPref(PREF_TELEMETRY_ENABLED, false)) {
enabledElement.classList.remove("hidden");
disabledElement.classList.add("hidden");
toggleElement.innerHTML = this.disableTelemetry;
} else {
enabledElement.classList.add("hidden");
disabledElement.classList.remove("hidden");
toggleElement.innerHTML = this.enableTelemetry;
}
}
};
let SlowSQL = {
slowSqlHits: bundle.GetStringFromName("slowSqlHits"),
slowSqlAverage: bundle.GetStringFromName("slowSqlAverage"),
slowSqlStatement: bundle.GetStringFromName("slowSqlStatement"),
mainThreadTitle: bundle.GetStringFromName("slowSqlMain"),
otherThreadTitle: bundle.GetStringFromName("slowSqlOther"),
/**
* Render slow SQL statistics
*/
render: function SlowSQL_render() {
let debugSlowSql = getPref(PREF_DEBUG_SLOW_SQL, false);
let {mainThread, otherThreads} =
Telemetry[debugSlowSql ? "debugSlowSQL" : "slowSQL"];
let mainThreadCount = Object.keys(mainThread).length;
let otherThreadCount = Object.keys(otherThreads).length;
if (mainThreadCount == 0 && otherThreadCount == 0) {
showEmptySectionMessage("slow-sql-section");
return;
}
if (debugSlowSql) {
document.getElementById("sql-warning").classList.remove("hidden");
}
let slowSqlDiv = document.getElementById("slow-sql-tables");
// Main thread
if (mainThreadCount > 0) {
let table = document.createElement("table");
this.renderTableHeader(table, this.mainThreadTitle);
this.renderTable(table, mainThread);
slowSqlDiv.appendChild(table);
slowSqlDiv.appendChild(document.createElement("hr"));
}
// Other threads
if (otherThreadCount > 0) {
let table = document.createElement("table");
this.renderTableHeader(table, this.otherThreadTitle);
this.renderTable(table, otherThreads);
slowSqlDiv.appendChild(table);
slowSqlDiv.appendChild(document.createElement("hr"));
}
},
/**
* Creates a header row for a Slow SQL table
*
* @param aTable Parent table element
* @param aTitle Table's title
*/
renderTableHeader: function SlowSQL_renderTableHeader(aTable, aTitle) {
let caption = document.createElement("caption");
caption.appendChild(document.createTextNode(aTitle));
aTable.appendChild(caption);
let headings = document.createElement("tr");
this.appendColumn(headings, "th", this.slowSqlHits);
this.appendColumn(headings, "th", this.slowSqlAverage);
this.appendColumn(headings, "th", this.slowSqlStatement);
aTable.appendChild(headings);
},
/**
* Fills out the table body
*
* @param aTable Parent table element
* @param aSql SQL stats object
*/
renderTable: function SlowSQL_renderTable(aTable, aSql) {
for (let [sql, [hitCount, totalTime]] of Iterator(aSql)) {
let averageTime = totalTime / hitCount;
let sqlRow = document.createElement("tr");
this.appendColumn(sqlRow, "td", hitCount);
this.appendColumn(sqlRow, "td", averageTime.toFixed(0));
this.appendColumn(sqlRow, "td", sql);
aTable.appendChild(sqlRow);
}
},
/**
* Helper function for appending a column to a Slow SQL table.
*
* @param aRowElement Parent row element
* @param aColType Column's tag name
* @param aColText Column contents
*/
appendColumn: function SlowSQL_appendColumn(aRowElement, aColType, aColText) {
let colElement = document.createElement(aColType);
let aColTextElement = document.createTextNode(aColText);
colElement.appendChild(aColTextElement);
aRowElement.appendChild(colElement);
}
};
let ChromeHangs = {
symbolRequest: null,
stackTitle: bundle.GetStringFromName("stackTitle"),
memoryMapTitle: bundle.GetStringFromName("memoryMapTitle"),
errorMessage: bundle.GetStringFromName("errorFetchingSymbols"),
/**
* Renders raw chrome hang data
*/
render: function ChromeHangs_render() {
let hangsDiv = document.getElementById("chrome-hangs-data");
this.clearHangData(hangsDiv);
document.getElementById("fetch-symbols").classList.remove("hidden");
document.getElementById("hide-symbols").classList.add("hidden");
let hangs = Telemetry.chromeHangs;
if (hangs.length == 0) {
showEmptySectionMessage("chrome-hangs-section");
return;
}
this.renderMemoryMap(hangsDiv);
for (let i = 0; i < hangs.length; ++i) {
let currentHang = hangs[i];
this.renderHangHeader(hangsDiv, i + 1, currentHang.duration);
this.renderStack(hangsDiv, currentHang.stack)
}
},
/**
* Renders the title of the hang: e.g. "Hang Report #1 (6 seconds)"
*
* @param aDiv Output div
* @param aIndex The number of the hang
* @param aDuration The duration of the hang
*/
renderHangHeader: function ChromeHangs_renderHangHeader(aDiv, aIndex, aDuration) {
let titleElement = document.createElement("span");
titleElement.className = "hang-title";
let titleText = bundle.formatStringFromName(
"hangTitle", [aIndex, aDuration], 2);
titleElement.appendChild(document.createTextNode(titleText));
aDiv.appendChild(titleElement);
aDiv.appendChild(document.createElement("br"));
},
/**
* Outputs the raw PCs from the hang's stack
*
* @param aDiv Output div
* @param aStack Array of PCs from the hang stack
*/
renderStack: function ChromeHangs_renderStack(aDiv, aStack) {
aDiv.appendChild(document.createTextNode(this.stackTitle));
let stackText = " " + aStack.join(" ");
aDiv.appendChild(document.createTextNode(stackText));
aDiv.appendChild(document.createElement("br"));
aDiv.appendChild(document.createElement("br"));
},
/**
* Outputs the memory map associated with this hang report
*
* @param aDiv Output div
*/
renderMemoryMap: function ChromeHangs_renderMemoryMap(aDiv) {
aDiv.appendChild(document.createTextNode(this.memoryMapTitle));
aDiv.appendChild(document.createElement("br"));
let singleMemoryMap = Telemetry.chromeHangs[0].memoryMap;
for (let currentModule of singleMemoryMap) {
aDiv.appendChild(document.createTextNode(currentModule.join(" ")));
aDiv.appendChild(document.createElement("br"));
}
aDiv.appendChild(document.createElement("br"));
},
/**
* Sends a symbolication request for the recorded hangs
*/
fetchSymbols: function ChromeHangs_fetchSymbols() {
let symbolServerURI =
getPref(PREF_SYMBOL_SERVER_URI, DEFAULT_SYMBOL_SERVER_URI);
let chromeHangsJSON = JSON.stringify(Telemetry.chromeHangs);
this.symbolRequest = XMLHttpRequest();
this.symbolRequest.open("POST", symbolServerURI, true);
this.symbolRequest.setRequestHeader("Content-type", "application/json");
this.symbolRequest.setRequestHeader("Content-length", chromeHangsJSON.length);
this.symbolRequest.setRequestHeader("Connection", "close");
this.symbolRequest.onreadystatechange = this.handleSymbolResponse.bind(this);
this.symbolRequest.send(chromeHangsJSON);
},
/**
* Called when the 'readyState' of the XMLHttpRequest changes. We only care
* about state 4 ("completed") - handling the response data.
*/
handleSymbolResponse: function ChromeHangs_handleSymbolResponse() {
if (this.symbolRequest.readyState != 4)
return;
document.getElementById("fetch-symbols").classList.add("hidden");
document.getElementById("hide-symbols").classList.remove("hidden");
let hangsDiv = document.getElementById("chrome-hangs-data");
this.clearHangData(hangsDiv);
if (this.symbolRequest.status != 200) {
hangsDiv.appendChild(document.createTextNode(this.errorMessage));
return;
}
let jsonResponse = {};
try {
jsonResponse = JSON.parse(this.symbolRequest.responseText);
} catch (e) {
hangsDiv.appendChild(document.createTextNode(this.errorMessage));
return;
}
let hangs = Telemetry.chromeHangs;
for (let i = 0; i < jsonResponse.length; ++i) {
let stack = jsonResponse[i];
let hangDuration = hangs[i].duration;
this.renderHangHeader(hangsDiv, i + 1, hangDuration);
for (let symbol of stack) {
hangsDiv.appendChild(document.createTextNode(symbol));
hangsDiv.appendChild(document.createElement("br"));
}
hangsDiv.appendChild(document.createElement("br"));
}
},
/**
* Removes child elements from the supplied div
*
* @param aDiv Element to be cleared
*/
clearHangData: function ChromeHangs_clearHangData(aDiv) {
while (aDiv.hasChildNodes()) {
aDiv.removeChild(aDiv.lastChild);
}
}
};
let Histogram = {
hgramSamplesCaption: bundle.GetStringFromName("histogramSamples"),
hgramAverageCaption: bundle.GetStringFromName("histogramAverage"),
hgramSumCaption: bundle.GetStringFromName("histogramSum"),
/**
* Renders a single Telemetry histogram
*
* @param aParent Parent element
* @param aName Histogram name
* @param aHgram Histogram information
*/
render: function Histogram_render(aParent, aName, aHgram) {
let hgram = this.unpack(aHgram);
let outerDiv = document.createElement("div");
outerDiv.className = "histogram";
outerDiv.id = aName;
let divTitle = document.createElement("div");
divTitle.className = "histogram-title";
divTitle.appendChild(document.createTextNode(aName));
outerDiv.appendChild(divTitle);
let stats = hgram.sample_count + " " + this.hgramSamplesCaption + ", " +
this.hgramAverageCaption + " = " + hgram.pretty_average + ", " +
this.hgramSumCaption + " = " + hgram.sum;
let divStats = document.createElement("div");
divStats.appendChild(document.createTextNode(stats));
outerDiv.appendChild(divStats);
if (isRTL())
hgram.values.reverse();
this.renderValues(outerDiv, hgram.values, hgram.max);
aParent.appendChild(outerDiv);
},
/**
* Unpacks histogram values
*
* @param aHgram Packed histogram
*
* @return Unpacked histogram representation
*/
unpack: function Histogram_unpack(aHgram) {
let sample_count = aHgram.counts.reduceRight(function (a, b) a + b);
let buckets = [0, 1];
if (aHgram.histogram_type != Telemetry.HISTOGRAM_BOOLEAN) {
buckets = aHgram.ranges;
}
let average = Math.round(aHgram.sum * 10 / sample_count) / 10;
let max_value = Math.max.apply(Math, aHgram.counts);
let first = true;
let last = 0;
let values = [];
for (let i = 0; i < buckets.length; i++) {
let count = aHgram.counts[i];
if (!count)
continue;
if (first) {
first = false;
if (i) {
values.push([buckets[i - 1], 0]);
}
}
last = i + 1;
values.push([buckets[i], count]);
}
if (last && last < buckets.length) {
values.push([buckets[last], 0]);
}
let result = {
values: values,
pretty_average: average,
max: max_value,
sample_count: sample_count,
sum: aHgram.sum
};
return result;
},
/**
* Create histogram bars
*
* @param aDiv Outer parent div
* @param aValues Histogram values
* @param aMaxValue Largest histogram value in set
*/
renderValues: function Histogram_renderValues(aDiv, aValues, aMaxValue) {
for (let [label, value] of aValues) {
let belowEm = Math.round(MAX_BAR_HEIGHT * (value / aMaxValue) * 10) / 10;
let aboveEm = MAX_BAR_HEIGHT - belowEm;
let barDiv = document.createElement("div");
barDiv.className = "bar";
barDiv.style.paddingTop = aboveEm + "em";
// Add value label or an nbsp if no value
barDiv.appendChild(document.createTextNode(value ? value : '\u00A0'));
// Create the blue bar
let bar = document.createElement("div");
bar.className = "bar-inner";
bar.style.height = belowEm + "em";
barDiv.appendChild(bar);
// Add bucket label
barDiv.appendChild(document.createTextNode(label));
aDiv.appendChild(barDiv);
}
}
};
let KeyValueTable = {
keysHeader: bundle.GetStringFromName("keysHeader"),
valuesHeader: bundle.GetStringFromName("valuesHeader"),
/**
* Fill out a 2-column table with keys and values
*/
render: function KeyValueTable_render(aTableID, aMeasurements) {
let table = document.getElementById(aTableID);
this.renderHeader(table);
this.renderBody(table, aMeasurements);
},
/**
* Create the table header
*
* @param aTable Table element
*/
renderHeader: function KeyValueTable_renderHeader(aTable) {
let headerRow = document.createElement("tr");
aTable.appendChild(headerRow);
let keysColumn = document.createElement("th");
keysColumn.appendChild(document.createTextNode(this.keysHeader));
let valuesColumn = document.createElement("th");
valuesColumn.appendChild(document.createTextNode(this.valuesHeader));
headerRow.appendChild(keysColumn);
headerRow.appendChild(valuesColumn);
},
/**
* Create the table body
*
* @param aTable Table element
* @param aMeasurements Key/value map
*/
renderBody: function KeyValueTable_renderBody(aTable, aMeasurements) {
for (let [key, value] of Iterator(aMeasurements)) {
if (typeof value == "object") {
value = JSON.stringify(value);
}
let newRow = document.createElement("tr");
aTable.appendChild(newRow);
let keyField = document.createElement("td");
keyField.appendChild(document.createTextNode(key));
newRow.appendChild(keyField);
let valueField = document.createElement("td");
valueField.appendChild(document.createTextNode(value));
newRow.appendChild(valueField);
}
}
};
/**
* Helper function for showing "No data collected" message for a section
*
* @param aSectionID ID of the section element that needs to be changed
*/
function showEmptySectionMessage(aSectionID) {
let sectionElement = document.getElementById(aSectionID);
// Hide toggle captions
let toggleElements = sectionElement.getElementsByClassName("toggle-caption");
toggleElements[0].classList.add("hidden");
toggleElements[1].classList.add("hidden");
// Show "No data collected" message
let messageElement = sectionElement.getElementsByClassName("empty-caption")[0];
messageElement.classList.remove("hidden");
// Don't allow section to be expanded by clicking on the header text
let sectionHeaders = sectionElement.getElementsByClassName("section-name");
for (let sectionHeader of sectionHeaders) {
sectionHeader.removeEventListener("click", toggleSection);
sectionHeader.style.cursor = "auto";
}
// Don't allow section to be expanded by clicking on the toggle text
let toggleLinks = sectionElement.getElementsByClassName("toggle-caption");
for (let toggleLink of toggleLinks) {
toggleLink.removeEventListener("click", toggleSection);
}
}
/**
* Helper function that expands and collapses sections +
* changes caption on the toggle text
*/
function toggleSection(aEvent) {
let parentElement = aEvent.target.parentElement;
let sectionDiv = parentElement.getElementsByTagName("div")[0];
sectionDiv.classList.toggle("hidden");
let toggleLinks = parentElement.getElementsByClassName("toggle-caption");
toggleLinks[0].classList.toggle("hidden");
toggleLinks[1].classList.toggle("hidden");
}
/**
* Initializes load/unload, pref change and mouse-click listeners
*/
function setupListeners() {
Services.prefs.addObserver(PREF_TELEMETRY_ENABLED, observer, false);
observer.updatePrefStatus();
// Clean up observers when page is closed
window.addEventListener("unload",
function unloadHandler(aEvent) {
window.removeEventListener("unload", unloadHandler);
Services.prefs.removeObserver(PREF_TELEMETRY_ENABLED, observer);
}, false);
document.getElementById("toggle-telemetry").addEventListener("click",
function () {
let value = getPref(PREF_TELEMETRY_ENABLED, false);
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, !value);
}, false);
document.getElementById("fetch-symbols").addEventListener("click",
function () {
ChromeHangs.fetchSymbols();
}, false);
document.getElementById("hide-symbols").addEventListener("click",
function () {
ChromeHangs.render();
}, false);
// Clicking on the section name will toggle its state
let sectionHeaders = document.getElementsByClassName("section-name");
for (let sectionHeader of sectionHeaders) {
sectionHeader.addEventListener("click", toggleSection, false);
}
// Clicking on the "collapse"/"expand" text will also toggle section's state
let toggleLinks = document.getElementsByClassName("toggle-caption");
for (let toggleLink of toggleLinks) {
toggleLink.addEventListener("click", toggleSection, false);
}
}
function onLoad() {
window.removeEventListener("load", onLoad);
// Set up event listeners
setupListeners();
// Show slow SQL stats
SlowSQL.render();
// Show chrome hang stacks
ChromeHangs.render();
// Show histogram data
let histograms = Telemetry.histogramSnapshots;
if (Object.keys(histograms).length) {
let hgramDiv = document.getElementById("histograms");
for (let [name, hgram] of Iterator(histograms)) {
Histogram.render(hgramDiv, name, hgram);
}
} else {
showEmptySectionMessage("histograms-section");
}
// Get the Telemetry Ping payload
let pingData = Cc['@mozilla.org/supports-string;1'].
createInstance(Ci.nsISupportsString);
TelemetryPing.observe(pingData, "get-payload", "");
let ping = {};
try {
ping = JSON.parse(pingData.data);
} catch (e) {
}
// Show simple measurements
if (Object.keys(ping.simpleMeasurements).length) {
KeyValueTable.render("simple-measurements-table", ping.simpleMeasurements);
} else {
showEmptySectionMessage("simple-measurements-section");
}
// Show basic system info gathered
if (Object.keys(ping.info).length) {
KeyValueTable.render("system-info-table", ping.info);
} else {
showEmptySectionMessage("system-info-section");
}
// Show addon histogram data
histograms = Telemetry.addonHistogramSnapshots;
if (Object.keys(histograms).length) {
let addonDiv = document.getElementById("addon-histograms");
for (let [name, hgram] of Iterator(histograms)) {
Histogram.render(addonDiv, "ADDON_" + name, hgram);
}
} else {
showEmptySectionMessage("addon-histograms-section");
}
}
window.addEventListener("load", onLoad, false);

Просмотреть файл

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
# 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/.
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
<!ENTITY % aboutTelemetryDTD SYSTEM "chrome://global/locale/aboutTelemetry.dtd"> %aboutTelemetryDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&aboutTelemetry.pageTitle;</title>
<link rel="stylesheet" href="chrome://global/content/aboutTelemetry.css"
type="text/css"/>
<script type="application/javascript;version=1.7"
src="chrome://global/content/aboutTelemetry.js"/>
</head>
<body dir="&locale.dir;">
<header id="page-description">
<h1>&aboutTelemetry.pageTitle;</h1>
<h2>&aboutTelemetry.pageSubtitle;</h2>
<p id="description-enabled">&aboutTelemetry.telemetryEnabled;</p>
<p id="description-disabled">&aboutTelemetry.telemetryDisabled;</p>
<button id="toggle-telemetry" type="button"/>
</header>
<section id="slow-sql-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.slowSqlSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="slow-sql-tables" class="data hidden">
<p id="sql-warning" class="hidden">&aboutTelemetry.fullSqlWarning;</p>
</div>
</section>
<section id="chrome-hangs-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.chromeHangsSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="chrome-hangs" class="data hidden">
<a id="fetch-symbols" href="javascript:">&aboutTelemetry.fetchSymbols;</a>
<a id="hide-symbols" class="hidden" href="javascript:">&aboutTelemetry.hideSymbols;</a>
<br/>
<br/>
<div id="chrome-hangs-data">
</div>
</div>
</section>
<section id="histograms-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.histogramsSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="histograms" class="data hidden">
</div>
</section>
<section id="simple-measurements-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.simpleMeasurementsSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="simple-measurements" class="data hidden">
<table id="simple-measurements-table">
</table>
</div>
</section>
<section id="system-info-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.systemInfoSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="system-info" class="data hidden">
<table id="system-info-table">
</table>
</div>
</section>
<section id="addon-histograms-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.addonHistogramsSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="addon-histograms" class="data hidden">
</div>
</section>
</body>
</html>

Просмотреть файл

@ -16,6 +16,9 @@ toolkit.jar:
content/global/aboutRights-unbranded.xhtml (aboutRights-unbranded.xhtml)
* content/global/aboutSupport.js
* content/global/aboutSupport.xhtml
content/global/aboutTelemetry.js
* content/global/aboutTelemetry.xhtml
content/global/aboutTelemetry.css (aboutTelemetry.css)
content/global/directionDetector.html
content/global/plugins.html
content/global/plugins.css

Просмотреть файл

@ -0,0 +1,66 @@
<!-- 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/. -->
<!ENTITY aboutTelemetry.pageTitle "Telemetry Data">
<!ENTITY aboutTelemetry.pageSubtitle "
This page shows the performance and feature-use data collected by Telemetry.
This information is submitted anonymously to Mozilla to help improve &brandShortName;.
">
<!ENTITY aboutTelemetry.telemetryEnabled "
Telemetry is <span>enabled</span>.
">
<!ENTITY aboutTelemetry.telemetryDisabled "
Telemetry is <span>disabled</span>.
">
<!ENTITY aboutTelemetry.slowSqlSection "
Slow SQL Statements
">
<!ENTITY aboutTelemetry.chromeHangsSection "
Browser Hangs
">
<!ENTITY aboutTelemetry.histogramsSection "
Histograms
">
<!ENTITY aboutTelemetry.simpleMeasurementsSection "
Simple Measurements
">
<!ENTITY aboutTelemetry.systemInfoSection "
System Information
">
<!ENTITY aboutTelemetry.addonHistogramsSection "
Histograms Collected by Add-ons
">
<!ENTITY aboutTelemetry.toggleOn "
Click to expand section
">
<!ENTITY aboutTelemetry.toggleOff "
Click to collapse section
">
<!ENTITY aboutTelemetry.emptySection "
(No data collected)
">
<!ENTITY aboutTelemetry.fullSqlWarning "
NOTE: Slow SQL debugging is enabled. Full SQL strings may be displayed below but they will not be submitted to Telemetry.
">
<!ENTITY aboutTelemetry.fetchSymbols "
Fetch function names for hang stacks
">
<!ENTITY aboutTelemetry.hideSymbols "
Show raw data from hangs
">

Просмотреть файл

@ -0,0 +1,39 @@
# 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/.
slowSqlMain = Slow SQL Statements on Main Thread
slowSqlOther = Slow SQL Statements on Helper Threads
slowSqlHits = Hits
slowSqlAverage = Avg. Time (ms)
slowSqlStatement = Statement
# Note to translators: Please leave the %S specifiers in the translated string.
# They will be replaced with values programmaticaly, e.g. "Hang Report #3 (5 seconds)"
# - The first %S will be replaced with the number of the hang
# - The second %S will be replaced with the duration of the hang
hangTitle = Hang Report #%S (%S seconds)
stackTitle = Stack:
memoryMapTitle = Memory map:
errorFetchingSymbols = An error occurred while fetching symbols. Check that you are connected to the Internet and try again.
histogramSamples = samples
histogramAverage = average
histogramSum = sum
disableTelemetry = Disable Telemetry
enableTelemetry = Enable Telemetry
keysHeader = Property
valuesHeader = Value

Просмотреть файл

@ -12,6 +12,8 @@
locale/@AB_CD@/global/aboutRights.properties (%chrome/global/aboutRights.properties)
locale/@AB_CD@/global/aboutSupport.dtd (%chrome/global/aboutSupport.dtd)
locale/@AB_CD@/global/aboutSupport.properties (%chrome/global/aboutSupport.properties)
locale/@AB_CD@/global/aboutTelemetry.dtd (%chrome/global/aboutTelemetry.dtd)
locale/@AB_CD@/global/aboutTelemetry.properties (%chrome/global/aboutTelemetry.properties)
locale/@AB_CD@/global/actions.dtd (%chrome/global/actions.dtd)
locale/@AB_CD@/global/appPicker.dtd (%chrome/global/appPicker.dtd)
locale/@AB_CD@/global/brand.dtd (generic/chrome/global/brand.dtd)