Home | History | Annotate | Download | only in tracks
      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 base.require('base.sorted_array_utils');
      8 base.require('tracing.tracks.container_track');
      9 base.require('ui');
     10 
     11 base.exportTo('tracing.tracks', function() {
     12 
     13   /**
     14    * A track that displays a SliceGroup.
     15    * @constructor
     16    * @extends {ContainerTrack}
     17    */
     18 
     19   var SliceGroupTrack = ui.define(
     20       'slice-group-track', tracing.tracks.ContainerTrack);
     21 
     22   SliceGroupTrack.prototype = {
     23 
     24     __proto__: tracing.tracks.ContainerTrack.prototype,
     25 
     26     decorate: function(viewport) {
     27       tracing.tracks.ContainerTrack.prototype.decorate.call(this, viewport);
     28       this.classList.add('slice-group-track');
     29       this.tooltip_ = '';
     30       this.heading_ = '';
     31     },
     32 
     33     get group() {
     34       return this.group_;
     35     },
     36 
     37     set group(g) {
     38       this.group_ = g;
     39       this.updateContents_();
     40     },
     41 
     42     get heading() {
     43       return this.heading_;
     44     },
     45 
     46     set heading(h) {
     47       this.heading_ = h;
     48       this.updateContents_();
     49     },
     50 
     51     get tooltip() {
     52       return this.tooltip_;
     53     },
     54 
     55     set tooltip(t) {
     56       this.tooltip_ = t;
     57       this.updateContents_();
     58     },
     59 
     60     set decorateHit(f) {
     61       this.decorateHit_ = f;
     62       this.updateContents_();
     63     },
     64 
     65     addSliceTrack_: function(slices) {
     66       var track = new tracing.tracks.SliceTrack(this.viewport);
     67       track.slices = slices;
     68       track.decorateHit = this.decorateHit_;
     69       track.categoryFilter_ = this.categoryFilter;
     70       this.appendChild(track);
     71       return track;
     72     },
     73 
     74     get subRows() {
     75       return base.asArray(this.children).map(function(sliceTrack) {
     76         return sliceTrack.slices;
     77       });
     78     },
     79 
     80     get hasVisibleContent() {
     81       return this.children.length > 0;
     82     },
     83 
     84     updateContents_: function() {
     85       if (!this.group_) {
     86         this.updateHeadingAndTooltip_();
     87         return;
     88       }
     89 
     90       var slices = tracing.filterSliceArray(this.categoryFilter,
     91                                             this.group_.slices);
     92       if (this.areArrayContentsSame_(this.filteredSlices_, slices)) {
     93         this.updateHeadingAndTooltip_();
     94         return;
     95       }
     96 
     97       this.filteredSlices_ = slices;
     98 
     99       this.detach();
    100       if (!slices.length)
    101         return;
    102       var subRows = this.buildSubRows_(slices);
    103       for (var srI = 0; srI < subRows.length; srI++) {
    104         var subRow = subRows[srI];
    105         if (!subRow.length)
    106           continue;
    107         this.addSliceTrack_(subRow);
    108       }
    109       this.updateHeadingAndTooltip_();
    110     },
    111 
    112     updateHeadingAndTooltip_: function() {
    113       if (!this.firstChild)
    114         return;
    115       this.firstChild.heading = this.heading_;
    116       this.firstChild.tooltip = this.tooltip_;
    117     },
    118 
    119     /**
    120      * Breaks up the list of slices into N rows, each of which is a list of
    121      * slices that are non overlapping.
    122      */
    123     buildSubRows_: function(slices) {
    124       // This function works by walking through slices by start time.
    125       //
    126       // The basic idea here is to insert each slice as deep into the subrow
    127       // list as it can go such that every subSlice is fully contained by its
    128       // parent slice.
    129       //
    130       // Visually, if we start with this:
    131       //  0:  [    a       ]
    132       //  1:    [  b  ]
    133       //  2:    [c][d]
    134       //
    135       // To place this slice:
    136       //               [e]
    137       // We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
    138       // even intersect). So we go to row 1. That gives us [b], and [d] wont fit
    139       // into that either. So, we go to row 0 and its last slice, [a]. That can
    140       // completely contain [e], so that means we should add [e] as a subchild
    141       // of [a]. That puts it on row 1, yielding:
    142       //  0:  [    a       ]
    143       //  1:    [  b  ][e]
    144       //  2:    [c][d]
    145       //
    146       // If we then get this slice:
    147       //                      [f]
    148       // We do the same deepest-to-shallowest walk of the subrows trying to fit
    149       // it. This time, it doesn't fit in any open slice. So, we simply append
    150       // it to row 0:
    151       //  0:  [    a       ]  [f]
    152       //  1:    [  b  ][e]
    153       //  2:    [c][d]
    154       if (!slices.length)
    155         return [];
    156 
    157       var ops = [];
    158       for (var i = 0; i < slices.length; i++) {
    159         if (slices[i].subSlices)
    160           slices[i].subSlices.splice(0,
    161                                      slices[i].subSlices.length);
    162         ops.push(i);
    163       }
    164 
    165       ops.sort(function(ix, iy) {
    166         var x = slices[ix];
    167         var y = slices[iy];
    168         if (x.start != y.start)
    169           return x.start - y.start;
    170 
    171         // Elements get inserted into the slices array in order of when the
    172         // slices end.  Because slices must be properly nested, we break
    173         // start-time ties by assuming that the elements appearing earlier in
    174         // the slices array (and thus ending earlier) start later.
    175         return iy - ix;
    176       });
    177 
    178       var subRows = [[]];
    179       this.badSlices_ = [];  // TODO(simonjam): Connect this again.
    180 
    181       for (var i = 0; i < ops.length; i++) {
    182         var op = ops[i];
    183         var slice = slices[op];
    184 
    185         // Try to fit the slice into the existing subrows.
    186         var inserted = false;
    187         for (var j = subRows.length - 1; j >= 0; j--) {
    188           if (subRows[j].length == 0)
    189             continue;
    190 
    191           var insertedSlice = subRows[j][subRows[j].length - 1];
    192           if (slice.start < insertedSlice.start) {
    193             this.badSlices_.push(slice);
    194             inserted = true;
    195           }
    196           if (slice.start >= insertedSlice.start &&
    197               slice.end <= insertedSlice.end) {
    198             // Insert it into subRow j + 1.
    199             while (subRows.length <= j + 1)
    200               subRows.push([]);
    201             subRows[j + 1].push(slice);
    202             if (insertedSlice.subSlices)
    203               insertedSlice.subSlices.push(slice);
    204             inserted = true;
    205             break;
    206           }
    207         }
    208         if (inserted)
    209           continue;
    210 
    211         // Append it to subRow[0] as a root.
    212         subRows[0].push(slice);
    213       }
    214 
    215       return subRows;
    216     },
    217 
    218     areArrayContentsSame_: function(a, b) {
    219       if (!a || !b)
    220         return false;
    221       if (!a.length || !b.length)
    222         return false;
    223       if (a.length != b.length)
    224         return false;
    225       for (var i = 0; i < a.length; ++i) {
    226         if (a[i] != b[i])
    227           return false;
    228       }
    229       return true;
    230     }
    231   };
    232 
    233   return {
    234     SliceGroupTrack: SliceGroupTrack
    235   };
    236 });
    237