1<!DOCTYPE html> 2<!-- 3Copyright (c) 2013 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/extension_registry.html"> 9<link rel="import" href="/tracing/model/event.html"> 10<link rel="import" href="/tracing/model/object_snapshot.html"> 11<link rel="import" href="/tracing/base/range.html"> 12<link rel="import" href="/tracing/base/sorted_array_utils.html"> 13 14<script> 15'use strict'; 16 17/** 18 * @fileoverview Provides the ObjectSnapshot and ObjectHistory classes. 19 */ 20tr.exportTo('tr.model', function() { 21 var ObjectSnapshot = tr.model.ObjectSnapshot; 22 23 /** 24 * An object with a specific id, whose state has been snapshotted several 25 * times. 26 * 27 * @constructor 28 */ 29 function ObjectInstance( 30 parent, scopedId, category, name, creationTs, opt_baseTypeName) { 31 tr.model.Event.call(this); 32 this.parent = parent; 33 this.scopedId = scopedId; 34 this.category = category; 35 this.baseTypeName = opt_baseTypeName ? opt_baseTypeName : name; 36 this.name = name; 37 this.creationTs = creationTs; 38 this.creationTsWasExplicit = false; 39 this.deletionTs = Number.MAX_VALUE; 40 this.deletionTsWasExplicit = false; 41 this.colorId = 0; 42 this.bounds = new tr.b.Range(); 43 this.snapshots = []; 44 this.hasImplicitSnapshots = false; 45 } 46 47 ObjectInstance.prototype = { 48 __proto__: tr.model.Event.prototype, 49 50 get typeName() { 51 return this.name; 52 }, 53 54 addBoundsToRange: function(range) { 55 range.addRange(this.bounds); 56 }, 57 58 addSnapshot: function(ts, args, opt_name, opt_baseTypeName) { 59 if (ts < this.creationTs) 60 throw new Error('Snapshots must be >= instance.creationTs'); 61 if (ts >= this.deletionTs) 62 throw new Error('Snapshots cannot be added after ' + 63 'an objects deletion timestamp.'); 64 65 var lastSnapshot; 66 if (this.snapshots.length > 0) { 67 lastSnapshot = this.snapshots[this.snapshots.length - 1]; 68 if (lastSnapshot.ts == ts) 69 throw new Error('Snapshots already exists at this time!'); 70 if (ts < lastSnapshot.ts) { 71 throw new Error( 72 'Snapshots must be added in increasing timestamp order'); 73 } 74 } 75 76 // Update baseTypeName if needed. 77 if (opt_name && 78 (this.name != opt_name)) { 79 if (!opt_baseTypeName) 80 throw new Error('Must provide base type name for name update'); 81 if (this.baseTypeName != opt_baseTypeName) 82 throw new Error('Cannot update type name: base types dont match'); 83 this.name = opt_name; 84 } 85 86 var snapshotConstructor = 87 tr.model.ObjectSnapshot.getConstructor( 88 this.category, this.name); 89 var snapshot = new snapshotConstructor(this, ts, args); 90 this.snapshots.push(snapshot); 91 return snapshot; 92 }, 93 94 wasDeleted: function(ts) { 95 var lastSnapshot; 96 if (this.snapshots.length > 0) { 97 lastSnapshot = this.snapshots[this.snapshots.length - 1]; 98 if (lastSnapshot.ts > ts) 99 throw new Error( 100 'Instance cannot be deleted at ts=' + 101 ts + '. A snapshot exists that is older.'); 102 } 103 this.deletionTs = ts; 104 this.deletionTsWasExplicit = true; 105 }, 106 107 /** 108 * See ObjectSnapshot constructor notes on object initialization. 109 */ 110 preInitialize: function() { 111 for (var i = 0; i < this.snapshots.length; i++) 112 this.snapshots[i].preInitialize(); 113 }, 114 115 /** 116 * See ObjectSnapshot constructor notes on object initialization. 117 */ 118 initialize: function() { 119 for (var i = 0; i < this.snapshots.length; i++) 120 this.snapshots[i].initialize(); 121 }, 122 123 getSnapshotAt: function(ts) { 124 if (ts < this.creationTs) { 125 if (this.creationTsWasExplicit) 126 throw new Error('ts must be within lifetime of this instance'); 127 return this.snapshots[0]; 128 } 129 if (ts > this.deletionTs) 130 throw new Error('ts must be within lifetime of this instance'); 131 132 var snapshots = this.snapshots; 133 var i = tr.b.findIndexInSortedIntervals( 134 snapshots, 135 function(snapshot) { return snapshot.ts; }, 136 function(snapshot, i) { 137 if (i == snapshots.length - 1) 138 return snapshots[i].objectInstance.deletionTs; 139 return snapshots[i + 1].ts - snapshots[i].ts; 140 }, 141 ts); 142 if (i < 0) { 143 // Note, this is a little bit sketchy: this lets early ts point at the 144 // first snapshot, even before it is taken. We do this because raster 145 // tasks usually post before their tile snapshots are dumped. This may 146 // be a good line of code to re-visit if we start seeing strange and 147 // confusing object references showing up in the traces. 148 return this.snapshots[0]; 149 } 150 if (i >= this.snapshots.length) 151 return this.snapshots[this.snapshots.length - 1]; 152 return this.snapshots[i]; 153 }, 154 155 updateBounds: function() { 156 this.bounds.reset(); 157 this.bounds.addValue(this.creationTs); 158 if (this.deletionTs != Number.MAX_VALUE) 159 this.bounds.addValue(this.deletionTs); 160 else if (this.snapshots.length > 0) 161 this.bounds.addValue(this.snapshots[this.snapshots.length - 1].ts); 162 }, 163 164 shiftTimestampsForward: function(amount) { 165 this.creationTs += amount; 166 if (this.deletionTs != Number.MAX_VALUE) 167 this.deletionTs += amount; 168 this.snapshots.forEach(function(snapshot) { 169 snapshot.ts += amount; 170 }); 171 }, 172 173 get userFriendlyName() { 174 return this.typeName + ' object ' + this.scopedId; 175 } 176 }; 177 178 tr.model.EventRegistry.register( 179 ObjectInstance, 180 { 181 name: 'objectInstance', 182 pluralName: 'objectInstances', 183 singleViewElementName: 'tr-ui-a-single-object-instance-sub-view', 184 multiViewElementName: 'tr-ui-a-multi-object-sub-view' 185 }); 186 187 var options = new tr.b.ExtensionRegistryOptions( 188 tr.b.TYPE_BASED_REGISTRY_MODE); 189 options.mandatoryBaseClass = ObjectInstance; 190 options.defaultConstructor = ObjectInstance; 191 tr.b.decorateExtensionRegistry(ObjectInstance, options); 192 193 return { 194 ObjectInstance: ObjectInstance 195 }; 196}); 197</script> 198