Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 /**
      8  * @fileoverview Provides the TimelineSliceGroup class.
      9  */
     10 base.require('timeline_slice');
     11 base.require('timeline_color_scheme');
     12 base.require('timeline_filter');
     13 
     14 base.exportTo('tracing', function() {
     15   var TimelineSlice = tracing.TimelineSlice;
     16 
     17   /**
     18    * A group of TimelineSlices, plus code to create them from B/E events, as
     19    * well as arrange them into subRows.
     20    *
     21    * Do not mutate the slices array directly. Modify it only by
     22    * TimelineSliceGroup mutation methods.
     23    *
     24    * @constructor
     25    * @param {function(new:TimelineSlice, category, title, colorId, start, args)}
     26    *     opt_sliceConstructor The constructor to use when creating slices.
     27    */
     28   function TimelineSliceGroup(opt_sliceConstructor) {
     29     var sliceConstructor = opt_sliceConstructor || TimelineSlice;
     30     this.sliceConstructor = sliceConstructor;
     31 
     32     this.openPartialSlices_ = [];
     33 
     34     this.slices = [];
     35   }
     36 
     37   TimelineSliceGroup.prototype = {
     38     __proto__: Object.prototype,
     39 
     40     /**
     41      * Helper function that pushes the provided slice onto the slices array.
     42      * @param {TimelineSlice} slice The slice to be added to the slices array.
     43      */
     44     pushSlice: function(slice) {
     45       this.slices.push(slice);
     46       return slice;
     47     },
     48 
     49     /**
     50      * Helper function that pushes the provided slice onto the slices array.
     51      * @param {Array.<TimelineSlice>} slices An array of slices to be added.
     52      */
     53     pushSlices: function(slices) {
     54       this.slices.push.apply(this.slices, slices);
     55     },
     56 
     57     /**
     58      * Opens a new slice in the group's slices.
     59      *
     60      * Calls to beginSlice and
     61      * endSlice must be made with non-monotonically-decreasing timestamps.
     62      *
     63      * @param {String} title Title of the slice to add.
     64      * @param {Number} ts The timetsamp of the slice, in milliseconds.
     65      * @param {Object.<string, Object>} opt_args Arguments associated with
     66      * the slice.
     67      */
     68     beginSlice: function(category, title, ts, opt_args) {
     69       if (this.openPartialSlices_.length) {
     70         var prevSlice = this.openPartialSlices_[
     71             this.openPartialSlices_.length - 1];
     72         if (ts < prevSlice.start)
     73           throw new Error('Slices must be added in increasing timestamp order');
     74       }
     75 
     76       var colorId = tracing.getStringColorId(title);
     77       var slice = new this.sliceConstructor(category, title, colorId, ts,
     78                                             opt_args ? opt_args : {});
     79       this.openPartialSlices_.push(slice);
     80       return slice;
     81     },
     82 
     83     isTimestampValidForBeginOrEnd: function(ts) {
     84       if (!this.openPartialSlices_.length)
     85         return true;
     86       var top = this.openPartialSlices_[this.openPartialSlices_.length - 1];
     87       return ts >= top.start;
     88     },
     89 
     90     /**
     91      * @return {Number} The number of beginSlices for which an endSlice has not
     92      * been issued.
     93      */
     94     get openSliceCount() {
     95       return this.openPartialSlices_.length;
     96     },
     97 
     98     /**
     99      * Ends the last begun slice in this group and pushes it onto the slice
    100      * array.
    101      *
    102      * @param {Number} ts Timestamp when the slice ended.
    103      * @return {TimelineSlice} slice.
    104      */
    105     endSlice: function(ts) {
    106       if (!this.openSliceCount)
    107         throw new Error('endSlice called without an open slice');
    108       var slice = this.openPartialSlices_[this.openSliceCount - 1];
    109       this.openPartialSlices_.splice(this.openSliceCount - 1, 1);
    110       if (ts < slice.start)
    111         throw new Error('Slice ' + slice.name +
    112                         ' end time is before its start.');
    113 
    114       slice.duration = ts - slice.start;
    115       this.pushSlice(slice);
    116 
    117       return slice;
    118     },
    119 
    120     /**
    121      * Closes any open slices.
    122      * @param {Number} opt_maxTimestamp The end time to use for the closed
    123      * slices. If not provided,
    124      * the max timestamp for this slice is provided.
    125      */
    126     autoCloseOpenSlices: function(opt_maxTimestamp) {
    127       if (!opt_maxTimestamp) {
    128         this.updateBounds();
    129         opt_maxTimestamp = this.maxTimestamp;
    130       }
    131       while (this.openSliceCount > 0) {
    132         var slice = this.endSlice(opt_maxTimestamp);
    133         slice.didNotFinish = true;
    134       }
    135     },
    136 
    137     /**
    138      * Shifts all the timestamps inside this group forward by the amount
    139      * specified.
    140      */
    141     shiftTimestampsForward: function(amount) {
    142       for (var sI = 0; sI < this.slices.length; sI++) {
    143         var slice = this.slices[sI];
    144         slice.start = (slice.start + amount);
    145       }
    146       for (var sI = 0; sI < this.openPartialSlices_.length; sI++) {
    147         var slice = this.openPartialSlices_[i];
    148         slice.start = (slice.start + amount);
    149       }
    150     },
    151 
    152     /**
    153      * Updates the bounds for this group based on the slices it contains.
    154      */
    155     updateBounds: function() {
    156       var vals = [];
    157       if (this.slices.length) {
    158         var minTimestamp = Number.MAX_VALUE;
    159         var maxTimestamp = -Number.MAX_VALUE;
    160         for (var i = 0; i < this.slices.length; i++) {
    161           if (this.slices[i].start < minTimestamp)
    162             minTimestamp = this.slices[i].start;
    163           if (this.slices[i].end > maxTimestamp)
    164             maxTimestamp = this.slices[i].end;
    165         }
    166         vals.push(minTimestamp);
    167         vals.push(maxTimestamp);
    168       }
    169 
    170       if (this.openPartialSlices_.length) {
    171         vals.push(this.openPartialSlices_[0].start);
    172         vals.push(
    173             this.openPartialSlices_[this.openPartialSlices_.length - 1].start);
    174       }
    175 
    176       if (vals.length) {
    177         this.minTimestamp = Math.min.apply(Math, vals);
    178         this.maxTimestamp = Math.max.apply(Math, vals);
    179       } else {
    180         this.minTimestamp = undefined;
    181         this.maxTimestamp = undefined;
    182       }
    183     }
    184   };
    185 
    186   return {
    187     TimelineSliceGroup: TimelineSliceGroup
    188   };
    189 });
    190