// 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 }; });