1 // Copyright (c) 2013 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 Provides the ObjectSnapshot and ObjectHistory classes. 9 */ 10 base.require('base.range'); 11 base.require('base.sorted_array_utils'); 12 base.require('tracing.trace_model.object_snapshot'); 13 14 base.exportTo('tracing.trace_model', function() { 15 var ObjectSnapshot = tracing.trace_model.ObjectSnapshot; 16 17 /** 18 * An object with a specific id, whose state has been snapshotted several 19 * times. 20 * 21 * @constructor 22 */ 23 function ObjectInstance(parent, id, category, name, creationTs) { 24 this.parent = parent; 25 this.id = id; 26 this.category = category; 27 this.name = name; 28 this.creationTs = creationTs; 29 this.creationTsWasExplicit = false; 30 this.deletionTs = Number.MAX_VALUE; 31 this.deletionTsWasExplicit = false; 32 this.selected = false; 33 this.colorId = 0; 34 this.bounds = new base.Range(); 35 this.snapshots = []; 36 this.hasImplicitSnapshots = false; 37 } 38 39 ObjectInstance.prototype = { 40 __proto__: Object.prototype, 41 42 get typeName() { 43 return this.name; 44 }, 45 46 addSnapshot: function(ts, args) { 47 if (ts < this.creationTs) 48 throw new Error('Snapshots must be >= instance.creationTs'); 49 if (ts >= this.deletionTs) 50 throw new Error('Snapshots cannot be added after ' + 51 'an objects deletion timestamp.'); 52 53 var lastSnapshot; 54 if (this.snapshots.length > 0) { 55 lastSnapshot = this.snapshots[this.snapshots.length - 1]; 56 if (lastSnapshot.ts == ts) 57 throw new Error('Snapshots already exists at this time!'); 58 if (ts < lastSnapshot.ts) { 59 throw new Error( 60 'Snapshots must be added in increasing timestamp order'); 61 } 62 } 63 64 var snapshotConstructor = 65 tracing.trace_model.ObjectSnapshot.getConstructor(this.name); 66 var snapshot = new snapshotConstructor(this, ts, args); 67 this.snapshots.push(snapshot); 68 return snapshot; 69 }, 70 71 wasDeleted: function(ts) { 72 var lastSnapshot; 73 if (this.snapshots.length > 0) { 74 lastSnapshot = this.snapshots[this.snapshots.length - 1]; 75 if (lastSnapshot.ts > ts) 76 throw new Error( 77 'Instance cannot be deleted at ts=' + 78 ts + '. A snapshot exists that is older.'); 79 } 80 this.deletionTs = ts; 81 this.deletionTsWasExplicit = true; 82 }, 83 84 /** 85 * See ObjectSnapshot constructor notes on object initialization. 86 */ 87 preInitialize: function() { 88 for (var i = 0; i < this.snapshots.length; i++) 89 this.snapshots[i].preInitialize(); 90 }, 91 92 /** 93 * See ObjectSnapshot constructor notes on object initialization. 94 */ 95 initialize: function() { 96 for (var i = 0; i < this.snapshots.length; i++) 97 this.snapshots[i].initialize(); 98 }, 99 100 getSnapshotAt: function(ts) { 101 if (ts < this.creationTs) { 102 if (this.creationTsWasExplicit) 103 throw new Error('ts must be within lifetime of this instance'); 104 return this.snapshots[0]; 105 } 106 if (ts > this.deletionTs) 107 throw new Error('ts must be within lifetime of this instance'); 108 109 var snapshots = this.snapshots; 110 var i = base.findLowIndexInSortedIntervals( 111 snapshots, 112 function(snapshot) { return snapshot.ts; }, 113 function(snapshot, i) { 114 if (i == snapshots.length - 1) 115 return snapshots[i].objectInstance.deletionTs; 116 return snapshots[i + 1].ts - snapshots[i].ts; 117 }, 118 ts); 119 if (i < 0) { 120 // Note, this is a little bit sketchy: this lets early ts point at the 121 // first snapshot, even before it is taken. We do this because raster 122 // tasks usually post before their tile snapshots are dumped. This may 123 // be a good line of code to re-visit if we start seeing strange and 124 // confusing object references showing up in the traces. 125 return this.snapshots[0]; 126 } 127 if (i >= this.snapshots.length) 128 return this.snapshots[this.snapshots.length - 1]; 129 return this.snapshots[i]; 130 }, 131 132 updateBounds: function() { 133 this.bounds.reset(); 134 this.bounds.addValue(this.creationTs); 135 if (this.deletionTs != Number.MAX_VALUE) 136 this.bounds.addValue(this.deletionTs); 137 else if (this.snapshots.length > 0) 138 this.bounds.addValue(this.snapshots[this.snapshots.length - 1].ts); 139 }, 140 141 shiftTimestampsForward: function(amount) { 142 this.creationTs += amount; 143 if (this.deletionTs != Number.MAX_VALUE) 144 this.deletionTs += amount; 145 this.snapshots.forEach(function(snapshot) { 146 snapshot.ts += amount; 147 }); 148 } 149 }; 150 151 ObjectInstance.nameToConstructorMap_ = {}; 152 ObjectInstance.register = function(name, constructor) { 153 if (ObjectInstance.nameToConstructorMap_[name]) 154 throw new Error('Constructor already registerd for ' + name); 155 ObjectInstance.nameToConstructorMap_[name] = constructor; 156 }; 157 158 ObjectInstance.unregister = function(name) { 159 delete ObjectInstance.nameToConstructorMap_[name]; 160 }; 161 162 ObjectInstance.getConstructor = function(name) { 163 if (ObjectInstance.nameToConstructorMap_[name]) 164 return ObjectInstance.nameToConstructorMap_[name]; 165 return ObjectInstance; 166 }; 167 168 return { 169 ObjectInstance: ObjectInstance 170 }; 171 }); 172