Diff
Modified: trunk/Source/WebCore/ChangeLog (133914 => 133915)
--- trunk/Source/WebCore/ChangeLog 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/ChangeLog 2012-11-08 18:24:07 UTC (rev 133915)
@@ -1,3 +1,41 @@
+2012-11-08 Andrey Kosyakov <ca...@chromium.org>
+
+ Web Inspector: show statistics over selected frame range in Timeline's Frame mode
+ https://bugs.webkit.org/show_bug.cgi?id=101593
+
+ Reviewed by Pavel Feldman.
+
+ - change status bar records counter wording to "N of M frames|records shown" depending on mode;
+ - append average frame length and & stddev in frame mode;
+ - expand the above to a popover that includes frame count, range duration and min/avg/max/stddev on frame length;
+ - show frame bars & dividers iff selection range includes < 30 frames (drive-by)
+
+ * English.lproj/localizedStrings.js:
+ * inspector/front-end/TimelineFrameController.js:
+ (WebInspector.FrameStatistics):
+ * inspector/front-end/TimelineModel.js:
+ (WebInspector.TimelineModel.aggregateTimeByCategory):
+ * inspector/front-end/TimelinePanel.js:
+ (WebInspector.TimelinePanel.prototype.get statusBarItems):
+ (WebInspector.TimelinePanel.prototype._createStatusBarItems.getAnchor):
+ (WebInspector.TimelinePanel.prototype._createStatusBarItems):
+ (WebInspector.TimelinePanel.prototype._updateRecordsCounter):
+ (WebInspector.TimelinePanel.prototype._updateFrameStatistics):
+ (WebInspector.TimelinePanel.prototype._showFrameStatistics):
+ (WebInspector.TimelinePanel.prototype._updateFrameBars):
+ (WebInspector.TimelinePanel.prototype._overviewModeChanged.set if):
+ (WebInspector.TimelinePanel.prototype._overviewModeChanged):
+ (WebInspector.TimelinePanel.prototype._refresh):
+ * inspector/front-end/TimelinePresentationModel.js:
+ (WebInspector.TimelinePresentationModel.prototype.):
+ (WebInspector.TimelinePresentationModel.prototype.compareEndTime):
+ (WebInspector.TimelinePresentationModel.prototype.filteredFrames):
+ (WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics):
+ * inspector/front-end/timelinePanel.css:
+ (.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity):
+ (.timeline-records-stats):
+ (.timeline-frames-stats):
+
2012-11-08 Jakob Petsovits <jpetsov...@rim.com>
[BlackBerry] Rework the API to use document coordinates
Modified: trunk/Source/WebCore/English.lproj/localizedStrings.js (133914 => 133915)
--- trunk/Source/WebCore/English.lproj/localizedStrings.js 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/English.lproj/localizedStrings.js 2012-11-08 18:24:07 UTC (rev 133915)
@@ -25,7 +25,9 @@
localizedStrings["%d errors, %d warning"] = "%d errors, %d warning";
localizedStrings["%d errors, %d warnings"] = "%d errors, %d warnings";
localizedStrings["%d of %d"] = "%d of %d";
-localizedStrings["%d of %d captured records are visible"] = "%d of %d captured records are visible";
+localizedStrings["%d of %d records shown"] = "%d of %d records shown";
+localizedStrings["%d of %d frames shown"] = "%d of %d frames shown";
+localizedStrings["avg: %s, \u03c3: %s"] = "avg: %s, \u03c3: %s";
localizedStrings["%d warning"] = "%d warning";
localizedStrings["%d warnings"] = "%d warnings";
localizedStrings["%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)"] = "%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)";
@@ -797,3 +799,11 @@
localizedStrings["Detached from the target"] = "Detached from the target";
localizedStrings["Remote debugging has been terminated with reason: "] = "Remote debugging has been terminated with reason: ";
localizedStrings["Please re-attach to the new target."] = "Please re-attach to the new target.";
+localizedStrings["%s (%.0f FPS)"] = "%s (%.0f FPS)";
+localizedStrings["Selected Range"] = "Selected Range";
+localizedStrings["%s\u2013%s (%d frames)"] = "%s\u2013%s (%d frames)";
+localizedStrings["Minimum Time"] = "Minimum Time";
+localizedStrings["Average Time"] = "Average Time";
+localizedStrings["Maximum Time"] = "Maximum Time";
+localizedStrings["Standard Deviation"] = "Standard Deviation";
+localizedStrings["Time by category"] = "Time by category";
Modified: trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js (133914 => 133915)
--- trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js 2012-11-08 18:24:07 UTC (rev 133915)
@@ -107,7 +107,36 @@
/**
* @constructor
+ * @param {Array.<WebInspector.TimelineFrame>} frames
*/
+WebInspector.FrameStatistics = function(frames)
+{
+ this.frameCount = frames.length;
+ this.minDuration = Infinity;
+ this.maxDuration = 0;
+ this.timeByCategory = {};
+ this.startOffset = frames[0].startTimeOffset;
+ var lastFrame = frames[this.frameCount - 1];
+ this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
+
+ var totalDuration = 0;
+ var sumOfSquares = 0;
+ for (var i = 0; i < this.frameCount; ++i) {
+ var duration = frames[i].duration;
+ totalDuration += duration;
+ sumOfSquares += duration * duration;
+ this.minDuration = Math.min(this.minDuration, duration);
+ this.maxDuration = Math.max(this.maxDuration, duration);
+ WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
+ }
+ this.average = totalDuration / this.frameCount;
+ var variance = sumOfSquares / this.frameCount - this.average * this.average;
+ this.stddev = Math.sqrt(variance);
+}
+
+/**
+ * @constructor
+ */
WebInspector.TimelineFrame = function()
{
this.timeByCategory = {};
Modified: trunk/Source/WebCore/inspector/front-end/TimelineModel.js (133914 => 133915)
--- trunk/Source/WebCore/inspector/front-end/TimelineModel.js 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelineModel.js 2012-11-08 18:24:07 UTC (rev 133915)
@@ -129,6 +129,16 @@
total[categoryName] = (total[categoryName] || 0) + ownTime;
}
+/**
+ * @param {Object} total
+ * @param {Object} addend
+ */
+WebInspector.TimelineModel.aggregateTimeByCategory = function(total, addend)
+{
+ for (var category in addend)
+ total[category] = (total[category] || 0) + addend[category];
+}
+
WebInspector.TimelineModel.prototype = {
startRecord: function()
{
Modified: trunk/Source/WebCore/inspector/front-end/TimelinePanel.js (133914 => 133915)
--- trunk/Source/WebCore/inspector/front-end/TimelinePanel.js 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelinePanel.js 2012-11-08 18:24:07 UTC (rev 133915)
@@ -206,7 +206,8 @@
{
return this._statusBarButtons.select("element").concat([
this._miscStatusBarItems,
- this.recordsCounter
+ this.recordsCounter,
+ this.frameStatistics
]);
},
@@ -254,7 +255,15 @@
}
this.recordsCounter = document.createElement("span");
- this.recordsCounter.className = "timeline-records-counter";
+ this.recordsCounter.className = "timeline-records-stats";
+
+ this.frameStatistics = document.createElement("span");
+ this.frameStatistics.className = "timeline-records-stats hidden";
+ function getAnchor()
+ {
+ return this.frameStatistics;
+ }
+ this._frameStatisticsPopoverHelper = new WebInspector.PopoverHelper(this.frameStatistics, getAnchor.bind(this), this._showFrameStatistics.bind(this));
},
_createTimelineCategoryStatusBarCheckbox: function(category, onCheckboxClicked)
@@ -392,9 +401,34 @@
_updateRecordsCounter: function(recordsInWindowCount)
{
- this.recordsCounter.textContent = WebInspector.UIString("%d of %d captured records are visible", recordsInWindowCount, this._allRecordsCount);
+ this.recordsCounter.textContent = WebInspector.UIString("%d of %d records shown", recordsInWindowCount, this._allRecordsCount);
},
+ _updateFrameStatistics: function(frames)
+ {
+ if (frames.length) {
+ this._lastFrameStatistics = new WebInspector.FrameStatistics(frames);
+ var details = WebInspector.UIString("avg: %s, \u03c3: %s",
+ Number.secondsToString(this._lastFrameStatistics.average, true), Number.secondsToString(this._lastFrameStatistics.stddev, true));
+ } else
+ this._lastFrameStatistics = null;
+ this.frameStatistics.textContent = WebInspector.UIString("%d of %d frames shown", frames.length, this._presentationModel.frames().length);
+ if (details) {
+ this.frameStatistics.appendChild(document.createTextNode(" ("));
+ this.frameStatistics.createChild("span", "timeline-frames-stats").textContent = details;
+ this.frameStatistics.appendChild(document.createTextNode(")"));
+ }
+ },
+
+ /**
+ * @param {Element} anchor
+ * @param {WebInspector.Popover} popover
+ */
+ _showFrameStatistics: function(anchor, popover)
+ {
+ popover.show(WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics(this._lastFrameStatistics), anchor);
+ },
+
_updateEventDividers: function()
{
this._timelineGrid.removeEventDividers();
@@ -414,14 +448,8 @@
this._timelineGrid.addEventDividers(dividers);
},
- _shouldShowFrames: function()
+ _updateFrameBars: function(frames)
{
- return this._frameMode && this._presentationModel.frames().length > 0 && this.calculator.boundarySpan() < 1.0;
- },
-
- _updateFrames: function()
- {
- var frames = this._presentationModel.frames();
var clientWidth = this._graphRowsElementWidth;
if (this._frameContainer)
this._frameContainer.removeChildren();
@@ -440,8 +468,6 @@
var frame = frames[i];
var frameStart = this._calculator.computePosition(frame.startTime);
var frameEnd = this._calculator.computePosition(frame.endTime);
- if (frameEnd <= 0 || frameStart >= clientWidth)
- continue;
var frameStrip = document.createElement("div");
frameStrip.className = "timeline-frame-strip";
@@ -488,11 +514,15 @@
if (frameMode) {
this.element.addStyleClass("timeline-frame-overview");
+ this.recordsCounter.addStyleClass("hidden");
+ this.frameStatistics.removeStyleClass("hidden");
this._frameController = new WebInspector.TimelineFrameController(this._model, this._overviewPane, this._presentationModel);
} else {
this._frameController.dispose();
this._frameController = null;
this.element.removeStyleClass("timeline-frame-overview");
+ this.recordsCounter.removeStyleClass("hidden");
+ this.frameStatistics.addStyleClass("hidden");
}
}
if (shouldShowMemory === this._memoryStatistics.visible())
@@ -707,14 +737,19 @@
var recordsInWindowCount = this._refreshRecords();
this._updateRecordsCounter(recordsInWindowCount);
- if(!this._boundariesAreValid) {
+ if (!this._boundariesAreValid) {
this._updateEventDividers();
- if (this._shouldShowFrames()) {
- this._timelineGrid.removeDividers();
- this._updateFrames();
- } else {
+ var frames = this._frameController && this._presentationModel.filteredFrames(this._overviewPane.windowStartTime(), this._overviewPane.windowEndTime());
+ if (frames) {
+ this._updateFrameStatistics(frames);
+ const maxFramesForFrameBars = 30;
+ if (frames.length && frames.length < maxFramesForFrameBars) {
+ this._timelineGrid.removeDividers();
+ this._updateFrameBars(frames);
+ } else
+ this._timelineGrid.updateDividers(this._calculator);
+ } else
this._timelineGrid.updateDividers(this._calculator);
- }
if (this._mainThreadMonitoringEnabled)
this._refreshMainThreadBars();
}
Modified: trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js (133914 => 133915)
--- trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/TimelinePresentationModel.js 2012-11-08 18:24:07 UTC (rev 133915)
@@ -434,6 +434,23 @@
return recordsInWindow;
},
+ filteredFrames: function(startTime, endTime)
+ {
+ function compareStartTime(value, object)
+ {
+ return value - object.startTime;
+ }
+ function compareEndTime(value, object)
+ {
+ return value - object.endTime;
+ }
+ var firstFrame = insertionIndexForObjectInListSortedByFunction(startTime, this._frames, compareStartTime);
+ var lastFrame = insertionIndexForObjectInListSortedByFunction(endTime, this._frames, compareEndTime);
+ while (lastFrame < this._frames.length && this._frames[lastFrame].endTime <= endTime)
+ ++lastFrame;
+ return this._frames.slice(firstFrame, lastFrame);
+ },
+
isVisible: function(record)
{
for (var i = 0; i < this._filters.length; ++i) {
@@ -1127,6 +1144,33 @@
}
/**
+ * @param {WebInspector.FrameStatistics} statistics
+ */
+WebInspector.TimelinePresentationModel.generatePopupContentForFrameStatistics = function(statistics)
+{
+ /**
+ * @param {number} time
+ */
+ function formatTimeAndFPS(time)
+ {
+ return WebInspector.UIString("%s (%.0f FPS)", Number.secondsToString(time, true), 1 / time);
+ }
+
+ var contentHelper = new WebInspector.TimelinePresentationModel.PopupContentHelper(WebInspector.UIString("Selected Range"));
+
+ contentHelper._appendTextRow(WebInspector.UIString("Selected range"), WebInspector.UIString("%s\u2013%s (%d frames)",
+ Number.secondsToString(statistics.startOffset, true), Number.secondsToString(statistics.endOffset, true), statistics.frameCount));
+ contentHelper._appendTextRow(WebInspector.UIString("Minimum Time"), formatTimeAndFPS(statistics.minDuration));
+ contentHelper._appendTextRow(WebInspector.UIString("Average Time"), formatTimeAndFPS(statistics.average));
+ contentHelper._appendTextRow(WebInspector.UIString("Maximum Time"), formatTimeAndFPS(statistics.maxDuration));
+ contentHelper._appendTextRow(WebInspector.UIString("Standard Deviation"), Number.secondsToString(statistics.stddev, true));
+ contentHelper._appendElementRow(WebInspector.UIString("Time by category"),
+ WebInspector.TimelinePresentationModel._generateAggregatedInfo(statistics.timeByCategory));
+
+ return contentHelper._contentTable;
+}
+
+/**
* @param {CanvasRenderingContext2D} context
* @param {number} width
* @param {number} height
Modified: trunk/Source/WebCore/inspector/front-end/timelinePanel.css (133914 => 133915)
--- trunk/Source/WebCore/inspector/front-end/timelinePanel.css 2012-11-08 18:21:23 UTC (rev 133914)
+++ trunk/Source/WebCore/inspector/front-end/timelinePanel.css 2012-11-08 18:24:07 UTC (rev 133915)
@@ -429,17 +429,23 @@
background-color: rgb(66, 129, 235) !important;
}
-.timeline-records-counter, .storage-application-cache-status, .storage-application-cache-connectivity {
+.timeline-records-stats, .storage-application-cache-status, .storage-application-cache-connectivity {
font-size: 11px;
text-shadow: white 0 1px 0;
}
-.timeline-records-counter {
+.timeline-records-stats {
float: right;
margin-top: 5px;
margin-right: 6px;
}
+.timeline-frames-stats {
+ pointer-events: auto;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
#resources-container-content {
overflow: visible;
min-height: 100%;