// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @fileoverview Provides the ObjectSnapshot and ObjectHistory classes.
*/
base.require('base.range');
base.require('base.sorted_array_utils');
base.require('tracing.trace_model.object_snapshot');
base.exportTo('tracing.trace_model', function() {
var ObjectSnapshot = tracing.trace_model.ObjectSnapshot;
/**
* An object with a specific id, whose state has been snapshotted several
* times.
*
* @constructor
*/
function ObjectInstance(parent, id, category, name, creationTs) {
this.parent = parent;
this.id = id;
this.category = category;
this.name = name;
this.creationTs = creationTs;
this.creationTsWasExplicit = false;
this.deletionTs = Number.MAX_VALUE;
this.deletionTsWasExplicit = false;
this.selected = false;
this.colorId = 0;
this.bounds = new base.Range();
this.snapshots = [];
this.hasImplicitSnapshots = false;
}
ObjectInstance.prototype = {
__proto__: Object.prototype,
get typeName() {
return this.name;
},
addSnapshot: function(ts, args) {
if (ts < this.creationTs)
throw new Error('Snapshots must be >= instance.creationTs');
if (ts >= this.deletionTs)
throw new Error('Snapshots cannot be added after ' +
'an objects deletion timestamp.');
var lastSnapshot;
if (this.snapshots.length > 0) {
lastSnapshot = this.snapshots[this.snapshots.length - 1];
if (lastSnapshot.ts == ts)
throw new Error('Snapshots already exists at this time!');
if (ts < lastSnapshot.ts) {
throw new Error(
'Snapshots must be added in increasing timestamp order');
}
}
var snapshotConstructor =
tracing.trace_model.ObjectSnapshot.getConstructor(this.name);
var snapshot = new snapshotConstructor(this, ts, args);
this.snapshots.push(snapshot);
return snapshot;
},
wasDeleted: function(ts) {
var lastSnapshot;
if (this.snapshots.length > 0) {
lastSnapshot = this.snapshots[this.snapshots.length - 1];
if (lastSnapshot.ts > ts)
throw new Error(
'Instance cannot be deleted at ts=' +
ts + '. A snapshot exists that is older.');
}
this.deletionTs = ts;
this.deletionTsWasExplicit = true;
},
/**
* See ObjectSnapshot constructor notes on object initialization.
*/
preInitialize: function() {
for (var i = 0; i < this.snapshots.length; i++)
this.snapshots[i].preInitialize();
},
/**
* See ObjectSnapshot constructor notes on object initialization.
*/
initialize: function() {
for (var i = 0; i < this.snapshots.length; i++)
this.snapshots[i].initialize();
},
getSnapshotAt: function(ts) {
if (ts < this.creationTs) {
if (this.creationTsWasExplicit)
throw new Error('ts must be within lifetime of this instance');
return this.snapshots[0];
}
if (ts > this.deletionTs)
throw new Error('ts must be within lifetime of this instance');
var snapshots = this.snapshots;
var i = base.findLowIndexInSortedIntervals(
snapshots,
function(snapshot) { return snapshot.ts; },
function(snapshot, i) {
if (i == snapshots.length - 1)
return snapshots[i].objectInstance.deletionTs;
return snapshots[i + 1].ts - snapshots[i].ts;
},
ts);
if (i < 0) {
// Note, this is a little bit sketchy: this lets early ts point at the
// first snapshot, even before it is taken. We do this because raster
// tasks usually post before their tile snapshots are dumped. This may
// be a good line of code to re-visit if we start seeing strange and
// confusing object references showing up in the traces.
return this.snapshots[0];
}
if (i >= this.snapshots.length)
return this.snapshots[this.snapshots.length - 1];
return this.snapshots[i];
},
updateBounds: function() {
this.bounds.reset();
this.bounds.addValue(this.creationTs);
if (this.deletionTs != Number.MAX_VALUE)
this.bounds.addValue(this.deletionTs);
else if (this.snapshots.length > 0)
this.bounds.addValue(this.snapshots[this.snapshots.length - 1].ts);
},
shiftTimestampsForward: function(amount) {
this.creationTs += amount;
if (this.deletionTs != Number.MAX_VALUE)
this.deletionTs += amount;
this.snapshots.forEach(function(snapshot) {
snapshot.ts += amount;
});
}
};
ObjectInstance.nameToConstructorMap_ = {};
ObjectInstance.register = function(name, constructor) {
if (ObjectInstance.nameToConstructorMap_[name])
throw new Error('Constructor already registerd for ' + name);
ObjectInstance.nameToConstructorMap_[name] = constructor;
};
ObjectInstance.unregister = function(name) {
delete ObjectInstance.nameToConstructorMap_[name];
};
ObjectInstance.getConstructor = function(name) {
if (ObjectInstance.nameToConstructorMap_[name])
return ObjectInstance.nameToConstructorMap_[name];
return ObjectInstance;
};
return {
ObjectInstance: ObjectInstance
};
});