1<!DOCTYPE html> 2<!-- 3Copyright (c) 2015 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/base.html"> 9<link rel="import" href="/tracing/base/range_utils.html"> 10<link rel="import" href="/tracing/core/auditor.html"> 11<link rel="import" href="/tracing/model/event_info.html"> 12<link rel="import" href="/tracing/model/user_model/animation_expectation.html"> 13<link rel="import" href="/tracing/model/user_model/response_expectation.html"> 14 15<script> 16'use strict'; 17 18tr.exportTo('tr.importer', function() { 19 // This is an intermediate data format between InputLatencyAsyncSlices and 20 // Response and Animation IRs. 21 function ProtoExpectation(irType, name) { 22 this.irType = irType; 23 this.names = new Set(name ? [name] : undefined); 24 this.start = Infinity; 25 this.end = -Infinity; 26 this.associatedEvents = new tr.model.EventSet(); 27 this.isAnimationBegin = false; 28 } 29 30 ProtoExpectation.RESPONSE_TYPE = 'r'; 31 ProtoExpectation.ANIMATION_TYPE = 'a'; 32 33 // Explicitly ignore some input events to allow 34 // UserModelBuilder.checkAllInputEventsHandled() to determine which events 35 // were unintentionally ignored due to a bug. 36 ProtoExpectation.IGNORED_TYPE = 'ignored'; 37 38 ProtoExpectation.prototype = { 39 get isValid() { 40 return this.end > this.start; 41 }, 42 43 // Return true if any associatedEvent's typeName is in typeNames. 44 containsTypeNames: function(typeNames) { 45 for (var i = 0; i < this.associatedEvents.length; ++i) { 46 if (typeNames.indexOf(this.associatedEvents[i].typeName) >= 0) 47 return true; 48 } 49 return false; 50 }, 51 52 containsSliceTitle: function(title) { 53 for (var i = 0; i < this.associatedEvents.length; ++i) { 54 if (title === this.associatedEvents[i].title) 55 return true; 56 } 57 return false; 58 }, 59 60 createInteractionRecord: function(model) { 61 if (!this.isValid) { 62 console.error('Invalid ProtoExpectation: ' + this.debug() + 63 ' File a bug with this trace!'); 64 return undefined; 65 } 66 67 var initiatorTitles = []; 68 this.names.forEach(function(name) { 69 initiatorTitles.push(name); 70 }); 71 initiatorTitles = initiatorTitles.sort().join(','); 72 73 var duration = this.end - this.start; 74 75 var ir = undefined; 76 switch (this.irType) { 77 case ProtoExpectation.RESPONSE_TYPE: 78 ir = new tr.model.um.ResponseExpectation( 79 model, initiatorTitles, this.start, duration, 80 this.isAnimationBegin); 81 break; 82 case ProtoExpectation.ANIMATION_TYPE: 83 ir = new tr.model.um.AnimationExpectation( 84 model, initiatorTitles, this.start, duration); 85 break; 86 } 87 if (!ir) 88 return undefined; 89 90 ir.sourceEvents.addEventSet(this.associatedEvents); 91 92 function pushAssociatedEvents(event) { 93 ir.associatedEvents.push(event); 94 95 // |event| is either an InputLatencyAsyncSlice (which collects all of 96 // its associated events transitively) or a CSS Animation (which doesn't 97 // have any associated events). So this does not need to recurse. 98 if (event.associatedEvents) 99 ir.associatedEvents.addEventSet(event.associatedEvents); 100 } 101 102 this.associatedEvents.forEach(function(event) { 103 pushAssociatedEvents(event); 104 105 // Old-style InputLatencyAsyncSlices have subSlices. 106 if (event.subSlices) 107 event.subSlices.forEach(pushAssociatedEvents); 108 }); 109 110 return ir; 111 }, 112 113 // Merge the other ProtoExpectation into this one. 114 // The irTypes need not match: ignored ProtoExpectations might be merged 115 // into overlapping ProtoExpectations, and Touch-only Animations are merged 116 // into Tap Responses. 117 merge: function(other) { 118 other.names.forEach(function(name) { this.names.add(name); }.bind(this)); 119 120 // Don't use pushEvent(), which would lose special start, end. 121 this.associatedEvents.addEventSet(other.associatedEvents); 122 this.start = Math.min(this.start, other.start); 123 this.end = Math.max(this.end, other.end); 124 if (other.isAnimationBegin) 125 this.isAnimationBegin = true; 126 }, 127 128 // Include |event| in this ProtoExpectation, expanding start/end to include 129 // it. 130 pushEvent: function(event) { 131 // Usually, this method will be called while iterating over a list of 132 // events sorted by start time, so this method won't usually change 133 // this.start. However, this will sometimes be called for 134 // ProtoExpectations created by previous handlers, in which case 135 // event.start could possibly be before this.start. 136 this.start = Math.min(this.start, event.start); 137 this.end = Math.max(this.end, event.end); 138 this.associatedEvents.push(event); 139 }, 140 141 // Returns true if timestamp is contained in this ProtoExpectation. 142 containsTimestampInclusive: function(timestamp) { 143 return (this.start <= timestamp) && (timestamp <= this.end); 144 }, 145 146 // Return true if the other event intersects this ProtoExpectation. 147 intersects: function(other) { 148 // http://stackoverflow.com/questions/325933 149 return (other.start < this.end) && (other.end > this.start); 150 }, 151 152 isNear: function(event, threshold) { 153 return (this.end + threshold) > event.start; 154 }, 155 156 // Return a string describing this ProtoExpectation for debugging. 157 debug: function() { 158 var debugString = this.irType + '('; 159 debugString += parseInt(this.start) + ' '; 160 debugString += parseInt(this.end); 161 this.associatedEvents.forEach(function(event) { 162 debugString += ' ' + event.typeName; 163 }); 164 return debugString + ')'; 165 } 166 }; 167 168 return { 169 ProtoExpectation: ProtoExpectation 170 }; 171}); 172</script> 173