blob: 3413667c1b1b533f67d4a4586503efb438f39d78 [file] [log] [blame]
Jamie Gennis2da489c2012-09-19 18:06:29 -07001// 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
7base.require('tracks.timeline_container_track');
8base.require('sorted_array_utils');
9base.require('ui');
10
11base.exportTo('tracks', function() {
12
13 /**
14 * A track that displays a TimelineSliceGroup.
15 * @constructor
16 * @extends {TimelineContainerTrack}
17 */
18
19 var TimelineSliceGroupTrack = base.ui.define(tracks.TimelineContainerTrack);
20
21 TimelineSliceGroupTrack.prototype = {
22
23 __proto__: tracks.TimelineContainerTrack.prototype,
24
25 decorate: function() {
26 this.classList.add('timeline-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 tracks.TimelineSliceTrack();
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;
Jamie Gennis6e58f012012-09-27 17:41:20 -0700143
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;
Jamie Gennis2da489c2012-09-19 18:06:29 -0700149 });
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 TimelineSliceGroupTrack: TimelineSliceGroupTrack
208 };
209});