| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @constructor | 6 * @constructor |
| 7 */ | 7 */ |
| 8 WebInspector.TimelineIRModel = function() | 8 WebInspector.TimelineIRModel = function() |
| 9 { | 9 { |
| 10 this.reset(); | 10 this.reset(); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 | 75 |
| 76 var groups = WebInspector.TimelineUIUtils.asyncEventGroups(); | 76 var groups = WebInspector.TimelineUIUtils.asyncEventGroups(); |
| 77 var asyncEventsByGroup = timelineModel.mainThreadAsyncEvents(); | 77 var asyncEventsByGroup = timelineModel.mainThreadAsyncEvents(); |
| 78 var inputLatencies = asyncEventsByGroup.get(groups.input); | 78 var inputLatencies = asyncEventsByGroup.get(groups.input); |
| 79 if (!inputLatencies) | 79 if (!inputLatencies) |
| 80 return; | 80 return; |
| 81 this._processInputLatencies(inputLatencies); | 81 this._processInputLatencies(inputLatencies); |
| 82 var animations = asyncEventsByGroup.get(groups.animation); | 82 var animations = asyncEventsByGroup.get(groups.animation); |
| 83 if (animations) | 83 if (animations) |
| 84 this._processAnimations(animations); | 84 this._processAnimations(animations); |
| 85 var range = new WebInspector.SegmentedRange(); | 85 var range = new SegmentedRange(); |
| 86 range.appendRange(this._drags); // Drags take lower precedence than anim
ation, as we can't detect them reliably. | 86 range.appendRange(this._drags); // Drags take lower precedence than anim
ation, as we can't detect them reliably. |
| 87 range.appendRange(this._cssAnimations); | 87 range.appendRange(this._cssAnimations); |
| 88 range.appendRange(this._scrolls); | 88 range.appendRange(this._scrolls); |
| 89 range.appendRange(this._responses); | 89 range.appendRange(this._responses); |
| 90 this._segments = range.segments(); | 90 this._segments = range.segments(); |
| 91 }, | 91 }, |
| 92 | 92 |
| 93 /** | 93 /** |
| 94 * @param {!Array<!WebInspector.TracingModel.AsyncEvent>} events | 94 * @param {!Array<!WebInspector.TracingModel.AsyncEvent>} events |
| 95 */ | 95 */ |
| (...skipping 26 matching lines...) Expand all Loading... |
| 122 WebInspector.console.error(WebInspector.UIString("Two flings
at the same time? %s vs %s", flingStart.startTime, event.startTime)); | 122 WebInspector.console.error(WebInspector.UIString("Two flings
at the same time? %s vs %s", flingStart.startTime, event.startTime)); |
| 123 break; | 123 break; |
| 124 } | 124 } |
| 125 flingStart = event; | 125 flingStart = event; |
| 126 break; | 126 break; |
| 127 | 127 |
| 128 case eventTypes.FlingCancel: | 128 case eventTypes.FlingCancel: |
| 129 // FIXME: also process renderer fling events. | 129 // FIXME: also process renderer fling events. |
| 130 if (!flingStart) | 130 if (!flingStart) |
| 131 break; | 131 break; |
| 132 this._scrolls.append(new WebInspector.Segment(flingStart.startTi
me, event.endTime, phases.Fling)); | 132 this._scrolls.append(new Segment(flingStart.startTime, event.end
Time, phases.Fling)); |
| 133 flingStart = null; | 133 flingStart = null; |
| 134 break; | 134 break; |
| 135 | 135 |
| 136 case eventTypes.ImplSideFling: | 136 case eventTypes.ImplSideFling: |
| 137 this._scrolls.append(this._segmentForEvent(event, phases.Fling))
; | 137 this._scrolls.append(this._segmentForEvent(event, phases.Fling))
; |
| 138 break; | 138 break; |
| 139 | 139 |
| 140 case eventTypes.ShowPress: | 140 case eventTypes.ShowPress: |
| 141 case eventTypes.Tap: | 141 case eventTypes.Tap: |
| 142 case eventTypes.KeyDown: | 142 case eventTypes.KeyDown: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 161 | 161 |
| 162 case eventTypes.TouchCancel: | 162 case eventTypes.TouchCancel: |
| 163 touchStart = null; | 163 touchStart = null; |
| 164 break; | 164 break; |
| 165 | 165 |
| 166 case eventTypes.TouchMove: | 166 case eventTypes.TouchMove: |
| 167 if (firstTouchMove) { | 167 if (firstTouchMove) { |
| 168 this._drags.append(this._segmentForEvent(event, phases.Drag)
); | 168 this._drags.append(this._segmentForEvent(event, phases.Drag)
); |
| 169 } else if (touchStart) { | 169 } else if (touchStart) { |
| 170 firstTouchMove = event; | 170 firstTouchMove = event; |
| 171 this._responses.append(new WebInspector.Segment(touchStart.s
tartTime, event.endTime, phases.Response)); | 171 this._responses.append(new Segment(touchStart.startTime, eve
nt.endTime, phases.Response)); |
| 172 } | 172 } |
| 173 break; | 173 break; |
| 174 | 174 |
| 175 case eventTypes.TouchEnd: | 175 case eventTypes.TouchEnd: |
| 176 touchStart = null; | 176 touchStart = null; |
| 177 break; | 177 break; |
| 178 | 178 |
| 179 case eventTypes.MouseDown: | 179 case eventTypes.MouseDown: |
| 180 mouseDown = event; | 180 mouseDown = event; |
| 181 mouseMove = null; | 181 mouseMove = null; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 192 break; | 192 break; |
| 193 | 193 |
| 194 case eventTypes.MouseUp: | 194 case eventTypes.MouseUp: |
| 195 this._responses.append(this._segmentForEvent(event, phases.Respo
nse)); | 195 this._responses.append(this._segmentForEvent(event, phases.Respo
nse)); |
| 196 mouseDown = null; | 196 mouseDown = null; |
| 197 break; | 197 break; |
| 198 | 198 |
| 199 case eventTypes.MouseWheel: | 199 case eventTypes.MouseWheel: |
| 200 // Do not consider first MouseWheel as trace viewer's implementa
tion does -- in case of MouseWheel it's not really special. | 200 // Do not consider first MouseWheel as trace viewer's implementa
tion does -- in case of MouseWheel it's not really special. |
| 201 if (mouseWheel && canMerge(thresholdsMs.mouse, mouseWheel, event
)) | 201 if (mouseWheel && canMerge(thresholdsMs.mouse, mouseWheel, event
)) |
| 202 this._scrolls.append(new WebInspector.Segment(mouseWheel.end
Time, event.startTime, phases.Scroll)); | 202 this._scrolls.append(new Segment(mouseWheel.endTime, event.s
tartTime, phases.Scroll)); |
| 203 this._scrolls.append(this._segmentForEvent(event, phases.Scroll)
); | 203 this._scrolls.append(this._segmentForEvent(event, phases.Scroll)
); |
| 204 mouseWheel = event; | 204 mouseWheel = event; |
| 205 break; | 205 break; |
| 206 } | 206 } |
| 207 } | 207 } |
| 208 | 208 |
| 209 /** | 209 /** |
| 210 * @param {number} threshold | 210 * @param {number} threshold |
| 211 * @param {!WebInspector.TracingModel.AsyncEvent} first | 211 * @param {!WebInspector.TracingModel.AsyncEvent} first |
| 212 * @param {!WebInspector.TracingModel.AsyncEvent} second | 212 * @param {!WebInspector.TracingModel.AsyncEvent} second |
| (...skipping 10 matching lines...) Expand all Loading... |
| 223 */ | 223 */ |
| 224 _processAnimations: function(events) | 224 _processAnimations: function(events) |
| 225 { | 225 { |
| 226 for (var i = 0; i < events.length; ++i) | 226 for (var i = 0; i < events.length; ++i) |
| 227 this._cssAnimations.append(this._segmentForEvent(events[i], WebInspe
ctor.TimelineIRModel.Phases.Animation)); | 227 this._cssAnimations.append(this._segmentForEvent(events[i], WebInspe
ctor.TimelineIRModel.Phases.Animation)); |
| 228 }, | 228 }, |
| 229 | 229 |
| 230 /** | 230 /** |
| 231 * @param {!WebInspector.TracingModel.AsyncEvent} event | 231 * @param {!WebInspector.TracingModel.AsyncEvent} event |
| 232 * @param {!WebInspector.TimelineIRModel.Phases} phase | 232 * @param {!WebInspector.TimelineIRModel.Phases} phase |
| 233 * @return {!WebInspector.Segment} | 233 * @return {!Segment} |
| 234 */ | 234 */ |
| 235 _segmentForEvent: function(event, phase) | 235 _segmentForEvent: function(event, phase) |
| 236 { | 236 { |
| 237 return new WebInspector.Segment(event.startTime, event.endTime, phase); | 237 return new Segment(event.startTime, event.endTime, phase); |
| 238 }, | 238 }, |
| 239 | 239 |
| 240 /** | 240 /** |
| 241 * @return {!Array<!WebInspector.Segment>} | 241 * @return {!Array<!Segment>} |
| 242 */ | 242 */ |
| 243 interactionRecords: function() | 243 interactionRecords: function() |
| 244 { | 244 { |
| 245 return this._segments; | 245 return this._segments; |
| 246 }, | 246 }, |
| 247 | 247 |
| 248 reset: function() | 248 reset: function() |
| 249 { | 249 { |
| 250 var thresholdsMs = WebInspector.TimelineIRModel._mergeThresholdsMs; | 250 var thresholdsMs = WebInspector.TimelineIRModel._mergeThresholdsMs; |
| 251 | 251 |
| 252 this._segments = []; | 252 this._segments = []; |
| 253 this._drags = new WebInspector.SegmentedRange(merge.bind(null, threshold
sMs.mouse)); | 253 this._drags = new SegmentedRange(merge.bind(null, thresholdsMs.mouse)); |
| 254 this._cssAnimations = new WebInspector.SegmentedRange(merge.bind(null, t
hresholdsMs.animation)); | 254 this._cssAnimations = new SegmentedRange(merge.bind(null, thresholdsMs.a
nimation)); |
| 255 this._responses = new WebInspector.SegmentedRange(merge.bind(null, 0)); | 255 this._responses = new SegmentedRange(merge.bind(null, 0)); |
| 256 this._scrolls = new WebInspector.SegmentedRange(merge.bind(null, thresho
ldsMs.animation)); | 256 this._scrolls = new SegmentedRange(merge.bind(null, thresholdsMs.animati
on)); |
| 257 | 257 |
| 258 /** | 258 /** |
| 259 * @param {number} threshold | 259 * @param {number} threshold |
| 260 * @param {!WebInspector.Segment} first | 260 * @param {!Segment} first |
| 261 * @param {!WebInspector.Segment} second | 261 * @param {!Segment} second |
| 262 */ | 262 */ |
| 263 function merge(threshold, first, second) | 263 function merge(threshold, first, second) |
| 264 { | 264 { |
| 265 return first.end + threshold >= second.begin && first.data === secon
d.data ? first : null; | 265 return first.end + threshold >= second.begin && first.data === secon
d.data ? first : null; |
| 266 } | 266 } |
| 267 }, | 267 }, |
| 268 | 268 |
| 269 /** | 269 /** |
| 270 * @param {string} eventName | 270 * @param {string} eventName |
| 271 * @return {?WebInspector.TimelineIRModel.InputEvents} | 271 * @return {?WebInspector.TimelineIRModel.InputEvents} |
| 272 */ | 272 */ |
| 273 _inputEventType: function(eventName) | 273 _inputEventType: function(eventName) |
| 274 { | 274 { |
| 275 var prefix = "InputLatency::"; | 275 var prefix = "InputLatency::"; |
| 276 if (!eventName.startsWith(prefix)) { | 276 if (!eventName.startsWith(prefix)) { |
| 277 if (eventName === WebInspector.TimelineIRModel.InputEvents.ImplSideF
ling) | 277 if (eventName === WebInspector.TimelineIRModel.InputEvents.ImplSideF
ling) |
| 278 return /** @type {!WebInspector.TimelineIRModel.InputEvents} */
(eventName); | 278 return /** @type {!WebInspector.TimelineIRModel.InputEvents} */
(eventName); |
| 279 console.error("Unrecognized input latency event: " + eventName); | 279 console.error("Unrecognized input latency event: " + eventName); |
| 280 return null; | 280 return null; |
| 281 } | 281 } |
| 282 return /** @type {!WebInspector.TimelineIRModel.InputEvents} */ (eventNa
me.substr(prefix.length)); | 282 return /** @type {!WebInspector.TimelineIRModel.InputEvents} */ (eventNa
me.substr(prefix.length)); |
| 283 } | 283 } |
| 284 }; | 284 }; |
| 285 | 285 |
| 286 /** | |
| 287 * @constructor | |
| 288 * @param {(function(!WebInspector.Segment, !WebInspector.Segment): ?WebInspecto
r.Segment)=} mergeCallback | |
| 289 */ | |
| 290 WebInspector.SegmentedRange = function(mergeCallback) | |
| 291 { | |
| 292 /** @type {!Array<!WebInspector.Segment>} */ | |
| 293 this._segments = []; | |
| 294 this._mergeCallback = mergeCallback; | |
| 295 } | |
| 296 | |
| 297 /** | |
| 298 * @constructor | |
| 299 * @param {number} begin | |
| 300 * @param {number} end | |
| 301 * @param {*} data | |
| 302 */ | |
| 303 WebInspector.Segment = function(begin, end, data) | |
| 304 { | |
| 305 if (begin > end) | |
| 306 console.assert(false, "Invalid segment"); | |
| 307 this.begin = begin; | |
| 308 this.end = end; | |
| 309 this.data = data; | |
| 310 } | |
| 311 | |
| 312 WebInspector.Segment.prototype = { | |
| 313 /** | |
| 314 * @param {!WebInspector.Segment} that | |
| 315 * @return {boolean} | |
| 316 */ | |
| 317 intersects: function(that) | |
| 318 { | |
| 319 return this.begin < that.end && that.begin < this.end; | |
| 320 } | |
| 321 }; | |
| 322 | |
| 323 WebInspector.SegmentedRange.prototype = { | |
| 324 /** | |
| 325 * @param {!WebInspector.Segment} newSegment | |
| 326 */ | |
| 327 append: function(newSegment) | |
| 328 { | |
| 329 // 1. Find the proper insertion point for new segment | |
| 330 var startIndex = this._segments.lowerBound(newSegment, (a, b) => a.begin
- b.begin); | |
| 331 var endIndex = startIndex; | |
| 332 var merged = null; | |
| 333 if (startIndex > 0) { | |
| 334 // 2. Try mering the preceding segment | |
| 335 var precedingSegment = this._segments[startIndex - 1]; | |
| 336 merged = this._tryMerge(precedingSegment, newSegment); | |
| 337 if (merged) { | |
| 338 --startIndex; | |
| 339 newSegment = merged; | |
| 340 } else if (this._segments[startIndex - 1].end >= newSegment.begin) { | |
| 341 // 2a. If merge failed and segments overlap, adjust preceding se
gment. | |
| 342 // If an old segment entirely contains new one, split it in two. | |
| 343 if (newSegment.end < precedingSegment.end) | |
| 344 this._segments.splice(startIndex, 0, new WebInspector.Segmen
t(newSegment.end, precedingSegment.end, precedingSegment.data)); | |
| 345 precedingSegment.end = newSegment.begin; | |
| 346 } | |
| 347 } | |
| 348 // 3. Consume all segments that are entirely covered by the new one. | |
| 349 while (endIndex < this._segments.length && this._segments[endIndex].end
<= newSegment.end) | |
| 350 ++endIndex; | |
| 351 // 4. Merge or adjust the succeeding segment if it overlaps. | |
| 352 if (endIndex < this._segments.length) { | |
| 353 merged = this._tryMerge(newSegment, this._segments[endIndex]); | |
| 354 if (merged) { | |
| 355 endIndex++; | |
| 356 newSegment = merged; | |
| 357 } else if (newSegment.intersects(this._segments[endIndex])) | |
| 358 this._segments[endIndex].begin = newSegment.end; | |
| 359 } | |
| 360 this._segments.splice(startIndex, endIndex - startIndex, newSegment); | |
| 361 }, | |
| 362 | |
| 363 /** | |
| 364 * @param {!WebInspector.SegmentedRange} that | |
| 365 */ | |
| 366 appendRange: function(that) | |
| 367 { | |
| 368 that.segments().forEach(segment => this.append(segment)); | |
| 369 }, | |
| 370 | |
| 371 /** | |
| 372 * @return {!Array<!WebInspector.Segment>} | |
| 373 */ | |
| 374 segments: function() | |
| 375 { | |
| 376 return this._segments; | |
| 377 }, | |
| 378 | |
| 379 /** | |
| 380 * @param {!WebInspector.Segment} first | |
| 381 * @param {!WebInspector.Segment} second | |
| 382 * @return {?WebInspector.Segment} | |
| 383 */ | |
| 384 _tryMerge: function(first, second) | |
| 385 { | |
| 386 var merged = this._mergeCallback && this._mergeCallback(first, second); | |
| 387 if (!merged) | |
| 388 return null; | |
| 389 merged.begin = first.begin; | |
| 390 merged.end = Math.max(first.end, second.end); | |
| 391 return merged; | |
| 392 } | |
| 393 } | |
| OLD | NEW |