diff options
author | mtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-05 14:19:50 +0000 |
---|---|---|
committer | mtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-05 14:19:50 +0000 |
commit | 1edbb1dbeae78ac3d7ef9b6f15b6e0bcf79b38a2 (patch) | |
tree | 04c35e6ff7d31fdd62dc05e6c1b94a6b584c46d7 | |
parent | 291abdbf8046c8548737687f0004153bfb12b329 (diff) | |
download | chromium_src-1edbb1dbeae78ac3d7ef9b6f15b6e0bcf79b38a2.zip chromium_src-1edbb1dbeae78ac3d7ef9b6f15b6e0bcf79b38a2.tar.gz chromium_src-1edbb1dbeae78ac3d7ef9b6f15b6e0bcf79b38a2.tar.bz2 |
[fsp] Introduce a request timeline to chrome://provided-file-systems.
This CL introduces a simple timeline to see request activities in time on a
two dimensional chart.
TEST=Tested manually with an experimental extension.
BUG=376095
Review URL: https://codereview.chromium.org/310303003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275091 0039d316-1c4b-4281-b951-d872f2087c98
3 files changed, 301 insertions, 6 deletions
diff --git a/chrome/browser/resources/chromeos/provided_file_systems.css b/chrome/browser/resources/chromeos/provided_file_systems.css index b049d4a1..63b99f1 100644 --- a/chrome/browser/resources/chromeos/provided_file_systems.css +++ b/chrome/browser/resources/chromeos/provided_file_systems.css @@ -11,7 +11,8 @@ body { } #fileSystems, -#requestEvents { +#requestEvents, +#requestTimeline { background-color: white; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); margin-bottom: 20px; @@ -67,3 +68,69 @@ body { #requestEvents td { border-top: 1px solid #eee; } + +#requestEvents td span { + border-radius: 5px; + padding: 3px; +} + +#requestTimeline { + overflow: auto; + width: 100%; +} + +#requestTimeline .request-timeline-padding { + margin: 10px 5px; + position: relative; +} + +#requestTimeline .request-timeline-request { + border: none; + border-radius: 4px; + height: 8px; + margin: 3px 0; + min-width: 8px; + opacity: 0.75; + position: absolute; + transition: opacity 100ms; +} + +#requestTimeline [data-request-type='REQUEST_UNMOUNT'], +#requestEvents [data-request-type='REQUEST_UNMOUNT'] span { + background-color: cadetblue; + color: white; +} + +#requestTimeline [data-request-type='GET_METADATA'], +#requestEvents [data-request-type='GET_METADATA'] span { + background-color: gold; +} + +#requestTimeline [data-request-type='READ_DIRECTORY'], +#requestEvents [data-request-type='READ_DIRECTORY'] span { + background-color: hotpink; + color: white; +} + +#requestTimeline [data-request-type='OPEN_FILE'], +#requestEvents [data-request-type='OPEN_FILE'] span { + background-color: darkturquoise; + color: white; +} + +#requestTimeline [data-request-type='CLOSE_FILE'], +#requestEvents [data-request-type='CLOSE_FILE'] span { + background-color: mediumspringgreen; +} + +#requestTimeline [data-request-type='READ_FILE'], +#requestEvents [data-request-type='READ_FILE'] span { + background-color: royalblue; + color: white; +} + +#requestTimeline [data-state='rejected'], +#requestEvents [data-error]:not([data-error='']) span { + background-color: tomato; + color: white; +} diff --git a/chrome/browser/resources/chromeos/provided_file_systems.html b/chrome/browser/resources/chromeos/provided_file_systems.html index d7add37..7e2dd47 100644 --- a/chrome/browser/resources/chromeos/provided_file_systems.html +++ b/chrome/browser/resources/chromeos/provided_file_systems.html @@ -46,6 +46,29 @@ </template> </polymer-element> + <polymer-element name="request-timeline"> + <template> + <link rel="stylesheet" + href="chrome://provided-file-systems/provided_file_systems.css"> + <div id="requestTimeline"> + <div class="request-timeline-padding" + style="min-height: {{rows.length * rowHeight}}px"> + <template id="request-timeline-request" + repeat="{{request in chart}}"> + <div class="request-timeline-request" + title="{{request.id}}" + data-state="{{request.state}}" + data-request-type="{{request.requestType}}" + style="left: {{request.left * scale}}px; + top: {{request.row * rowHeight}}px; + width: {{request.length * scale}}px"> + </div> + </template> + </div> + </div> + </template> + </polymer-element> + <polymer-element name="request-events"> <template> <link rel="stylesheet" @@ -65,11 +88,18 @@ <tbody> <template id="request-event" repeat="{{item in model}}"> <tr> - <td>{{item.time | formatTime}}</td> + <td> + <a id="request-{{item.id}}"></a> + {{item.time | formatTime}} + </td> <td>{{item.id}}</td> - <td>{{item.requestType}}</td> + <td data-request-type="{{item.requestType}}"> + <span>{{item.requestType}}</span> + </td> <td>{{item.eventType}}</td> - <td>{{item.error}}</td> + <td data-error="{{item.error}}"> + <span>{{item.error}}</span> + </td> <td>{{item.hasMore | formatHasMore}}</td> </tr> </template> @@ -82,6 +112,9 @@ <file-systems id="file-systems"> </file-systems> + <request-timeline hidden id="request-timeline"> + </request-timeline> + <request-events hidden id="request-events"> </request-events> </body> diff --git a/chrome/browser/resources/chromeos/provided_file_systems.js b/chrome/browser/resources/chromeos/provided_file_systems.js index 712f859..5e81155 100644 --- a/chrome/browser/resources/chromeos/provided_file_systems.js +++ b/chrome/browser/resources/chromeos/provided_file_systems.js @@ -24,7 +24,10 @@ Polymer('file-systems', { requestEventsNode.hidden = false; requestEventsNode.model = []; - console.log(sender.dataset.extensionId, sender.dataset.id); + var requestTimelineNode = document.querySelector('#request-timeline'); + requestTimelineNode.hidden = false; + requestTimelineNode.model = []; + chrome.send('selectFileSystem', [sender.dataset.extensionId, sender.dataset.id]); }, @@ -75,6 +78,194 @@ Polymer('request-events', { model: [] }); +// Defines the request-timeline element. +Polymer('request-timeline', { + /** + * Observes changes in the model. + * @type {Object.<string, string>} + */ + observe: { + 'model.length': 'chartUpdate' + }, + + /** + * Called when the element is created. + */ + ready: function() { + // Update active requests in the background for nice animation. + var activeUpdateAnimation = function() { + this.activeUpdate(); + requestAnimationFrame(activeUpdateAnimation); + }.bind(this); + activeUpdateAnimation(); + }, + + /** + * Updates chart elements of active requests, so they grow with time. + */ + activeUpdate: function() { + if (Object.keys(this.active).length == 0) + return; + + for (var id in this.active) { + var index = this.active[id]; + this.chart[index].length = Date.now() - this.chart[index].time; + } + }, + + /** + * Generates <code>chart</code> from the new <code>model</code> value. + */ + chartUpdate: function(oldLength, newLength) { + // If the new value is empty, then clear the model. + if (!newLength) { + this.active = {}; + this.rows = []; + this.chart = []; + this.timeStart = null; + this.idleStart = null; + this.idleTotal = 0; + return; + } + + // Only adding new entries to the model is supported (or clearing). + console.assert(newLength >= oldLength); + + for (var i = oldLength; i < newLength; i++) { + var event = this.model[i]; + switch (event.eventType) { + case 'created': + // If this is the first creation event in the chart, then store its + // time as beginning time of the chart. + if (!this.timeStart) + this.timeStart = event.time; + + // If this event terminates idling, then add the idling time to total + // idling time. This is used to avoid gaps in the chart while idling. + if (Object.keys(this.active).length == 0 && this.idleStart) + this.idleTotal += event.time.getTime() - this.idleStart.getTime(); + + // Find the appropriate row for this chart element. + var rowIndex = 0; + while (true) { + // Add to this row only if there is enough space, and if the row + // is of the same type. + var addToRow = (rowIndex >= this.rows.length) || + (this.rows[rowIndex].time.getTime() <= event.time.getTime() && + !this.rows[rowIndex].active && + (this.rows[rowIndex].requestType == event.requestType)); + + if (addToRow) { + this.chart.push({ + index: this.chart.length, + id: event.id, + time: event.time, + requestType: event.requestType, + left: event.time - this.timeStart - this.idleTotal, + row: rowIndex, + modelIndexes: [i] + }); + + this.rows[rowIndex] = { + requestType: event.requestType, + time: event.time, + active: true + }; + + this.active[event.id] = this.chart.length - 1; + break; + } + + rowIndex++; + } + break; + + case 'fulfilled': + case 'rejected': + if (!(event.id in this.active)) + return; + var chartIndex = this.active[event.id]; + this.chart[chartIndex].state = event.eventType; + this.chart[chartIndex].modelIndexes.push(i); + break; + + case 'destroyed': + if (!(event.id in this.active)) + return; + + var chartIndex = this.active[event.id]; + this.chart[chartIndex].length = + event.time - this.chart[chartIndex].time; + this.chart[chartIndex].modelIndexes.push(i); + this.rows[this.chart[chartIndex].row].time = event.time; + this.rows[this.chart[chartIndex].row].active = false; + delete this.active[event.id]; + + // If this was the last active request, then idling starts. + if (Object.keys(this.active).length == 0) + this.idleStart = event.time; + break; + } + } + }, + + /** + * Map of requests which has started, but are not completed yet, from + * a request id to the chart element index. + * @type {Object.<number, number>}} + */ + active: {}, + + /** + * List of chart elements, calculated from the model. + * @type {Array.<Object>} + */ + chart: [], + + /** + * List of rows in the chart, with the last endTime value on it. + * @type {Array.<Object>} + */ + rows: [], + + /** + * Scale of the chart. + * @type {number} + */ + scale: 0.1, + + /** + * Height of each row in the chart in pixels. + * @type {number} + */ + rowHeight: 14, + + /** + * Time of the first created request. + * @type {Date} + */ + timeStart: null, + + /** + * Time of the last idling started. + * @type {Date} + */ + idleStart: null, + + /** + * Total idling time since chart generation started. Used to avoid + * generating gaps in the chart when there is no activity. In milliseconds. + * @type {number} + */ + idleTotal: 0, + + /** + * List of requests information maps. + * @type {Array.<Object>} + */ + model: [] +}); + /* * Updates the mounted file system list. * @param {Array.<Object>} fileSystems Array containing provided file system @@ -90,8 +281,12 @@ function updateFileSystems(fileSystems) { * @param {Object} event Event. */ function onRequestEvent(event) { - var requestEventsNode = document.querySelector('#request-events'); event.time = new Date(event.time); // Convert to a real Date object. + + var requestTimelineNode = document.querySelector('#request-timeline'); + requestTimelineNode.model.push(event); + + var requestEventsNode = document.querySelector('#request-events'); requestEventsNode.model.push(event); } |