Home | History | Annotate | Download | only in tracing
      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  */
     10 base.require('base.events');
     11 base.require('base.guid');
     12 base.require('base.range');
     13 
     14 base.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