blob: 3dc6b642469eb1b555d87a8aa1dfc99d45750ca4 [file] [log] [blame]
// 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.
'use strict';
/**
* @fileoverview Provides the TimelineSliceGroup class.
*/
base.require('timeline_slice');
base.require('timeline_color_scheme');
base.require('timeline_filter');
base.exportTo('tracing', function() {
var TimelineSlice = tracing.TimelineSlice;
/**
* A group of TimelineSlices, plus code to create them from B/E events, as
* well as arrange them into subRows.
*
* Do not mutate the slices array directly. Modify it only by
* TimelineSliceGroup mutation methods.
*
* @constructor
* @param {function(new:TimelineSlice, category, title, colorId, start, args)}
* opt_sliceConstructor The constructor to use when creating slices.
*/
function TimelineSliceGroup(opt_sliceConstructor) {
var sliceConstructor = opt_sliceConstructor || TimelineSlice;
this.sliceConstructor = sliceConstructor;
this.openPartialSlices_ = [];
this.slices = [];
}
TimelineSliceGroup.prototype = {
__proto__: Object.prototype,
/**
* Helper function that pushes the provided slice onto the slices array.
* @param {TimelineSlice} slice The slice to be added to the slices array.
*/
pushSlice: function(slice) {
this.slices.push(slice);
return slice;
},
/**
* Helper function that pushes the provided slice onto the slices array.
* @param {Array.<TimelineSlice>} slices An array of slices to be added.
*/
pushSlices: function(slices) {
this.slices.push.apply(this.slices, slices);
},
/**
* Opens a new slice in the group's slices.
*
* Calls to beginSlice and
* endSlice must be made with non-monotonically-decreasing timestamps.
*
* @param {String} title Title of the slice to add.
* @param {Number} ts The timetsamp of the slice, in milliseconds.
* @param {Object.<string, Object>} opt_args Arguments associated with
* the slice.
*/
beginSlice: function(category, title, ts, opt_args) {
if (this.openPartialSlices_.length) {
var prevSlice = this.openPartialSlices_[
this.openPartialSlices_.length - 1];
if (ts < prevSlice.start)
throw new Error('Slices must be added in increasing timestamp order');
}
var colorId = tracing.getStringColorId(title);
var slice = new this.sliceConstructor(category, title, colorId, ts,
opt_args ? opt_args : {});
this.openPartialSlices_.push(slice);
return slice;
},
isTimestampValidForBeginOrEnd: function(ts) {
if (!this.openPartialSlices_.length)
return true;
var top = this.openPartialSlices_[this.openPartialSlices_.length - 1];
return ts >= top.start;
},
/**
* @return {Number} The number of beginSlices for which an endSlice has not
* been issued.
*/
get openSliceCount() {
return this.openPartialSlices_.length;
},
/**
* Ends the last begun slice in this group and pushes it onto the slice
* array.
*
* @param {Number} ts Timestamp when the slice ended.
* @return {TimelineSlice} slice.
*/
endSlice: function(ts) {
if (!this.openSliceCount)
throw new Error('endSlice called without an open slice');
var slice = this.openPartialSlices_[this.openSliceCount - 1];
this.openPartialSlices_.splice(this.openSliceCount - 1, 1);
if (ts < slice.start)
throw new Error('Slice ' + slice.name +
' end time is before its start.');
slice.duration = ts - slice.start;
this.pushSlice(slice);
return slice;
},
/**
* Closes any open slices.
* @param {Number} opt_maxTimestamp The end time to use for the closed
* slices. If not provided,
* the max timestamp for this slice is provided.
*/
autoCloseOpenSlices: function(opt_maxTimestamp) {
if (!opt_maxTimestamp) {
this.updateBounds();
opt_maxTimestamp = this.maxTimestamp;
}
while (this.openSliceCount > 0) {
var slice = this.endSlice(opt_maxTimestamp);
slice.didNotFinish = true;
}
},
/**
* Shifts all the timestamps inside this group forward by the amount
* specified.
*/
shiftTimestampsForward: function(amount) {
for (var sI = 0; sI < this.slices.length; sI++) {
var slice = this.slices[sI];
slice.start = (slice.start + amount);
}
for (var sI = 0; sI < this.openPartialSlices_.length; sI++) {
var slice = this.openPartialSlices_[i];
slice.start = (slice.start + amount);
}
},
/**
* Updates the bounds for this group based on the slices it contains.
*/
updateBounds: function() {
var vals = [];
if (this.slices.length) {
var minTimestamp = Number.MAX_VALUE;
var maxTimestamp = -Number.MAX_VALUE;
for (var i = 0; i < this.slices.length; i++) {
if (this.slices[i].start < minTimestamp)
minTimestamp = this.slices[i].start;
if (this.slices[i].end > maxTimestamp)
maxTimestamp = this.slices[i].end;
}
vals.push(minTimestamp);
vals.push(maxTimestamp);
}
if (this.openPartialSlices_.length) {
vals.push(this.openPartialSlices_[0].start);
vals.push(
this.openPartialSlices_[this.openPartialSlices_.length - 1].start);
}
if (vals.length) {
this.minTimestamp = Math.min.apply(Math, vals);
this.maxTimestamp = Math.max.apply(Math, vals);
} else {
this.minTimestamp = undefined;
this.maxTimestamp = undefined;
}
}
};
return {
TimelineSliceGroup: TimelineSliceGroup
};
});