Modifications to performance monitor UI to address real webui.

UI snapshot: http://imgur.com/js4nc

Review URL: https://chromiumcodereview.appspot.com/10820031

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152214 0039d316-1c4b-4281-b951-d872f2087c98
parent 48f2f46e
...@@ -124,6 +124,13 @@ ...@@ -124,6 +124,13 @@
<include name="IDR_INSTANT_JS" file="resources\instant\instant.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_INSTANT_JS" file="resources\instant\instant.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_MEDIA_INTERNALS_HTML" file="resources\media_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_MEDIA_INTERNALS_HTML" file="resources\media_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_MEDIA_INTERNALS_JS" file="resources\media_internals\media_internals.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_MEDIA_INTERNALS_JS" file="resources\media_internals\media_internals.js" flattenhtml="true" type="BINDATA" />
<if expr="not pp_ifdef('android')">
<include name="IDR_PERFORMANCE_MONITOR_CHART_CSS" file="resources\performance_monitor\chart.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_PERFORMANCE_MONITOR_CHART_JS" file="resources\performance_monitor\chart.js" type="BINDATA" />
<include name="IDR_PERFORMANCE_MONITOR_HTML" file="resources\performance_monitor\chart.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_PERFORMANCE_MONITOR_JQUERY_FLOT_JS" file="..\..\third_party\flot\jquery.flot.min.js" type="BINDATA" />
<include name="IDR_PERFORMANCE_MONITOR_JQUERY_JS" file="..\..\third_party\flot\jquery.min.js" type="BINDATA" />
</if>
<include name="IDR_PREDICTORS_HTML" file="resources\predictors\predictors.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_PREDICTORS_HTML" file="resources\predictors\predictors.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_PREDICTORS_JS" file="resources\predictors\predictors.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_PREDICTORS_JS" file="resources\predictors\predictors.js" flattenhtml="true" type="BINDATA" />
<if expr="not pp_ifdef('android')"> <if expr="not pp_ifdef('android')">
......
...@@ -2,41 +2,29 @@ ...@@ -2,41 +2,29 @@
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */ * found in the LICENSE file. */
div#chooseMetrics { #choose-block {
float: left; -webkit-box-orient: horizontal;
}
div#chooseEvents {
float: left;
margin-left: 20px;
}
div#chooseTimeRange {
float: right;
}
div#chooseBlock {
background: #eee; background: #eee;
overflow: hidden; display: -webkit-box;
} }
div#charts { .spacer {
height: 500px; -webkit-box-flex: 1;
width: 900px;
} }
div.chart { /* |charts| is the style for the div enclosing all charts, collectively. Its
* dimensions must increase if the div encloses more than one chart.
* |chart| is the style for any div that encloses a single chart -- a
* child div of the larger #charts div. */
#charts,
.chart {
height: 500px; height: 500px;
width: 900px; width: 900px;
} }
div.event-label { div.event-label {
background: #fff; background: white;
border: 1px solid; border: 1px solid;
font: 0.6em; font: 0.6em;
position: absolute; position: absolute;
} }
.hidden {
visibility: hidden;
}
...@@ -7,44 +7,45 @@ found in the LICENSE file. --> ...@@ -7,44 +7,45 @@ found in the LICENSE file. -->
<!-- This page uses Flot version 0.7 in compressed form for efficiency. <!-- This page uses Flot version 0.7 in compressed form for efficiency.
Readable Flot source is available at http://code.google.com/p/flot/ Readable Flot source is available at http://code.google.com/p/flot/
Good caliber Flot API docs are at: http://people.iola.dk/olau/flot/API.txt Good caliber Flot API docs are at: http://people.iola.dk/olau/flot/API.txt
as of 6/2012 --> as of 6/2012. -->
<html> <html>
<head> <head>
<script type="text/javascript"
src="../../../../third_party/flot/jquery.min.js"></script>
<script type="text/javascript"
src="../../../../third_party/flot/jquery.flot.min.js"></script>
<link rel="stylesheet" type="text/css" href="chart.css"/> <link rel="stylesheet" type="text/css" href="chart.css"/>
</head> </head>
<body> <body>
<div id="chooseBlock"> <div id="choose-block">
<div id="chooseMetrics"> <div id="choose-metrics">
<h2>Choose Metrics</h2> <h2>Choose Metrics</h2>
</div> </div>
<div id="chooseEvents"> <div id="choose-events">
<h2>Choose Events</h2> <h2>Choose Events</h2>
</div> </div>
<div id="chooseTimeRange"> <div class="spacer"></div>
<div id="choose-time-range">
<h2>Choose Time Range</h2> <h2>Choose Time Range</h2>
</div> </div>
</div> </div>
<div id="charts"></div> <div id="charts"></div>
<div id="templates" class="hidden"> <div id="templates" hidden>
<div id="labelTemplate" class="event-label"></div> <div id="label-template" class="event-label"></div>
<div id="checkboxTemplate" class="checkbox"> <div id="checkbox-template" class="checkbox">
<label> <label>
<input type="checkbox"></input> <input type="checkbox"></input>
<span>Change this label</span> <span>Change this label</span>
</label> </label>
</div> </div>
<div id="radioTemplate" class="radio"> <div id="radio-template" class="radio">
<label> <label>
<input type="radio" name="TimeRange"/> <input type="radio" name="time-range"/>
<span>Change this label</span> <span>Change this label</span>
</label> </label>
</div> </div>
</div> </div>
<script type="text/javascript" src="chart.js"></script> <script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="jquery.js"></script>
<script src="flot.js"></script>
<script src="chart.js"></script>
</body> </body>
</html> </html>
...@@ -2,135 +2,172 @@ ...@@ -2,135 +2,172 @@
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */ * found in the LICENSE file. */
'use strict';
/* convert to cr.define('PerformanceMonitor', function() {... cr.define('performance_monitor', function() {
* when integrating into webui */ 'use strict';
var Installer = function() {
/** /**
* Enum for time ranges, giving a descriptive name, time span prior to |now|, * Enum for time ranges, giving a descriptive name, time span prior to |now|,
* data point resolution, and time-label frequency and format for each. * data point resolution, and time-label frequency and format for each.
* @enum {{ * @enum {{
* value: !number, * value: number,
* name: !string, * name: string,
* timeSpan: !number, * timeSpan: number,
* resolution: !number, * resolution: number,
* labelEvery: !number, * labelEvery: number,
* format: !string * format: string
* }} * }}
* @private * @private
*/ */
var TimeRange_ = { var TimeRange_ = {
// Prior 12 min, resolution of 1s, at most 720 points.
// Labels at 60 point (1 min) intervals.
minutes: {value: 0, name: 'Last 12 min', timeSpan: 720 * 1000,
resolution: 1000, labelEvery: 60, format: 'MM-dd'},
// Prior hour, resolution of 5s, at most 720 points.
// Labels at 60 point (5 min) intervals.
hour: {value: 1, name: 'Last Hour', timeSpan: 3600 * 1000,
resolution: 1000 * 5, labelEvery: 60, format: 'MM-dd'},
// Prior day, resolution of 2 min, at most 720 points. // Prior day, resolution of 2 min, at most 720 points.
// Labels at 90 point (3 hour) intervals. // Labels at 90 point (3 hour) intervals.
day: {value: 0, name: 'Last Day', timeSpan: 24 * 3600 * 1000, day: {value: 2, name: 'Last Day', timeSpan: 24 * 3600 * 1000,
resolution: 1000 * 60 * 2, labelEvery: 90, format: 'MM-dd'}, resolution: 1000 * 60 * 2, labelEvery: 90, format: 'MM-dd'},
// Prior week, resolution of 15 min -- at most 672 data points. // Prior week, resolution of 15 min -- at most 672 data points.
// Labels at 96 point (daily) intervals. // Labels at 96 point (daily) intervals.
week: {value: 1, name: 'Last Week', timeSpan: 7 * 24 * 3600 * 1000, week: {value: 3, name: 'Last Week', timeSpan: 7 * 24 * 3600 * 1000,
resolution: 1000 * 60 * 15, labelEvery: 96, format: 'M/d'}, resolution: 1000 * 60 * 15, labelEvery: 96, format: 'M/d'},
// Prior month (30 days), resolution of 1 hr -- at most 720 data points. // Prior month (30 days), resolution of 1 hr -- at most 720 data points.
// Labels at 168 point (weekly) intervals. // Labels at 168 point (weekly) intervals.
month: {value: 2, name: 'Last Month', timeSpan: 30 * 24 * 3600 * 1000, month: {value: 4, name: 'Last Month', timeSpan: 30 * 24 * 3600 * 1000,
resolution: 1000 * 3600, labelEvery: 168, format: 'M/d'}, resolution: 1000 * 3600, labelEvery: 168, format: 'M/d'},
// Prior quarter (90 days), resolution of 3 hr -- at most 720 data points. // Prior quarter (90 days), resolution of 3 hr -- at most 720 data points.
// Labels at 112 point (fortnightly) intervals. // Labels at 112 point (fortnightly) intervals.
quarter: {value: 3, name: 'Last Quarter', timeSpan: 90 * 24 * 3600 * 1000, quarter: {value: 5, name: 'Last Quarter', timeSpan: 90 * 24 * 3600 * 1000,
resolution: 1000 * 3600 * 3, labelEvery: 112, format: 'M/yy'}, resolution: 1000 * 3600 * 3, labelEvery: 112, format: 'M/yy'},
}; };
/*
* Offset, in ms, by which to subtract to convert GMT to local time
* @type {number}
*/
var timezoneOffset = new Date().getTimezoneOffset() * 60000;
/** @constructor */ /** @constructor */
function PerformanceMonitor() { function PerformanceMonitor() {
this.__proto__ = PerformanceMonitor.prototype; this.__proto__ = PerformanceMonitor.prototype;
/** /**
* All metrics have entries, but those not displayed have an empty div list. * All metrics have entries, but those not displayed have an empty div list.
* If a div list is not empty, the associated data will be non-null, or * If a div list is not empty, the associated data will be non-null, or
* null but about to be filled by webui response. Thus, any metric with * null but about to be filled by webui handler response. Thus, any metric
* non-empty div list but null data is awaiting a data response from the * with non-empty div list but null data is awaiting a data response
* webui. * from the webui handler. The webui handler uses numbers to uniquely
* identify metric and event types, so we use the same numbers (in
* string form) for the map key, repeating the id number in the |id|
* field, as a number.
* @type {Object.<string, { * @type {Object.<string, {
* id: number,
* description: string,
* units: string,
* yAxis: !{max: number, color: string},
* divs: !Array.<HTMLDivElement>, * divs: !Array.<HTMLDivElement>,
* yAxis: !{max: !number, color: !string}, * data: ?Array.<{time: number, value: number}>
* data: ?Array.<{time: !number, value: !number}>,
* description: !string,
* units: !string
* }>} * }>}
* @private * @private
*/ */
this.metricMap_ = { this.metricMap_ = {};
jankiness: {
divs: [],
yAxis: {max: 100, color: 'rgb(255, 128, 128)'},
data: null,
description: 'Jankiness',
units: 'milliJanks'
},
oddness: {
divs: [],
yAxis: {max: 20, color: 'rgb(0, 192, 0)'},
data: null,
description: 'Oddness',
units: 'kOdds'
}
};
/* /*
* Similar data for events, though no yAxis info is needed since events * Similar data for events, though no yAxis info is needed since events
* are simply labelled markers at X locations. Rules regarding null data * are simply labelled markers at X locations. Rules regarding null data
* with non-empty div list apply here as for metricMap_ above. * with non-empty div list apply here as for metricMap_ above.
* @type {Object.<string, { * @type {Object.<number, {
* id: number,
* description: string,
* color: string,
* divs: !Array.<HTMLDivElement>, * divs: !Array.<HTMLDivElement>,
* description: !string, * data: ?Array.<{time: number, longDescription: string}>
* color: !string,
* data: ?Array.<{time: !number, longDescription: !string}>
* }>} * }>}
* @private * @private
*/ */
this.eventMap_ = { this.eventMap_ = {};
wampusAttacks: {
divs: [],
description: 'Wampus Attack',
color: 'rgb(0, 0, 255)',
data: null
},
solarEclipses: {
divs: [],
description: 'Solar Eclipse',
color: 'rgb(255, 0, 255)',
data: null
}
};
/** /**
* Time periods in which the browser was active and collecting metrics. * Time periods in which the browser was active and collecting metrics
* and events. * and events.
* @type {!Array.<{start: !number, end: !number}>} * @type {!Array.<{start: number, end: number}>}
* @private * @private
*/ */
this.intervals_ = []; this.intervals_ = [];
this.setupCheckboxes_(
'#chooseMetrics', this.metricMap_, this.addMetric, this.dropMetric);
this.setupCheckboxes_(
'#chooseEvents', this.eventMap_, this.addEventType, this.dropEventType);
this.setupTimeRangeChooser_(); this.setupTimeRangeChooser_();
chrome.send('getAllEventTypes');
chrome.send('getAllMetricTypes');
this.setupMainChart_(); this.setupMainChart_();
TimeRange_.day.element.click().call(this); TimeRange_.day.element.click();
} }
PerformanceMonitor.prototype = { PerformanceMonitor.prototype = {
/** /**
* Set up the radio button set to choose time range. Use div#radioTemplate * Receive a list of all metrics, and populate |this.metricMap_| to
* reflect said list. Reconfigure the checkbox set for metric selection.
* @param {Array.<{metricType: string, shortDescription: string}>}
* allMetrics All metrics from which to select.
*/
getAllMetricTypesCallback: function(allMetrics) {
for (var i = 0; i < allMetrics.length; i++) {
var metric = allMetrics[i];
this.metricMap_[metric.metricType] = {
id: metric.metricType,
description: metric.shortDescription,
units: metric.units,
yAxis: {min: 0, max: metric.maxValue,
color: jQuery.color.parse('blue')},
divs: [],
data: null
};
}
this.setupCheckboxes_($('#choose-metrics')[0],
this.metricMap_, this.addMetric, this.dropMetric);
},
/**
* Receive a list of all event types, and populate |this.eventMap_| to
* reflect said list. Reconfigure the checkbox set for event selection.
* @param {Array.<{eventType: string, shortDescription: string}>}
* allEvents All events from which to select.
*/
getAllEventTypesCallback: function(allEvents) {
for (var i = 0; i < allEvents.length; i++) {
var eventInfo = allEvents[i];
this.eventMap_[eventInfo.eventType] = {
id: eventInfo.eventType,
color: jQuery.color.parse('red'),
data: null,
description: eventInfo.shortDescription,
divs: []
};
}
this.setupCheckboxes_($('#choose-events')[0],
this.eventMap_, this.addEventType, this.dropEventType);
},
/**
* Set up the radio button set to choose time range. Use div#radio-template
* as a template. * as a template.
* @private * @private
*/ */
setupTimeRangeChooser_: function() { setupTimeRangeChooser_: function() {
var timeDiv = $('#chooseTimeRange')[0]; var timeDiv = $('#choose-time-range')[0];
var radioTemplate = $('#radioTemplate')[0]; var radioTemplate = $('#radio-template')[0];
for (var time in TimeRange_) { for (var time in TimeRange_) {
var timeRange = TimeRange_[time]; var timeRange = TimeRange_[time];
...@@ -154,37 +191,33 @@ var Installer = function() { ...@@ -154,37 +191,33 @@ var Installer = function() {
/** /**
* Generalized function for setting up checkbox blocks for either events * Generalized function for setting up checkbox blocks for either events
* or metrics. Take a div ID |divId| into which to place the checkboxes, * or metrics. Take a div |div| into which to place the checkboxes,
* and a map |optionMap| with values that each include a property * and a map |optionMap| with values that each include a property
* |description|. Set up one checkbox for each entry in |optionMap| * |description|. Set up one checkbox for each entry in |optionMap|
* labelled with that description. Arrange callbacks to function |check| * labelled with that description. Arrange callbacks to function |check|
* or |uncheck|, passing them the key of the checked or unchecked option, * or |uncheck|, passing them the key of the checked or unchecked option,
* when the relevant checkbox state changes. * when the relevant checkbox state changes.
* @param {!string} divId Id of division into which to put checkboxes * @param {!HTMLDivElement} div A <div> into which to put checkboxes.
* @param {!Object} optionMap map of metric/event entries * @param {!Object} optionMap A map of metric/event entries.
* @param {!function(this:Controller, Object)} check * @param {!function(this:Controller, Object)} check
* function to select an entry (metric or event) * The function to select an entry (metric or event).
* @param {!function(this:Controller, Object)} uncheck * @param {!function(this:Controller, Object)} uncheck
* function to deselect an entry (metric or event) * The function to deselect an entry (metric or event).
* @private * @private
*/ */
setupCheckboxes_: function(divId, optionMap, check, uncheck) { setupCheckboxes_: function(div, optionMap, check, uncheck) {
var checkboxTemplate = $('#checkboxTemplate')[0]; var checkboxTemplate = $('#checkbox-template')[0];
var chooseMetricsDiv = $(divId)[0];
for (var option in optionMap) { for (var option in optionMap) {
var checkbox = checkboxTemplate.cloneNode(true); var checkbox = checkboxTemplate.cloneNode(true);
checkbox.querySelector('span').innerText = 'Show ' + checkbox.querySelector('span').innerText = 'Show ' +
optionMap[option].description; optionMap[option].description;
chooseMetricsDiv.appendChild(checkbox); div.appendChild(checkbox);
var input = checkbox.querySelector('input'); var input = checkbox.querySelector('input');
input.option = option; input.option = optionMap[option].id;
input.addEventListener('change', function(e) { input.addEventListener('change', function(e) {
if (e.target.checked) (e.target.checked ? check : uncheck).call(this, e.target.option);
check.call(this, e.target.option);
else
uncheck.call(this, e.target.option);
}.bind(this)); }.bind(this));
} }
}, },
...@@ -206,52 +239,33 @@ var Installer = function() { ...@@ -206,52 +239,33 @@ var Installer = function() {
* Set the time range for which to display metrics and events. For * Set the time range for which to display metrics and events. For
* now, the time range always ends at "now", but future implementations * now, the time range always ends at "now", but future implementations
* may allow time ranges not so anchored. * may allow time ranges not so anchored.
* @param {!{start: !number, end: !number, resolution: !number}} range * @param {!{start: number, end: number, resolution: number}} range
* The time range for which to get display data.
*/ */
setTimeRange: function(range) { setTimeRange: function(range) {
this.range = range; this.range = range;
this.end = Math.floor(Date.now() / range.resolution) * this.end = Math.floor(Date.now() / range.resolution) *
range.resolution; range.resolution;
// Take the GMT value of this.end ("now") and subtract from it the
// number of minutes by which we lag GMT in the present timezone,
// X mS/minute. This will show time in the present timezone.
this.end -= new Date().getTimezoneOffset() * 60000;
this.start = this.end - range.timeSpan; this.start = this.end - range.timeSpan;
this.requestIntervals(); this.requestIntervals();
}, },
/**
* Return mock interval set for testing.
* @return {!Array.<{start: !number, end: !number}>} intervals
*/
getMockIntervals: function() {
var interval = this.end - this.start;
return [
{start: this.start + interval * .1,
end: this.start + interval * .2},
{start: this.start + interval * .7,
end: this.start + interval}
];
},
/** /**
* Request activity intervals in the current time range. * Request activity intervals in the current time range.
*/ */
requestIntervals: function() { requestIntervals: function() {
this.onReceiveIntervals(this.getMockIntervals()); chrome.send('getActiveIntervals', [this.start, this.end]);
// Replace with: chrome.send('getIntervals', this.start, this.end,
// this.onReceiveIntervals);
}, },
/** /**
* Webui callback delivering response from requestIntervals call. Assumes * Webui callback delivering response from requestIntervals call. Assumes
* this is a new time range choice, which results in complete refresh of * this is a new time range choice, which results in complete refresh of
* all metrics and event types that are currently selected. * all metrics and event types that are currently selected.
* @param {!Array.<{start: !number, end: !number}>} intervals new intervals * @param {!Array.<{start: number, end: number}>} intervals
* The new intervals.
*/ */
onReceiveIntervals: function(intervals) { getActiveIntervalsCallback: function(intervals) {
this.intervals_ = intervals; this.intervals_ = intervals;
for (var metric in this.metricMap_) { for (var metric in this.metricMap_) {
...@@ -263,13 +277,13 @@ var Installer = function() { ...@@ -263,13 +277,13 @@ var Installer = function() {
for (var eventType in this.eventMap_) { for (var eventType in this.eventMap_) {
var eventValue = this.eventMap_[eventType]; var eventValue = this.eventMap_[eventType];
if (eventValue.divs.length > 0) if (eventValue.divs.length > 0)
this.refreshEventType(eventType); this.refreshEventType(eventValue.id);
} }
}, },
/** /**
* Add a new metric to the main (currently only) chart. * Add a new metric to the main (currently only) chart.
* @param {!string} metric Metric to start displaying * @param {string} metric The metric to start displaying.
*/ */
addMetric: function(metric) { addMetric: function(metric) {
this.metricMap_[metric].divs.push(this.charts[0]); this.metricMap_[metric].divs.push(this.charts[0]);
...@@ -278,7 +292,7 @@ var Installer = function() { ...@@ -278,7 +292,7 @@ var Installer = function() {
/** /**
* Remove a metric from the chart(s). * Remove a metric from the chart(s).
* @param {!string} metric Metric to stop displaying * @param {string} metric The metric to stop displaying.
*/ */
dropMetric: function(metric) { dropMetric: function(metric) {
var metricValue = this.metricMap_[metric]; var metricValue = this.metricMap_[metric];
...@@ -288,62 +302,36 @@ var Installer = function() { ...@@ -288,62 +302,36 @@ var Installer = function() {
affectedCharts.forEach(this.drawChart, this); affectedCharts.forEach(this.drawChart, this);
}, },
/**
* Return mock metric data points for testing. Give values ranging from
* offset to max-offset. (This lets us avoid direct overlap of
* different mock data sets in an ugly way that will die in the next
* version anyway.)
* @param {!number} max Max data value to return, less offset
* @param {!number} offset Adjustment factor
* @return {!Array.<{time: !number, value: !number}>}
*/
getMockDataPoints: function(max, offset) {
var dataPoints = [];
for (var i = 0; i < this.intervals_.length; i++) {
// Rise from low offset to high max-offset in 100 point steps.
for (var time = this.intervals_[i].start;
time <= this.intervals_[i].end; time += this.range.resolution) {
var numPoints = time / this.range.resolution;
dataPoints.push({time: time, value: offset + (numPoints % 100) *
(max - 2 * offset) / 100});
}
}
return dataPoints;
},
/** /**
* Request new metric data, assuming the metric table and chart already * Request new metric data, assuming the metric table and chart already
* exist. * exist.
* @param {!string} metric Metric for which to get data * @param {string} metric The metric for which to get data.
*/ */
refreshMetric: function(metric) { refreshMetric: function(metric) {
var metricValue = this.metricMap_[metric]; var metricValue = this.metricMap_[metric];
metricValue.data = null; // Mark metric as awaiting response. metricValue.data = null; // Mark metric as awaiting response.
this.onReceiveMetric(metric, chrome.send('getMetric', [metricValue.id,
this.getMockDataPoints(metricValue.yAxis.max, 5)); this.start, this.end, this.range.resolution]);
// Replace with:
// chrome.send("getMetric", this.range.start, this.range.end,
// this.range.resolution, this.onReceiveMetric);
}, },
/** /**
* Receive new datapoints for |metric|, convert the data to Flot-usable * Receive new datapoints for a metric, convert the data to Flot-usable
* form, and redraw all affected charts. * form, and redraw all affected charts.
* @param {!string} metric Metric to which |points| applies * @param {!{
* @param {!Array.<{time: !number, value: !number}>} points * type: number,
* new data points * points: !Array.<{time: number, value: number}>
* }} result An object giving metric ID, and time/value point pairs for
* that id.
*/ */
onReceiveMetric: function(metric, points) { getMetricCallback: function(result) {
var metricValue = this.metricMap_[metric]; var metricValue = this.metricMap_[result.metricType];
// Might have been dropped while waiting for data. // Might have been dropped while waiting for data.
if (metricValue.divs.length == 0) if (metricValue.divs.length == 0)
return; return;
var series = []; var series = [];
metricValue.data = [series]; metricValue.data = [series]; // Data ends with current open series.
// Traverse the points, and the intervals, in parallel. Both are in // Traverse the points, and the intervals, in parallel. Both are in
// ascending time order. Create a sequence of data "series" (per Flot) // ascending time order. Create a sequence of data "series" (per Flot)
...@@ -351,9 +339,9 @@ var Installer = function() { ...@@ -351,9 +339,9 @@ var Installer = function() {
var interval = this.intervals_[0]; var interval = this.intervals_[0];
var intervalIndex = 0; var intervalIndex = 0;
var pointIndex = 0; var pointIndex = 0;
while (pointIndex < points.length && while (pointIndex < result.points.length &&
intervalIndex < this.intervals_.length) { intervalIndex < this.intervals_.length) {
var point = points[pointIndex++]; var point = result.points[pointIndex++];
while (intervalIndex < this.intervals_.length && while (intervalIndex < this.intervals_.length &&
point.time > interval.end) { point.time > interval.end) {
interval = this.intervals_[++intervalIndex]; // Jump to new interval. interval = this.intervals_[++intervalIndex]; // Jump to new interval.
...@@ -363,16 +351,16 @@ var Installer = function() { ...@@ -363,16 +351,16 @@ var Installer = function() {
} }
} }
if (intervalIndex < this.intervals_.length && if (intervalIndex < this.intervals_.length &&
point.time > interval.start) point.time > interval.start) {
series.push([point.time, point.value]); series.push([point.time - timezoneOffset, point.value]);
}
} }
metricValue.divs.forEach(this.drawChart, this); metricValue.divs.forEach(this.drawChart, this);
}, },
/** /**
* Add a new event to the chart(s). * Add a new event to the chart(s).
* @param {!string} eventType type of event to start displaying * @param {string} eventType The type of event to start displaying.
*/ */
addEventType: function(eventType) { addEventType: function(eventType) {
// Events show on all charts. // Events show on all charts.
...@@ -382,7 +370,7 @@ var Installer = function() { ...@@ -382,7 +370,7 @@ var Installer = function() {
/* /*
* Remove an event from the chart(s). * Remove an event from the chart(s).
* @param {!string} eventType type of event to stop displaying * @param {string} eventType The type of event to stop displaying.
*/ */
dropEventType: function(eventType) { dropEventType: function(eventType) {
var eventValue = this.eventMap_[eventType]; var eventValue = this.eventMap_[eventType];
...@@ -392,67 +380,46 @@ var Installer = function() { ...@@ -392,67 +380,46 @@ var Installer = function() {
affectedCharts.forEach(this.drawChart, this); affectedCharts.forEach(this.drawChart, this);
}, },
/**
* Return mock event points for testing.
* @param {!string} eventType type of event to generate mock data for
* @return {!Array.<{time: !number, longDescription: !string}>}
*/
getMockEventValues: function(eventType) {
var mockValues = [];
for (var i = 0; i < this.intervals_.length; i++) {
var interval = this.intervals_[i];
mockValues.push({
time: interval.start,
longDescription: eventType + ' at ' +
new Date(interval.start) + ' blah, blah blah'});
mockValues.push({
time: (interval.start + interval.end) / 2,
longDescription: eventType + ' at ' +
new Date((interval.start + interval.end) / 2) + ' blah, blah'});
mockValues.push({
time: interval.end,
longDescription: eventType + ' at ' + new Date(interval.end) +
' blah, blah blah'});
}
return mockValues;
},
/** /**
* Request new data for |eventType|, for times in the current range. * Request new data for |eventType|, for times in the current range.
* @param {!string} eventType type of event to get new data for * @param {string} eventType The type of event to get new data for.
*/ */
refreshEventType: function(eventType) { refreshEventType: function(eventType) {
// Mark eventType as awaiting response. // Mark eventType as awaiting response.
this.eventMap_[eventType].data = null; this.eventMap_[eventType].data = null;
this.onReceiveEventType(eventType, this.getMockEventValues(eventType));
// Replace with: chrome.send('getEvents', [eventType, this.start, this.end]);
// chrome.send("getEvents", eventType, this.range.start, this.range.end);
}, },
/** /**
* Receive new data for |eventType|. If the event has been deselected while * Receive new events for |eventType|. If |eventType| has been deselected
* awaiting webui response, do nothing. Otherwise, save the data directly, * while awaiting webui handler response, do nothing. Otherwise, save the
* since events are handled differently than metrics when drawing * data directly, since events are handled differently than metrics
* (no "series"), and redraw all the affected charts. * when drawing (no "series"), and redraw all the affected charts.
* @param {!string} eventType type of event the new data applies to * @param {!{
* @param {!Array.<{time: !number, longDescription: !string}>} values * type: number,
* new event values * points: !Array.<{time: number, longDescription: string}>
* }} result An object giving eventType ID, and time/description pairs for
* each event of that type in the range requested.
*/ */
onReceiveEventType: function(eventType, values) { getEventsCallback: function(result) {
var eventValue = this.eventMap_[eventType]; var eventValue = this.eventMap_[result.eventType];
if (eventValue.divs.length == 0) if (eventValue.divs.length == 0)
return; return;
eventValue.data = values; result.points.forEach(function(element) {
element.time -= timezoneOffset;
});
eventValue.data = result.points;
eventValue.divs.forEach(this.drawChart, this); eventValue.divs.forEach(this.drawChart, this);
}, },
/** /**
* Return an object containing an array of metrics and another of events * Return an object containing an array of metrics and another of events
* that include |chart| as one of the divs into which they display. * that include |chart| as one of the divs into which they display.
* @param {HTMLDivElement} chart div for which to get relevant items * @param {HTMLDivElement} chart The <div> for which to get relevant items.
* @return {!{metrics: !Array,<Object>, events: !Array.<Object>}} * @return {!{metrics: !Array,<Object>, events: !Array.<Object>}}
* @private * @private
*/ */
...@@ -480,10 +447,11 @@ var Installer = function() { ...@@ -480,10 +447,11 @@ var Installer = function() {
/** /**
* Check all entries in an object of the type returned from getChartData, * Check all entries in an object of the type returned from getChartData,
* above, to see if all events and metrics have completed data (none is * above, to see if all events and metrics have completed data (none is
* awaiting an asynchronous webui response to get their current data). * awaiting an asynchronous webui handler response to get their current
* data).
* @param {!{metrics: !Array,<Object>, events: !Array.<Object>}} chartData * @param {!{metrics: !Array,<Object>, events: !Array.<Object>}} chartData
* event/metric data to check for readiness * The event/metric data to check for readiness.
* @return {boolean} is data ready? * @return {boolean} Whether data is ready.
* @private * @private
*/ */
isDataReady_: function(chartData) { isDataReady_: function(chartData) {
...@@ -506,15 +474,15 @@ var Installer = function() { ...@@ -506,15 +474,15 @@ var Installer = function() {
* (not per Flot) a |description| property to each, to be used for hand * (not per Flot) a |description| property to each, to be used for hand
* creating description boxes. * creating description boxes.
* @param {!Array.<{ * @param {!Array.<{
* description: !string, * description: string,
* color: !string, * color: string,
* data: !Array.<{time: !number}> * data: !Array.<{time: number}>
* }>} eventValues events to make markings for * }>} eventValues The events to make markings for.
* @return {!Array.<{ * @return {!Array.<{
* color: !string, * color: string,
* description: !string, * description: string,
* xaxis: {from: !number, to: !number} * xaxis: {from: number, to: number}
* }>} mark data structure for Flot to use * }>} A marks data structure for Flot to use.
* @private * @private
*/ */
getEventMarks_: function(eventValues) { getEventMarks_: function(eventValues) {
...@@ -524,11 +492,17 @@ var Installer = function() { ...@@ -524,11 +492,17 @@ var Installer = function() {
var eventValue = eventValues[i]; var eventValue = eventValues[i];
for (var d = 0; d < eventValue.data.length; d++) { for (var d = 0; d < eventValue.data.length; d++) {
var point = eventValue.data[d]; var point = eventValue.data[d];
if (point.time >= this.start - timezoneOffset &&
point.time <= this.end - timezoneOffset) {
markings.push({ markings.push({
color: eventValue.color, color: eventValue.color,
description: eventValue.description, description: eventValue.description,
xaxis: {from: point.time, to: point.time} xaxis: {from: point.time, to: point.time}
}); });
} else {
console.log('Event out of time range ' + this.start + ' -> ' +
this.end + ' at: ' + point.time);
}
} }
} }
...@@ -539,7 +513,7 @@ var Installer = function() { ...@@ -539,7 +513,7 @@ var Installer = function() {
* Redraw the chart in div |chart|, *if* all its dependent data is present. * Redraw the chart in div |chart|, *if* all its dependent data is present.
* Otherwise simply return, and await another call when all data is * Otherwise simply return, and await another call when all data is
* available. * available.
* @param {HTMLDivElement} chart div to redraw * @param {HTMLDivElement} chart The <div> to redraw.
*/ */
drawChart: function(chart) { drawChart: function(chart) {
var chartData = this.getChartData_(chart); var chartData = this.getChartData_(chart);
...@@ -561,19 +535,29 @@ var Installer = function() { ...@@ -561,19 +535,29 @@ var Installer = function() {
} }
}); });
// Ensure at least one y axis, as a reference for event placement.
if (yAxes.length == 0)
yAxes.push({max: 1.0});
var markings = this.getEventMarks_(chartData.events); var markings = this.getEventMarks_(chartData.events);
var chart = this.charts[0]; var chart = this.charts[0];
var plot = $.plot(chart, seriesSeq, { var plot = $.plot(chart, seriesSeq, {
yaxes: yAxes, yaxes: yAxes,
xaxis: {mode: 'time'}, xaxis: {
mode: 'time',
min: this.start - timezoneOffset,
max: this.end - timezoneOffset
},
points: {show: true, radius: 1},
lines: {show: true},
grid: {markings: markings} grid: {markings: markings}
}); });
// For each event in |markings|, create also a label div, with left // For each event in |markings|, create also a label div, with left
// edge colinear with the event vertical-line. Top of label is // edge colinear with the event vertical line. Top of label is
// presently a hack-in, putting labels in three tiers of 25px height // presently a hack-in, putting labels in three tiers of 25px height
// each to avoid overlap. Will need something better. // each to avoid overlap. Will need something better.
var labelTemplate = $('#labelTemplate')[0]; var labelTemplate = $('#label-template')[0];
for (var i = 0; i < markings.length; i++) { for (var i = 0; i < markings.length; i++) {
var mark = markings[i]; var mark = markings[i];
var point = var point =
...@@ -582,6 +566,7 @@ var Installer = function() { ...@@ -582,6 +566,7 @@ var Installer = function() {
labelDiv.innerText = mark.description; labelDiv.innerText = mark.description;
labelDiv.style.left = point.left + 'px'; labelDiv.style.left = point.left + 'px';
labelDiv.style.top = (point.top + 25 * (i % 3)) + 'px'; labelDiv.style.top = (point.top + 25 * (i % 3)) + 'px';
chart.appendChild(labelDiv); chart.appendChild(labelDiv);
} }
} }
...@@ -589,6 +574,6 @@ var Installer = function() { ...@@ -589,6 +574,6 @@ var Installer = function() {
return { return {
PerformanceMonitor: PerformanceMonitor PerformanceMonitor: PerformanceMonitor
}; };
}(); });
var performanceMonitor = new Installer.PerformanceMonitor(); var PerformanceMonitor = new performance_monitor.PerformanceMonitor();
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h" #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/browser/ui/webui/omnibox/omnibox_ui.h" #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
#include "chrome/browser/ui/webui/options2/options_ui.h" #include "chrome/browser/ui/webui/options2/options_ui.h"
#include "chrome/browser/ui/webui/performance_monitor/web_ui.h"
#include "chrome/browser/ui/webui/plugins_ui.h" #include "chrome/browser/ui/webui/plugins_ui.h"
#include "chrome/browser/ui/webui/policy_ui.h" #include "chrome/browser/ui/webui/policy_ui.h"
#include "chrome/browser/ui/webui/predictors/predictors_ui.h" #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
...@@ -263,6 +264,9 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, ...@@ -263,6 +264,9 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
// Android does not support plugins for now. // Android does not support plugins for now.
if (url.host() == chrome::kChromeUIPluginsHost) if (url.host() == chrome::kChromeUIPluginsHost)
return &NewWebUI<PluginsUI>; return &NewWebUI<PluginsUI>;
// Performance monitoring page is not on Android for now.
if (url.host() == chrome::kChromeUIPerformanceMonitorHost)
return &NewWebUI<performance_monitor::WebUI>;
#endif #endif
#if defined(ENABLE_EXTENSIONS) #if defined(ENABLE_EXTENSIONS)
if (url.host() == chrome::kChromeUIExtensionsFrameHost) if (url.host() == chrome::kChromeUIExtensionsFrameHost)
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/performance_monitor/web_ui.h"
#include "base/values.h"
#include "chrome/browser/performance_monitor/performance_monitor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/chrome_web_ui_data_source.h"
#include "chrome/browser/ui/webui/performance_monitor/web_ui_handler.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/web_ui.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
namespace {
ChromeWebUIDataSource* CreateWebUIHTMLSource() {
ChromeWebUIDataSource* source =
new ChromeWebUIDataSource(chrome::kChromeUIPerformanceMonitorHost);
source->add_resource_path("chart.css", IDR_PERFORMANCE_MONITOR_CHART_CSS);
source->add_resource_path("chart.js", IDR_PERFORMANCE_MONITOR_CHART_JS);
source->add_resource_path("jquery.js", IDR_PERFORMANCE_MONITOR_JQUERY_JS);
source->add_resource_path("flot.js", IDR_PERFORMANCE_MONITOR_JQUERY_FLOT_JS);
source->set_default_resource(IDR_PERFORMANCE_MONITOR_HTML);
return source;
}
} // namespace
namespace performance_monitor {
WebUI::WebUI(content::WebUI* web_ui) : WebUIController(web_ui) {
web_ui->AddMessageHandler(new performance_monitor::WebUIHandler());
ChromeWebUIDataSource* html_source = CreateWebUIHTMLSource();
Profile* profile = Profile::FromWebUI(web_ui);
ChromeURLDataManager::AddDataSource(profile, html_source);
}
} // namespace performance_monitor
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_PERFORMANCE_MONITOR_WEB_UI_H_
#define CHROME_BROWSER_UI_WEBUI_PERFORMANCE_MONITOR_WEB_UI_H_
#include "content/public/browser/web_ui_controller.h"
namespace performance_monitor {
class WebUI : public content::WebUIController {
public:
explicit WebUI(content::WebUI* web_ui);
private:
DISALLOW_COPY_AND_ASSIGN(WebUI);
};
} // namespace performance_monitor
#endif // CHROME_BROWSER_UI_WEBUI_PERFORMANCE_MONITOR_WEB_UI_H_
...@@ -4221,6 +4221,8 @@ ...@@ -4221,6 +4221,8 @@
'browser/ui/webui/options2/startup_pages_handler.h', 'browser/ui/webui/options2/startup_pages_handler.h',
'browser/ui/webui/options2/web_intents_settings_handler.cc', 'browser/ui/webui/options2/web_intents_settings_handler.cc',
'browser/ui/webui/options2/web_intents_settings_handler.h', 'browser/ui/webui/options2/web_intents_settings_handler.h',
'browser/ui/webui/performance_monitor/web_ui.cc',
'browser/ui/webui/performance_monitor/web_ui.h',
'browser/ui/webui/performance_monitor/web_ui_handler.cc', 'browser/ui/webui/performance_monitor/web_ui_handler.cc',
'browser/ui/webui/performance_monitor/web_ui_handler.h', 'browser/ui/webui/performance_monitor/web_ui_handler.h',
'browser/ui/webui/plugins_ui.cc', 'browser/ui/webui/plugins_ui.cc',
...@@ -5035,6 +5037,8 @@ ...@@ -5035,6 +5037,8 @@
'browser/ui/webui/ntp/app_launcher_handler.cc', 'browser/ui/webui/ntp/app_launcher_handler.cc',
'browser/ui/webui/ntp/app_launcher_handler.h', 'browser/ui/webui/ntp/app_launcher_handler.h',
'browser/ui/webui/ntp/ntp_resource_cache.cc', 'browser/ui/webui/ntp/ntp_resource_cache.cc',
'browser/ui/webui/performance_monitor/web_ui.cc',
'browser/ui/webui/performance_monitor/web_ui_handler.cc',
'browser/ui/webui/plugins_ui.cc', 'browser/ui/webui/plugins_ui.cc',
'browser/ui/webui/plugins_ui.h', 'browser/ui/webui/plugins_ui.h',
'browser/ui/window_sizer/window_sizer.cc', 'browser/ui/window_sizer/window_sizer.cc',
......
...@@ -62,6 +62,7 @@ const char kChromeUINetInternalsURL[] = "chrome://net-internals/"; ...@@ -62,6 +62,7 @@ const char kChromeUINetInternalsURL[] = "chrome://net-internals/";
const char kChromeUINewProfile[] = "chrome://newprofile/"; const char kChromeUINewProfile[] = "chrome://newprofile/";
const char kChromeUINewTabURL[] = "chrome://newtab/"; const char kChromeUINewTabURL[] = "chrome://newtab/";
const char kChromeUIOmniboxURL[] = "chrome://omnibox/"; const char kChromeUIOmniboxURL[] = "chrome://omnibox/";
const char kChromeUIPerformanceMonitorURL[] = "chrome://performance/";
const char kChromeUIPluginsURL[] = "chrome://plugins/"; const char kChromeUIPluginsURL[] = "chrome://plugins/";
const char kChromeUIPolicyURL[] = "chrome://policy/"; const char kChromeUIPolicyURL[] = "chrome://policy/";
const char kChromeUIPrintURL[] = "chrome://print/"; const char kChromeUIPrintURL[] = "chrome://print/";
...@@ -182,6 +183,7 @@ const char kChromeUINaClHost[] = "nacl"; ...@@ -182,6 +183,7 @@ const char kChromeUINaClHost[] = "nacl";
const char kChromeUINetInternalsHost[] = "net-internals"; const char kChromeUINetInternalsHost[] = "net-internals";
const char kChromeUINewTabHost[] = "newtab"; const char kChromeUINewTabHost[] = "newtab";
const char kChromeUIOmniboxHost[] = "omnibox"; const char kChromeUIOmniboxHost[] = "omnibox";
const char kChromeUIPerformanceMonitorHost[] = "performance";
const char kChromeUIPluginsHost[] = "plugins"; const char kChromeUIPluginsHost[] = "plugins";
const char kChromeUIPolicyHost[] = "policy"; const char kChromeUIPolicyHost[] = "policy";
const char kChromeUIPredictorsHost[] = "predictors"; const char kChromeUIPredictorsHost[] = "predictors";
......
...@@ -55,6 +55,7 @@ extern const char kChromeUINetInternalsURL[]; ...@@ -55,6 +55,7 @@ extern const char kChromeUINetInternalsURL[];
extern const char kChromeUINewProfile[]; extern const char kChromeUINewProfile[];
extern const char kChromeUINewTabURL[]; extern const char kChromeUINewTabURL[];
extern const char kChromeUIOmniboxURL[]; extern const char kChromeUIOmniboxURL[];
extern const char kChromeUIPerformanceMonitorURL[];
extern const char kChromeUIPluginsURL[]; extern const char kChromeUIPluginsURL[];
extern const char kChromeUIPolicyURL[]; extern const char kChromeUIPolicyURL[];
extern const char kChromeUIPrintURL[]; extern const char kChromeUIPrintURL[];
...@@ -171,6 +172,7 @@ extern const char kChromeUINaClHost[]; ...@@ -171,6 +172,7 @@ extern const char kChromeUINaClHost[];
extern const char kChromeUINetInternalsHost[]; extern const char kChromeUINetInternalsHost[];
extern const char kChromeUINewTabHost[]; extern const char kChromeUINewTabHost[];
extern const char kChromeUIOmniboxHost[]; extern const char kChromeUIOmniboxHost[];
extern const char kChromeUIPerformanceMonitorHost[];
extern const char kChromeUIPluginsHost[]; extern const char kChromeUIPluginsHost[];
extern const char kChromeUIPolicyHost[]; extern const char kChromeUIPolicyHost[];
extern const char kChromeUIPredictorsHost[]; extern const char kChromeUIPredictorsHost[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment