blob: 7d28b1c18d9ff05516afde8a003d2001351976ac [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 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.
-->
<link rel="import" href="/base/base.html">
<link rel="import" href="/core/auditor.html">
<link rel="import" href="/model/event_info.html">
<link rel="import" href="/base/range_utils.html">
<link rel="import" href="/extras/chrome/chrome_model_helper.html">
<link rel="import" href="/extras/rail/animation_interaction_record.html">
<link rel="import" href="/extras/rail/idle_interaction_record.html">
<link rel="import" href="/extras/rail/load_interaction_record.html">
<link rel="import" href="/extras/rail/response_interaction_record.html">
<script>
'use strict';
/**
* @fileoverview Base class for trace data Auditors.
*/
tr.exportTo('tr.e.rail', function() {
function RAILIRFinder(model, modelHelper) {
this.model = model;
this.modelHelper = modelHelper;
};
RAILIRFinder.supportsModelHelper = function(modelHelper) {
return modelHelper.browserHelper !== undefined;
};
RAILIRFinder.prototype = {
getAllLatencyEvents: function() {
return this.modelHelper.browserHelper.getLatencyEventsInRange(
this.model.bounds);
},
getAllFrameEvents: function() {
var frameEvents = [];
if (this.modelHelper.browserHelper) {
var fe = this.modelHelper.browserHelper.getFrameEventsInRange(
tr.e.audits.IMPL_FRAMETIME_TYPE,
this.model.bounds);
frameEvents.push.apply(frameEvents, fe);
}
tr.b.iterItems(this.modelHelper.rendererHelpers, function(pid, renderer) {
var fe = renderer.getFrameEventsInRange(tr.e.audits.IMPL_FRAMETIME_TYPE,
this.model.bounds);
frameEvents.push.apply(frameEvents, fe);
}, this);
frameEvents.sort(function(x, y) { return x.start - y.start; });
return frameEvents;
},
getAllGestures: function() {
var allLatencyEvents = this.getAllLatencyEvents();
function latencyEventToStartPoint(latencyEvent) {
return {
start: latencyEvent.start,
end: latencyEvent.start,
latencyEvent: latencyEvent
};
}
var latencyStartPoints = allLatencyEvents.map(latencyEventToStartPoint);
function joinStartPointsIntoGesture(startPoints) {
var latencyEvents = startPoints.map(function(startPoint) {
return startPoint.latencyEvent;
});
var e0 = latencyEvents[0];
var eN = latencyEvents[latencyEvents.length - 1];
var gesture = {
expectedStart: e0.start,
expectedEnd: eN.start,
latencyEvents: latencyEvents
};
return gesture;
}
return tr.e.audits.mergeExplicitRanges(
latencyStartPoints, 250, joinStartPointsIntoGesture);
},
getAllExpectedFlingingRanges: function() {
if (this.modelHelper.browserHelper === undefined)
return;
var ops = [];
var FLING_OP_START = 'start';
var FLING_OP_CANCEL = 'cancel';
var FLING_OP_ANIM_END = 'anim-end';
// Add ops for input fling start & cancel.
var allLatencyEvents = this.getAllLatencyEvents();
allLatencyEvents.forEach(function(latencyEvent) {
if (latencyEvent.subSlices.length == 0)
return;
var s0title = latencyEvent.subSlices[0].title;
if (s0title === 'InputLatency:GestureFlingStart') {
ops.push({
op: FLING_OP_START, guid: latencyEvent.guid,
opTs: latencyEvent.start,
latencyEvent: latencyEvent
});
} else if (s0title === 'InputLatency:GestureFlingCancel') {
ops.push({
op: FLING_OP_CANCEL, guid: latencyEvent.guid,
opTs: latencyEvent.start,
latencyEvent: latencyEvent
});
}
});
// Add ops for fling animations.
this.modelHelper.browserHelper.getAllAsyncSlicesMatching(function(event) {
if (event.title !== 'InputHandlerProxy::HandleGestureFling::started')
return;
ops.push({
op: FLING_OP_ANIM_END,
guid: event.guid,
opTs: event.end,
flingAnimationEvent: event
});
});
// Sort ops, stably.
ops.sort(function(x, y) {
var delta = x.opTs - y.opTs;
if (delta != 0)
return delta;
return x.guid - y.guid;
});
// Create expected-to-fling ranges based on UX expectations.
var allExpectedFlingRanges = [];
var currentFling = undefined;
ops.forEach(function(op) {
switch (op.op) {
case FLING_OP_START:
if (currentFling)
throw new Error('OMG THERE IS ALREADY A FLING');
currentFling = {
expectedStart: op.latencyEvent.start,
actualStart: op.latencyEvent.end,
expectedEnd: undefined,
actualEnd: undefined
};
break;
case FLING_OP_CANCEL:
if (!currentFling)
return;
currentFling.expectedEnd = op.latencyEvent.start;
currentFling.actualEnd = op.latencyEvent.end;
allExpectedFlingRanges.push(currentFling);
currentFling = undefined;
break;
case FLING_OP_ANIM_END:
if (!currentFling)
return;
currentFling.expectedEnd = op.flingAnimationEvent.end;
currentFling.actualEnd = op.flingAnimationEvent.end;
allExpectedFlingRanges.push(currentFling);
currentFling = undefined;
break;
}
});
if (currentFling) {
currentFling.expectedEnd = this.model.bounds.max;
currentFling.actualEnd = this.model.bounds.max;
allExpectedFlingRanges.push(currentFling);
currentFling = undefined;
}
return allExpectedFlingRanges;
},
getAllExpected60Ranges: function() {
return this.getAllExpectedFlingingRanges();
},
findGestureTapDowns: function() {
// InputLatency:GestureTapDown/
},
findLoadInteractionRecords: function() {
var commits =
this.modelHelper.browserHelper.getCommitProvisionalLoadEventsInRange(
this.model.bounds);
var frames = this.getAllFrameEvents();
commits.forEach(function(commitLoad, commitLoadIndex) {
frames.some(function(frame) {
if ((frame.start > commitLoad.start) &&
(frame.parentContainer.parent.pid ==
commitLoad.parentContainer.parent.pid)) {
if (frame.commitProvisionalLoad) return true;
console.log('commit first frame', commitLoad.start, frame.start,
frame.start - commitLoad.start);
commitLoad.firstFrame = frame;
frame.commitProvisionalLoad = commitLoad;
return true;
}
return false;
});
if (!commitLoad.firstFrame) {
console.log('missing first frame', commitLoad);
}
});
var lirs = [];
commits.forEach(function(commitLoad, commitLoadIndex) {
if (!commitLoad.firstFrame)
return;
lirs.push(new tr.e.rail.LoadInteractionRecord(
commitLoad.start, commitLoad.firstFrame.end - commitLoad.start));
});
return lirs;
},
findResponseInteractionRecords: function() {
return this.getAllGestures().map(function(gesture) {
return new tr.e.rail.ResponseInteractionRecord(
gesture.expectedStart, gesture.expectedEnd - gesture.expectedStart);
});
},
findAnimationInteractionRecords: function() {
return this.getAllExpected60Ranges().map(function(fling) {
return new tr.e.rail.AnimationInteractionRecord(
fling.expectedStart, fling.expectedEnd - fling.expectedStart);
});
},
findIdleInteractionRecords: function(otherIRs) {
var emptyRanges = tr.e.audits.findEmptyRangesBetweenExplicitRanges(
otherIRs, this.model.bounds);
return emptyRanges.map(function(range) {
return new tr.e.rail.IdleInteractionRecord(
range.min, range.max - range.min);
});
},
findAllInteractionRecords: function() {
var rirs = [];
rirs.push.apply(rirs, this.findLoadInteractionRecords());
rirs.push.apply(rirs, this.findResponseInteractionRecords());
rirs.push.apply(rirs, this.findAnimationInteractionRecords());
// TODO(benjhayden): Reintroduce this without reintroducing #1020.
// rirs.push.apply(rirs, this.findIdleInteractionRecords(rirs));
return rirs;
}
};
return {
RAILIRFinder: RAILIRFinder
};
});
</script>