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