1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/ui/tracks/multi_row_track.html">
9<link rel="import" href="/tracing/base/sorted_array_utils.html">
10<link rel="import" href="/tracing/ui/base/ui.html">
11
12<script>
13'use strict';
14
15tr.exportTo('tr.ui.tracks', function() {
16  /**
17   * A track that displays a SliceGroup.
18   * @constructor
19   * @extends {MultiRowTrack}
20   */
21  var SliceGroupTrack = tr.ui.b.define(
22      'slice-group-track', tr.ui.tracks.MultiRowTrack);
23
24  SliceGroupTrack.prototype = {
25
26    __proto__: tr.ui.tracks.MultiRowTrack.prototype,
27
28    decorate: function(viewport) {
29      tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this, viewport);
30      this.classList.add('slice-group-track');
31      this.group_ = undefined;
32      // Set the collapse threshold so we don't collapse by default, but the
33      // user can explicitly collapse if they want it.
34      this.defaultToCollapsedWhenSubRowCountMoreThan = 100;
35    },
36
37    addSubTrack_: function(slices) {
38      var track = new tr.ui.tracks.SliceTrack(this.viewport);
39      track.slices = slices;
40      this.appendChild(track);
41      return track;
42    },
43
44    get group() {
45      return this.group_;
46    },
47
48    set group(group) {
49      this.group_ = group;
50      this.setItemsToGroup(this.group_.slices, this.group_);
51    },
52
53    get eventContainer() {
54      return this.group;
55    },
56
57    addContainersToTrackMap: function(containerToTrackMap) {
58      tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(
59        this, arguments);
60      containerToTrackMap.addContainer(this.group, this);
61    },
62
63    /**
64     * Breaks up the list of slices into N rows, each of which is a list of
65     * slices that are non overlapping.
66     */
67    buildSubRows_: function(slices) {
68      var precisionUnit = this.group.model.intrinsicTimeUnit;
69
70      // This function works by walking through slices by start time.
71      //
72      // The basic idea here is to insert each slice as deep into the subrow
73      // list as it can go such that every subSlice is fully contained by its
74      // parent slice.
75      //
76      // Visually, if we start with this:
77      //  0:  [    a       ]
78      //  1:    [  b  ]
79      //  2:    [c][d]
80      //
81      // To place this slice:
82      //               [e]
83      // We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
84      // even intersect). So we go to row 1. That gives us [b], and [d] wont fit
85      // into that either. So, we go to row 0 and its last slice, [a]. That can
86      // completely contain [e], so that means we should add [e] as a subchild
87      // of [a]. That puts it on row 1, yielding:
88      //  0:  [    a       ]
89      //  1:    [  b  ][e]
90      //  2:    [c][d]
91      //
92      // If we then get this slice:
93      //                      [f]
94      // We do the same deepest-to-shallowest walk of the subrows trying to fit
95      // it. This time, it doesn't fit in any open slice. So, we simply append
96      // it to row 0:
97      //  0:  [    a       ]  [f]
98      //  1:    [  b  ][e]
99      //  2:    [c][d]
100      if (!slices.length)
101        return [];
102
103      var ops = [];
104      for (var i = 0; i < slices.length; i++) {
105        if (slices[i].subSlices)
106          slices[i].subSlices.splice(0,
107                                     slices[i].subSlices.length);
108        ops.push(i);
109      }
110
111      ops.sort(function(ix, iy) {
112        var x = slices[ix];
113        var y = slices[iy];
114        if (x.start != y.start)
115          return x.start - y.start;
116
117        // Elements get inserted into the slices array in order of when the
118        // slices start. Because slices must be properly nested, we break
119        // start-time ties by assuming that the elements appearing earlier in
120        // the slices array (and thus ending earlier) start earlier.
121        return ix - iy;
122      });
123
124      var subRows = [[]];
125      this.badSlices_ = [];  // TODO(simonjam): Connect this again.
126
127      for (var i = 0; i < ops.length; i++) {
128        var op = ops[i];
129        var slice = slices[op];
130
131        // Try to fit the slice into the existing subrows.
132        var inserted = false;
133        for (var j = subRows.length - 1; j >= 0; j--) {
134          if (subRows[j].length == 0)
135            continue;
136
137          var insertedSlice = subRows[j][subRows[j].length - 1];
138          if (slice.start < insertedSlice.start) {
139            this.badSlices_.push(slice);
140            inserted = true;
141          }
142          if (insertedSlice.bounds(slice, precisionUnit)) {
143            // Insert it into subRow j + 1.
144            while (subRows.length <= j + 1)
145              subRows.push([]);
146            subRows[j + 1].push(slice);
147            if (insertedSlice.subSlices)
148              insertedSlice.subSlices.push(slice);
149            inserted = true;
150            break;
151          }
152        }
153        if (inserted)
154          continue;
155
156        // Append it to subRow[0] as a root.
157        subRows[0].push(slice);
158      }
159
160      return subRows;
161    }
162  };
163
164  return {
165    SliceGroupTrack: SliceGroupTrack
166  };
167});
168</script>
169