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/** 8 * @fileoverview Code for the viewport. 9 */ 10base.require('base.events'); 11base.require('base.guid'); 12base.require('base.range'); 13 14base.exportTo('tracing', function() { 15 16 function SelectionSliceHit(track, slice) { 17 this.track = track; 18 this.slice = slice; 19 } 20 SelectionSliceHit.prototype = { 21 get modelObject() { 22 return this.slice; 23 }, 24 25 get selected() { 26 return this.slice.selected; 27 }, 28 set selected(v) { 29 this.slice.selected = v; 30 }, 31 addBoundsToRange: function(range) { 32 range.addValue(this.slice.start); 33 range.addValue(this.slice.end); 34 } 35 }; 36 37 function SelectionCounterSampleHit(track, counter, sampleIndex) { 38 this.track = track; 39 this.counter = counter; 40 this.sampleIndex = sampleIndex; 41 } 42 43 SelectionCounterSampleHit.prototype = { 44 get modelObject() { 45 return this.sampleIndex; 46 }, 47 48 get selected() { 49 return this.track.selectedSamples[this.sampleIndex] == true; 50 }, 51 52 set selected(v) { 53 if (v) 54 this.track.selectedSamples[this.sampleIndex] = true; 55 else 56 this.track.selectedSamples[this.sampleIndex] = false; 57 }, 58 59 addBoundsToRange: function(range) { 60 if (!this.track.timestamps) 61 return; 62 range.addValue(this.track.timestamps[this.sampleIndex]); 63 } 64 }; 65 66 function SelectionObjectSnapshotHit(track, objectSnapshot) { 67 this.track = track; 68 this.objectSnapshot = objectSnapshot; 69 } 70 SelectionObjectSnapshotHit.prototype = { 71 get modelObject() { 72 return this.objectSnapshot; 73 }, 74 75 get selected() { 76 return this.objectSnapshot.selected; 77 }, 78 set selected(v) { 79 this.objectSnapshot.selected = v; 80 }, 81 addBoundsToRange: function(range) { 82 range.addValue(this.objectSnapshot.ts); 83 } 84 }; 85 86 function SelectionObjectInstanceHit(track, objectInstance) { 87 this.track = track; 88 this.objectInstance = objectInstance; 89 } 90 SelectionObjectInstanceHit.prototype = { 91 get modelObject() { 92 return this.objectInstance; 93 }, 94 95 get selected() { 96 return this.objectInstance.selected; 97 }, 98 set selected(v) { 99 this.objectInstance.selected = v; 100 }, 101 addBoundsToRange: function(range) { 102 range.addRange(this.objectInstance.bounds); 103 } 104 }; 105 106 var HIT_TYPES = [ 107 { 108 constructor: SelectionSliceHit, 109 name: 'slice', 110 pluralName: 'slices' 111 }, 112 { 113 constructor: SelectionCounterSampleHit, 114 name: 'counterSample', 115 pluralName: 'counterSamples' 116 }, 117 { 118 constructor: SelectionObjectSnapshotHit, 119 name: 'objectSnapshot', 120 pluralName: 'objectSnapshots' 121 }, 122 { 123 constructor: SelectionObjectInstanceHit, 124 name: 'objectInstance', 125 pluralName: 'objectInstances' 126 } 127 ]; 128 129 /** 130 * Represents a selection within a and its associated set of tracks. 131 * @constructor 132 */ 133 function Selection(opt_hits) { 134 this.bounds_dirty_ = true; 135 this.bounds_ = new base.Range(); 136 this.length_ = 0; 137 this.guid_ = base.GUID.allocate(); 138 139 if (opt_hits) 140 this.pushHits(opt_hits); 141 } 142 Selection.prototype = { 143 __proto__: Object.prototype, 144 145 get bounds() { 146 if (this.bounds_dirty_) { 147 this.bounds_.reset(); 148 for (var i = 0; i < this.length_; i++) { 149 var hit = this[i]; 150 hit.addBoundsToRange(this.bounds_); 151 } 152 this.bounds_dirty_ = false; 153 } 154 return this.bounds_; 155 }, 156 157 get duration() { 158 if (this.bounds_.isEmpty) 159 return 0; 160 return this.bounds_.max - this.bounds_.min; 161 }, 162 163 get length() { 164 return this.length_; 165 }, 166 167 get guid() { 168 return this.guid_; 169 }, 170 171 clear: function() { 172 for (var i = 0; i < this.length_; ++i) 173 delete this[i]; 174 this.length_ = 0; 175 this.bounds_dirty_ = true; 176 }, 177 178 pushHit: function(hit) { 179 this.push_(hit); 180 }, 181 182 pushHits: function(hits) { 183 for (var i = 0; i < hits.length; i++) 184 this.pushHit(hits[i]); 185 }, 186 187 push_: function(hit) { 188 this[this.length_++] = hit; 189 this.bounds_dirty_ = true; 190 return hit; 191 }, 192 193 addSlice: function(track, slice) { 194 return this.push_(new SelectionSliceHit(track, slice)); 195 }, 196 197 addCounterSample: function(track, counter, sampleIndex) { 198 return this.push_( 199 new SelectionCounterSampleHit( 200 track, counter, sampleIndex)); 201 }, 202 203 addObjectSnapshot: function(track, objectSnapshot) { 204 return this.push_( 205 new SelectionObjectSnapshotHit(track, objectSnapshot)); 206 }, 207 208 addObjectInstance: function(track, objectInstance) { 209 return this.push_( 210 new SelectionObjectInstanceHit(track, objectInstance)); 211 }, 212 213 addSelection: function(selection) { 214 for (var i = 0; i < selection.length; i++) 215 this.push_(selection[i]); 216 }, 217 218 subSelection: function(index, count) { 219 count = count || 1; 220 221 var selection = new Selection(); 222 selection.bounds_dirty_ = true; 223 if (index < 0 || index + count > this.length_) 224 throw new Error('Index out of bounds'); 225 226 for (var i = index; i < index + count; i++) 227 selection.push_(this[i]); 228 229 return selection; 230 }, 231 232 getCounterSampleHitsAsSelection: function() { 233 var selection = new Selection(); 234 this.enumHitsOfType(SelectionCounterSampleHit, 235 selection.push_.bind(selection)); 236 return selection; 237 }, 238 239 getSliceHitsAsSelection: function() { 240 var selection = new Selection(); 241 this.enumHitsOfType(SelectionSliceHit, 242 selection.push_.bind(selection)); 243 return selection; 244 }, 245 246 getHitsOrganizedByType: function() { 247 var hits = {}; 248 HIT_TYPES.forEach(function(hitType) { 249 hits[hitType.pluralName] = new Selection(); 250 }); 251 for (var i = 0; i < this.length_; i++) { 252 var hit = this[i]; 253 HIT_TYPES.forEach(function(hitType) { 254 if (hit instanceof hitType.constructor) 255 hits[hitType.pluralName].push_(hit); 256 }); 257 } 258 return hits; 259 }, 260 261 enumHitsOfType: function(type, func) { 262 for (var i = 0; i < this.length_; i++) 263 if (this[i] instanceof type) 264 func(this[i]); 265 }, 266 267 getNumSliceHits: function() { 268 var numHits = 0; 269 this.enumHitsOfType(SelectionSliceHit, function(hit) { numHits++; }); 270 return numHits; 271 }, 272 273 getNumCounterHits: function() { 274 var numHits = 0; 275 this.enumHitsOfType(SelectionCounterSampleHit, function(hit) { 276 numHits++; 277 }); 278 return numHits; 279 }, 280 281 getNumObjectSnapshotHits: function() { 282 var numHits = 0; 283 this.enumHitsOfType(SelectionObjectSnapshotHit, function(hit) { 284 numHits++; 285 }); 286 return numHits; 287 }, 288 289 getNumObjectInstanceHits: function() { 290 var numHits = 0; 291 this.enumHitsOfType(SelectionObjectInstanceHit, function(hit) { 292 numHits++; 293 }); 294 return numHits; 295 }, 296 297 map: function(fn) { 298 for (var i = 0; i < this.length_; i++) 299 fn(this[i]); 300 }, 301 302 /** 303 * Helper for selection previous or next. 304 * @param {boolean} forwardp If true, select one forward (next). 305 * Else, select previous. 306 * @return {boolean} true if current selection changed. 307 */ 308 getShiftedSelection: function(offset) { 309 var newSelection = new Selection(); 310 for (var i = 0; i < this.length_; i++) { 311 var hit = this[i]; 312 hit.track.addItemNearToProvidedHitToSelection( 313 hit, offset, newSelection); 314 } 315 316 if (newSelection.length == 0) 317 return undefined; 318 return newSelection; 319 } 320 }; 321 322 function createSelectionFromObjectAndView(obj, opt_view) { 323 // TODO: fill in the track intelligently by finding the TimelineView, then 324 // finding the right track based on the provided object. 325 var track = undefined; 326 327 var selection = new Selection(); 328 if (obj instanceof tracing.trace_model.Slice) 329 selection.addSlice(track, obj); 330 else if (obj instanceof tracing.trace_model.ObjectSnapshot) 331 selection.addObjectSnapshot(track, obj); 332 else if (obj instanceof tracing.trace_model.ObjectInstance) 333 selection.addObjectInstance(track, obj); 334 else 335 throw new Error('Unrecognized selection type'); 336 return selection; 337 } 338 339 return { 340 SelectionSliceHit: SelectionSliceHit, 341 SelectionCounterSampleHit: SelectionCounterSampleHit, 342 SelectionObjectSnapshotHit: SelectionObjectSnapshotHit, 343 SelectionObjectInstanceHit: SelectionObjectInstanceHit, 344 Selection: Selection, 345 createSelectionFromObjectAndView: createSelectionFromObjectAndView 346 }; 347}); 348