blob: 93f9ed51be7fb6bc12535b976a76194df743349b [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="/core/trace_model/event_info.html">
<link rel="import" href="/extras/audits/utils.html">
<link rel="import" href="/extras/audits/chrome_model_helper.html">
<link rel="import" href="/extras/audits/rail_interaction_record.html">
<script>
'use strict';
/**
* @fileoverview Base class for trace data Auditors.
*/
tv.exportTo('tv.e.audits', function() {
function RAILIRFinder(model, modelHelper) {
this.model = model;
this.modelHelper = modelHelper;
};
RAILIRFinder.supportsModelHelper = function(modelHelper) {
return modelHelper.browser !== undefined;
};
RAILIRFinder.prototype = {
getAllLatencyEvents: function() {
return this.modelHelper.browser.getLatencyEventsInRange(
this.model.bounds);
},
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 tv.e.audits.mergeEvents(
latencyStartPoints, 250, joinStartPointsIntoGesture);
},
getAllExpectedFlingingRanges: function() {
if (this.modelHelper.browser === 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.
function isFlingAnimationEvent(event) {
return event.title === 'InputHandlerProxy::HandleGestureFling::started';
};
var browserHelper = this.modelHelper.browser;
var allFlingAnimationEvents = browserHelper.getAllAsyncSlicesMatching(
isFlingAnimationEvent);
allFlingAnimationEvents.forEach(function(flingAnimationEvent) {
ops.push({
op: FLING_OP_ANIM_END, guid: flingAnimationEvent.guid,
opTs: flingAnimationEvent.end,
flingAnimationEvent: flingAnimationEvent
});
});
// 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 GOING WAT IS THIS');
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/
},
findAllInteractionRecords: function() {
var rirs = [];
var gestures = this.getAllGestures();
var gestureIRs = gestures.map(function gestureToIR(gesture) {
var colorId = tv.b.ui.getColorIdForGeneralPurposeString('mt_input');
var ir = new tv.e.audits.RAILInteractionRecord(
'railInput', colorId,
gesture.expectedStart, gesture.expectedEnd - gesture.expectedStart);
ir.associatedEvents.push.apply(
ir.associatedEvents,
gesture.latencyEvents);
return ir;
});
rirs.push.apply(rirs, gestureIRs);
var flings = this.getAllExpected60Ranges();
var flingIRs = flings.map(function flingToIR(e60r) {
var colorId = tv.b.ui.getColorIdForGeneralPurposeString('mt_fling');
var ir = new tv.e.audits.RAILInteractionRecord(
'railFling', colorId,
e60r.expectedStart, e60r.expectedEnd - e60r.expectedStart);
return ir;
});
rirs.push.apply(rirs, flingIRs);
return rirs;
}
};
return {
RAILIRFinder: RAILIRFinder
};
});
</script>