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