diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePresentationModel.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePresentationModel.js | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePresentationModel.js b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePresentationModel.js new file mode 100644 index 00000000000..b5b23bcd054 --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePresentationModel.js @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2012 Intel Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @constructor + * @extends {WebInspector.Object} + * @param {!WebInspector.TimelineModel} model + * @param {!WebInspector.TimelineUIUtils} uiUtils + */ +WebInspector.TimelinePresentationModel = function(model, uiUtils) +{ + this._model = model; + this._uiUtils = uiUtils; + this._filters = []; + /** + * @type {!Map.<!WebInspector.TimelineModel.Record, !WebInspector.TimelinePresentationModel.Record>} + */ + this._recordToPresentationRecord = new Map(); + this.reset(); +} + +WebInspector.TimelinePresentationModel.prototype = { + /** + * @param {number} startTime + * @param {number} endTime + */ + setWindowTimes: function(startTime, endTime) + { + this._windowStartTime = startTime; + this._windowEndTime = endTime; + }, + + /** + * @param {?WebInspector.TimelineModel.Record} record + * @return {?WebInspector.TimelinePresentationModel.Record} + */ + toPresentationRecord: function(record) + { + return record ? this._recordToPresentationRecord.get(record) || null : null; + }, + + /** + * @return {!WebInspector.TimelinePresentationModel.Record} + */ + rootRecord: function() + { + return this._rootRecord; + }, + + reset: function() + { + this._recordToPresentationRecord.clear(); + this._rootRecord = new WebInspector.TimelinePresentationModel.RootRecord(); + /** @type {!Object.<string, !WebInspector.TimelinePresentationModel.Record>} */ + this._coalescingBuckets = {}; + }, + + /** + * @param {!WebInspector.TimelineModel.Record} record + */ + addRecord: function(record) + { + if (this._uiUtils.isProgram(record)) { + var records = record.children(); + for (var i = 0; i < records.length; ++i) + this._innerAddRecord(this._rootRecord, records[i]); + } else { + this._innerAddRecord(this._rootRecord, record); + } + }, + + /** + * @param {!WebInspector.TimelinePresentationModel.Record} parentRecord + * @param {!WebInspector.TimelineModel.Record} record + */ + _innerAddRecord: function(parentRecord, record) + { + var coalescingBucket; + + // On main thread, only coalesce if the last event is of same type. + if (parentRecord === this._rootRecord) + coalescingBucket = record.thread() ? record.type() : "mainThread"; + var coalescedRecord = this._findCoalescedParent(record, parentRecord, coalescingBucket); + if (coalescedRecord) + parentRecord = coalescedRecord; + + var formattedRecord = new WebInspector.TimelinePresentationModel.ActualRecord(record, parentRecord); + this._recordToPresentationRecord.put(record, formattedRecord); + + formattedRecord._collapsed = parentRecord === this._rootRecord; + if (coalescingBucket) + this._coalescingBuckets[coalescingBucket] = formattedRecord; + + for (var i = 0; record.children() && i < record.children().length; ++i) + this._innerAddRecord(formattedRecord, record.children()[i]); + + if (parentRecord.coalesced()) + this._updateCoalescingParent(formattedRecord); + }, + + /** + * @param {!WebInspector.TimelineModel.Record} record + * @param {!WebInspector.TimelinePresentationModel.Record} newParent + * @param {string=} bucket + * @return {?WebInspector.TimelinePresentationModel.Record} + */ + _findCoalescedParent: function(record, newParent, bucket) + { + const coalescingThresholdMillis = 5; + + var lastRecord = bucket ? this._coalescingBuckets[bucket] : newParent._presentationChildren.peekLast(); + if (lastRecord && lastRecord.coalesced()) + lastRecord = lastRecord._presentationChildren.peekLast(); + var startTime = record.startTime(); + var endTime = record.endTime(); + if (!lastRecord) + return null; + if (lastRecord.record().type() !== record.type()) + return null; + if (!this._uiUtils.isCoalescable(record.type())) + return null; + if (lastRecord.record().endTime() + coalescingThresholdMillis < startTime) + return null; + if (endTime + coalescingThresholdMillis < lastRecord.record().startTime()) + return null; + if (lastRecord.presentationParent().coalesced()) + return lastRecord.presentationParent(); + return this._replaceWithCoalescedRecord(lastRecord); + }, + + /** + * @param {!WebInspector.TimelinePresentationModel.Record} presentationRecord + * @return {!WebInspector.TimelinePresentationModel.Record} + */ + _replaceWithCoalescedRecord: function(presentationRecord) + { + var record = presentationRecord.record(); + var parent = presentationRecord._presentationParent; + var coalescedRecord = new WebInspector.TimelinePresentationModel.CoalescedRecord(record); + + coalescedRecord._collapsed = true; + coalescedRecord._presentationChildren.push(presentationRecord); + presentationRecord._presentationParent = coalescedRecord; + if (presentationRecord.hasWarnings() || presentationRecord.childHasWarnings()) + coalescedRecord._childHasWarnings = true; + + coalescedRecord._presentationParent = parent; + parent._presentationChildren[parent._presentationChildren.indexOf(presentationRecord)] = coalescedRecord; + WebInspector.TimelineUIUtils.aggregateTimeByCategory(coalescedRecord.presentationAggregatedStats(), presentationRecord.presentationAggregatedStats()); + + return coalescedRecord; + }, + + /** + * @param {!WebInspector.TimelinePresentationModel.Record} presentationRecord + */ + _updateCoalescingParent: function(presentationRecord) + { + var parentRecord = presentationRecord._presentationParent; + WebInspector.TimelineUIUtils.aggregateTimeByCategory(parentRecord.presentationAggregatedStats(), presentationRecord.presentationAggregatedStats()); + if (parentRecord.endTime() < presentationRecord.endTime()) + parentRecord._endTime = presentationRecord.endTime(); + }, + + /** + * @param {?RegExp} textFilter + */ + setTextFilter: function(textFilter) + { + this._textFilter = textFilter; + }, + + invalidateFilteredRecords: function() + { + delete this._filteredRecords; + }, + + /** + * @return {!Array.<!WebInspector.TimelinePresentationModel.Record>} + */ + filteredRecords: function() + { + if (this._filteredRecords) + return this._filteredRecords; + + var recordsInWindow = []; + + var stack = [{children: this._rootRecord._presentationChildren, index: 0, parentIsCollapsed: false, parentRecord: {}}]; + var revealedDepth = 0; + + function revealRecordsInStack() { + for (var depth = revealedDepth + 1; depth < stack.length; ++depth) { + if (stack[depth - 1].parentIsCollapsed) { + stack[depth].parentRecord._presentationParent._expandable = true; + return; + } + stack[depth - 1].parentRecord._collapsed = false; + recordsInWindow.push(stack[depth].parentRecord); + stack[depth].windowLengthBeforeChildrenTraversal = recordsInWindow.length; + stack[depth].parentIsRevealed = true; + revealedDepth = depth; + } + } + + while (stack.length) { + var entry = stack[stack.length - 1]; + var records = entry.children; + if (records && entry.index < records.length) { + var record = records[entry.index]; + ++entry.index; + if (record.startTime() < this._windowEndTime && record.endTime() > this._windowStartTime) { + if (this._model.isVisible(record.record())) { + record._presentationParent._expandable = true; + if (this._textFilter) + revealRecordsInStack(); + if (!entry.parentIsCollapsed) { + recordsInWindow.push(record); + revealedDepth = stack.length; + entry.parentRecord._collapsed = false; + } + } + } + + record._expandable = false; + + stack.push({children: record._presentationChildren, + index: 0, + parentIsCollapsed: entry.parentIsCollapsed || (record._collapsed && (!this._textFilter || record._expandedOrCollapsedWhileFiltered)), + parentRecord: record, + windowLengthBeforeChildrenTraversal: recordsInWindow.length}); + } else { + stack.pop(); + revealedDepth = Math.min(revealedDepth, stack.length - 1); + entry.parentRecord._visibleChildrenCount = recordsInWindow.length - entry.windowLengthBeforeChildrenTraversal; + } + } + + this._filteredRecords = recordsInWindow; + return recordsInWindow; + }, + + __proto__: WebInspector.Object.prototype +} + +/** + * @constructor + * @param {?WebInspector.TimelinePresentationModel.Record} parentRecord + */ +WebInspector.TimelinePresentationModel.Record = function(parentRecord) +{ + /** + * @type {!Array.<!WebInspector.TimelinePresentationModel.Record>} + */ + this._presentationChildren = []; + + if (parentRecord) { + this._presentationParent = parentRecord; + parentRecord._presentationChildren.push(this); + } +} + +WebInspector.TimelinePresentationModel.Record.prototype = { + /** + * @return {number} + */ + startTime: function() + { + throw new Error("Not implemented."); + }, + + /** + * @return {number} + */ + endTime: function() + { + throw new Error("Not implemented."); + }, + + /** + * @return {number} + */ + selfTime: function() + { + throw new Error("Not implemented."); + }, + + /** + * @return {!WebInspector.TimelineModel.Record} + */ + record: function() + { + throw new Error("Not implemented."); + }, + + /** + * @return {!Object.<string, number>} + */ + presentationAggregatedStats: function() + { + throw new Error("Not implemented."); + }, + + /** + * @return {!Array.<!WebInspector.TimelinePresentationModel.Record>} + */ + presentationChildren: function() + { + return this._presentationChildren; + }, + + /** + * @return {boolean} + */ + coalesced: function() + { + return false; + }, + + /** + * @return {boolean} + */ + collapsed: function() + { + return this._collapsed; + }, + + /** + * @param {boolean} collapsed + */ + setCollapsed: function(collapsed) + { + this._collapsed = collapsed; + this._expandedOrCollapsedWhileFiltered = true; + }, + + /** + * @return {?WebInspector.TimelinePresentationModel.Record} + */ + presentationParent: function() + { + return this._presentationParent || null; + }, + + /** + * @return {number} + */ + visibleChildrenCount: function() + { + return this._visibleChildrenCount || 0; + }, + + /** + * @return {boolean} + */ + expandable: function() + { + return !!this._expandable; + }, + + /** + * @return {boolean} + */ + hasWarnings: function() + { + return false; + }, + + /** + * @return {boolean} + */ + childHasWarnings: function() + { + return this._childHasWarnings; + }, + + /** + * @return {?WebInspector.TimelineRecordListRow} + */ + listRow: function() + { + return this._listRow; + }, + + /** + * @param {!WebInspector.TimelineRecordListRow} listRow + */ + setListRow: function(listRow) + { + this._listRow = listRow; + }, + + /** + * @return {?WebInspector.TimelineRecordGraphRow} + */ + graphRow: function() + { + return this._graphRow; + }, + + /** + * @param {!WebInspector.TimelineRecordGraphRow} graphRow + */ + setGraphRow: function(graphRow) + { + this._graphRow = graphRow; + } +} + +/** + * @constructor + * @extends {WebInspector.TimelinePresentationModel.Record} + * @param {!WebInspector.TimelineModel.Record} record + * @param {?WebInspector.TimelinePresentationModel.Record} parentRecord + */ +WebInspector.TimelinePresentationModel.ActualRecord = function(record, parentRecord) +{ + WebInspector.TimelinePresentationModel.Record.call(this, parentRecord); + this._record = record; + + if (this.hasWarnings()) { + for (var parent = this._presentationParent; parent && !parent._childHasWarnings; parent = parent._presentationParent) + parent._childHasWarnings = true; + } +} + +WebInspector.TimelinePresentationModel.ActualRecord.prototype = { + /** + * @return {number} + */ + startTime: function() + { + return this._record.startTime(); + }, + + /** + * @return {number} + */ + endTime: function() + { + return this._record.endTime(); + }, + + /** + * @return {number} + */ + selfTime: function() + { + return this._record.selfTime(); + }, + + /** + * @return {!WebInspector.TimelineModel.Record} + */ + record: function() + { + return this._record; + }, + + /** + * @return {!Object.<string, number>} + */ + presentationAggregatedStats: function() + { + return this._record.aggregatedStats(); + }, + + /** + * @return {boolean} + */ + hasWarnings: function() + { + return !!this._record.warnings(); + }, + + __proto__: WebInspector.TimelinePresentationModel.Record.prototype +} + +/** + * @constructor + * @extends {WebInspector.TimelinePresentationModel.Record} + * @param {!WebInspector.TimelineModel.Record} record + */ +WebInspector.TimelinePresentationModel.CoalescedRecord = function(record) +{ + WebInspector.TimelinePresentationModel.Record.call(this, null); + this._startTime = record.startTime(); + this._endTime = record.endTime(); + this._aggregatedStats = {}; +} + +WebInspector.TimelinePresentationModel.CoalescedRecord.prototype = { + /** + * @return {number} + */ + startTime: function() + { + return this._startTime; + }, + + /** + * @return {number} + */ + endTime: function() + { + return this._endTime; + }, + + /** + * @return {number} + */ + selfTime: function() + { + return 0; + }, + + /** + * @return {!WebInspector.TimelineModel.Record} + */ + record: function() + { + return this._presentationChildren[0].record(); + }, + + /** + * @return {!Object.<string, number>} + */ + presentationAggregatedStats: function() + { + return this._aggregatedStats; + }, + + /** + * @return {boolean} + */ + coalesced: function() + { + return true; + }, + + /** + * @return {boolean} + */ + hasWarnings: function() + { + return false; + }, + + __proto__: WebInspector.TimelinePresentationModel.Record.prototype +} + +/** + * @constructor + * @extends {WebInspector.TimelinePresentationModel.Record} + */ +WebInspector.TimelinePresentationModel.RootRecord = function() +{ + WebInspector.TimelinePresentationModel.Record.call(this, null); + this._aggregatedStats = {}; +} + +WebInspector.TimelinePresentationModel.RootRecord.prototype = { + /** + * @return {!Object.<string, number>} + */ + presentationAggregatedStats: function() + { + return this._aggregatedStats; + }, + + /** + * @return {boolean} + */ + hasWarnings: function() + { + return false; + }, + + __proto__: WebInspector.TimelinePresentationModel.Record.prototype +} |