OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
13 * distribution. | 13 * distribution. |
14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
17 * | 17 * |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | 31 * @unrestricted |
33 * @param {function(!WebInspector.TracingModel.Event):string} categoryMapper | |
34 */ | 32 */ |
35 WebInspector.TimelineFrameModel = function(categoryMapper) | 33 WebInspector.TimelineFrameModel = class { |
36 { | 34 /** |
| 35 * @param {function(!WebInspector.TracingModel.Event):string} categoryMapper |
| 36 */ |
| 37 constructor(categoryMapper) { |
37 this._categoryMapper = categoryMapper; | 38 this._categoryMapper = categoryMapper; |
38 this.reset(); | 39 this.reset(); |
| 40 } |
| 41 |
| 42 /** |
| 43 * @return {!Array.<!WebInspector.TimelineFrame>} |
| 44 */ |
| 45 frames() { |
| 46 return this._frames; |
| 47 } |
| 48 |
| 49 /** |
| 50 * @param {number} startTime |
| 51 * @param {number} endTime |
| 52 * @return {!Array.<!WebInspector.TimelineFrame>} |
| 53 */ |
| 54 filteredFrames(startTime, endTime) { |
| 55 /** |
| 56 * @param {number} value |
| 57 * @param {!WebInspector.TimelineFrame} object |
| 58 * @return {number} |
| 59 */ |
| 60 function compareStartTime(value, object) { |
| 61 return value - object.startTime; |
| 62 } |
| 63 /** |
| 64 * @param {number} value |
| 65 * @param {!WebInspector.TimelineFrame} object |
| 66 * @return {number} |
| 67 */ |
| 68 function compareEndTime(value, object) { |
| 69 return value - object.endTime; |
| 70 } |
| 71 var frames = this._frames; |
| 72 var firstFrame = frames.lowerBound(startTime, compareEndTime); |
| 73 var lastFrame = frames.lowerBound(endTime, compareStartTime); |
| 74 return frames.slice(firstFrame, lastFrame); |
| 75 } |
| 76 |
| 77 /** |
| 78 * @param {!WebInspector.TracingModel.Event} rasterTask |
| 79 * @return {boolean} |
| 80 */ |
| 81 hasRasterTile(rasterTask) { |
| 82 var data = rasterTask.args['tileData']; |
| 83 if (!data) |
| 84 return false; |
| 85 var frameId = data['sourceFrameNumber']; |
| 86 var frame = frameId && this._frameById[frameId]; |
| 87 if (!frame || !frame.layerTree) |
| 88 return false; |
| 89 return true; |
| 90 } |
| 91 |
| 92 /** |
| 93 * @param {!WebInspector.TracingModel.Event} rasterTask |
| 94 * @return Promise<?{rect: !DOMAgent.Rect, snapshot: !WebInspector.PaintProfil
erSnapshot}>} |
| 95 */ |
| 96 rasterTilePromise(rasterTask) { |
| 97 if (!this._target) |
| 98 return Promise.resolve(null); |
| 99 var data = rasterTask.args['tileData']; |
| 100 var frameId = data['sourceFrameNumber']; |
| 101 var tileId = data['tileId'] && data['tileId']['id_ref']; |
| 102 var frame = frameId && this._frameById[frameId]; |
| 103 if (!frame || !frame.layerTree || !tileId) |
| 104 return Promise.resolve(null); |
| 105 |
| 106 return frame.layerTree.layerTreePromise().then(layerTree => layerTree && lay
erTree.pictureForRasterTile(tileId)); |
| 107 } |
| 108 |
| 109 reset() { |
| 110 this._minimumRecordTime = Infinity; |
| 111 this._frames = []; |
| 112 this._frameById = {}; |
| 113 this._lastFrame = null; |
| 114 this._lastLayerTree = null; |
| 115 this._mainFrameCommitted = false; |
| 116 this._mainFrameRequested = false; |
| 117 this._framePendingCommit = null; |
| 118 this._lastBeginFrame = null; |
| 119 this._lastNeedsBeginFrame = null; |
| 120 this._framePendingActivation = null; |
| 121 this._lastTaskBeginTime = null; |
| 122 this._target = null; |
| 123 this._sessionId = null; |
| 124 this._currentTaskTimeByCategory = {}; |
| 125 } |
| 126 |
| 127 /** |
| 128 * @param {number} startTime |
| 129 */ |
| 130 handleBeginFrame(startTime) { |
| 131 if (!this._lastFrame) |
| 132 this._startFrame(startTime); |
| 133 this._lastBeginFrame = startTime; |
| 134 } |
| 135 |
| 136 /** |
| 137 * @param {number} startTime |
| 138 */ |
| 139 handleDrawFrame(startTime) { |
| 140 if (!this._lastFrame) { |
| 141 this._startFrame(startTime); |
| 142 return; |
| 143 } |
| 144 |
| 145 // - if it wasn't drawn, it didn't happen! |
| 146 // - only show frames that either did not wait for the main thread frame or
had one committed. |
| 147 if (this._mainFrameCommitted || !this._mainFrameRequested) { |
| 148 if (this._lastNeedsBeginFrame) { |
| 149 var idleTimeEnd = this._framePendingActivation ? this._framePendingActiv
ation.triggerTime : |
| 150 (this._lastBeginFrame |
| this._lastNeedsBeginFrame); |
| 151 if (idleTimeEnd > this._lastFrame.startTime) { |
| 152 this._lastFrame.idle = true; |
| 153 this._startFrame(idleTimeEnd); |
| 154 if (this._framePendingActivation) |
| 155 this._commitPendingFrame(); |
| 156 this._lastBeginFrame = null; |
| 157 } |
| 158 this._lastNeedsBeginFrame = null; |
| 159 } |
| 160 this._startFrame(startTime); |
| 161 } |
| 162 this._mainFrameCommitted = false; |
| 163 } |
| 164 |
| 165 handleActivateLayerTree() { |
| 166 if (!this._lastFrame) |
| 167 return; |
| 168 if (this._framePendingActivation && !this._lastNeedsBeginFrame) |
| 169 this._commitPendingFrame(); |
| 170 } |
| 171 |
| 172 handleRequestMainThreadFrame() { |
| 173 if (!this._lastFrame) |
| 174 return; |
| 175 this._mainFrameRequested = true; |
| 176 } |
| 177 |
| 178 handleCompositeLayers() { |
| 179 if (!this._framePendingCommit) |
| 180 return; |
| 181 this._framePendingActivation = this._framePendingCommit; |
| 182 this._framePendingCommit = null; |
| 183 this._mainFrameRequested = false; |
| 184 this._mainFrameCommitted = true; |
| 185 } |
| 186 |
| 187 /** |
| 188 * @param {!WebInspector.TracingFrameLayerTree} layerTree |
| 189 */ |
| 190 handleLayerTreeSnapshot(layerTree) { |
| 191 this._lastLayerTree = layerTree; |
| 192 } |
| 193 |
| 194 /** |
| 195 * @param {number} startTime |
| 196 * @param {boolean} needsBeginFrame |
| 197 */ |
| 198 handleNeedFrameChanged(startTime, needsBeginFrame) { |
| 199 if (needsBeginFrame) |
| 200 this._lastNeedsBeginFrame = startTime; |
| 201 } |
| 202 |
| 203 /** |
| 204 * @param {number} startTime |
| 205 */ |
| 206 _startFrame(startTime) { |
| 207 if (this._lastFrame) |
| 208 this._flushFrame(this._lastFrame, startTime); |
| 209 this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime - this
._minimumRecordTime); |
| 210 } |
| 211 |
| 212 /** |
| 213 * @param {!WebInspector.TimelineFrame} frame |
| 214 * @param {number} endTime |
| 215 */ |
| 216 _flushFrame(frame, endTime) { |
| 217 frame._setLayerTree(this._lastLayerTree); |
| 218 frame._setEndTime(endTime); |
| 219 if (this._lastLayerTree) |
| 220 this._lastLayerTree._setPaints(frame._paints); |
| 221 if (this._frames.length && (frame.startTime !== this._frames.peekLast().endT
ime || frame.startTime > frame.endTime)) |
| 222 console.assert( |
| 223 false, `Inconsistent frame time for frame ${this._frames.length} (${fr
ame.startTime} - ${frame.endTime})`); |
| 224 this._frames.push(frame); |
| 225 if (typeof frame._mainFrameId === 'number') |
| 226 this._frameById[frame._mainFrameId] = frame; |
| 227 } |
| 228 |
| 229 _commitPendingFrame() { |
| 230 this._lastFrame._addTimeForCategories(this._framePendingActivation.timeByCat
egory); |
| 231 this._lastFrame._paints = this._framePendingActivation.paints; |
| 232 this._lastFrame._mainFrameId = this._framePendingActivation.mainFrameId; |
| 233 this._framePendingActivation = null; |
| 234 } |
| 235 |
| 236 /** |
| 237 * @param {!Array.<string>} types |
| 238 * @param {!WebInspector.TimelineModel.Record} record |
| 239 * @return {?WebInspector.TimelineModel.Record} record |
| 240 */ |
| 241 _findRecordRecursively(types, record) { |
| 242 if (types.indexOf(record.type()) >= 0) |
| 243 return record; |
| 244 if (!record.children()) |
| 245 return null; |
| 246 for (var i = 0; i < record.children().length; ++i) { |
| 247 var result = this._findRecordRecursively(types, record.children()[i]); |
| 248 if (result) |
| 249 return result; |
| 250 } |
| 251 return null; |
| 252 } |
| 253 |
| 254 /** |
| 255 * @param {?WebInspector.Target} target |
| 256 * @param {!Array.<!WebInspector.TracingModel.Event>} events |
| 257 * @param {string} sessionId |
| 258 */ |
| 259 addTraceEvents(target, events, sessionId) { |
| 260 this._target = target; |
| 261 this._sessionId = sessionId; |
| 262 if (!events.length) |
| 263 return; |
| 264 if (events[0].startTime < this._minimumRecordTime) |
| 265 this._minimumRecordTime = events[0].startTime; |
| 266 for (var i = 0; i < events.length; ++i) |
| 267 this._addTraceEvent(events[i]); |
| 268 } |
| 269 |
| 270 /** |
| 271 * @param {!WebInspector.TracingModel.Event} event |
| 272 */ |
| 273 _addTraceEvent(event) { |
| 274 var eventNames = WebInspector.TimelineModel.RecordType; |
| 275 |
| 276 if (event.name === eventNames.SetLayerTreeId) { |
| 277 var sessionId = event.args['sessionId'] || event.args['data']['sessionId']
; |
| 278 if (this._sessionId === sessionId) |
| 279 this._layerTreeId = event.args['layerTreeId'] || event.args['data']['lay
erTreeId']; |
| 280 } else if (event.name === eventNames.TracingStartedInPage) { |
| 281 this._mainThread = event.thread; |
| 282 } else if ( |
| 283 event.phase === WebInspector.TracingModel.Phase.SnapshotObject && |
| 284 event.name === eventNames.LayerTreeHostImplSnapshot && parseInt(event.id
, 0) === this._layerTreeId) { |
| 285 var snapshot = /** @type {!WebInspector.TracingModel.ObjectSnapshot} */ (e
vent); |
| 286 this.handleLayerTreeSnapshot(new WebInspector.TracingFrameLayerTree(this._
target, snapshot)); |
| 287 } else { |
| 288 this._processCompositorEvents(event); |
| 289 if (event.thread === this._mainThread) |
| 290 this._addMainThreadTraceEvent(event); |
| 291 else if (this._lastFrame && event.selfTime && !WebInspector.TracingModel.i
sTopLevelEvent(event)) |
| 292 this._lastFrame._addTimeForCategory(this._categoryMapper(event), event.s
elfTime); |
| 293 } |
| 294 } |
| 295 |
| 296 /** |
| 297 * @param {!WebInspector.TracingModel.Event} event |
| 298 */ |
| 299 _processCompositorEvents(event) { |
| 300 var eventNames = WebInspector.TimelineModel.RecordType; |
| 301 |
| 302 if (event.args['layerTreeId'] !== this._layerTreeId) |
| 303 return; |
| 304 |
| 305 var timestamp = event.startTime; |
| 306 if (event.name === eventNames.BeginFrame) |
| 307 this.handleBeginFrame(timestamp); |
| 308 else if (event.name === eventNames.DrawFrame) |
| 309 this.handleDrawFrame(timestamp); |
| 310 else if (event.name === eventNames.ActivateLayerTree) |
| 311 this.handleActivateLayerTree(); |
| 312 else if (event.name === eventNames.RequestMainThreadFrame) |
| 313 this.handleRequestMainThreadFrame(); |
| 314 else if (event.name === eventNames.NeedsBeginFrameChanged) |
| 315 this.handleNeedFrameChanged(timestamp, event.args['data'] && event.args['d
ata']['needsBeginFrame']); |
| 316 } |
| 317 |
| 318 /** |
| 319 * @param {!WebInspector.TracingModel.Event} event |
| 320 */ |
| 321 _addMainThreadTraceEvent(event) { |
| 322 var eventNames = WebInspector.TimelineModel.RecordType; |
| 323 var timestamp = event.startTime; |
| 324 var selfTime = event.selfTime || 0; |
| 325 |
| 326 if (WebInspector.TracingModel.isTopLevelEvent(event)) { |
| 327 this._currentTaskTimeByCategory = {}; |
| 328 this._lastTaskBeginTime = event.startTime; |
| 329 } |
| 330 if (!this._framePendingCommit && WebInspector.TimelineFrameModel._mainFrameM
arkers.indexOf(event.name) >= 0) |
| 331 this._framePendingCommit = |
| 332 new WebInspector.PendingFrame(this._lastTaskBeginTime || event.startTi
me, this._currentTaskTimeByCategory); |
| 333 if (!this._framePendingCommit) { |
| 334 this._addTimeForCategory(this._currentTaskTimeByCategory, event); |
| 335 return; |
| 336 } |
| 337 this._addTimeForCategory(this._framePendingCommit.timeByCategory, event); |
| 338 |
| 339 if (event.name === eventNames.BeginMainThreadFrame && event.args['data'] &&
event.args['data']['frameId']) |
| 340 this._framePendingCommit.mainFrameId = event.args['data']['frameId']; |
| 341 if (event.name === eventNames.Paint && event.args['data']['layerId'] && even
t.picture && this._target) |
| 342 this._framePendingCommit.paints.push(new WebInspector.LayerPaintEvent(even
t, this._target)); |
| 343 if (event.name === eventNames.CompositeLayers && event.args['layerTreeId'] =
== this._layerTreeId) |
| 344 this.handleCompositeLayers(); |
| 345 } |
| 346 |
| 347 /** |
| 348 * @param {!Object.<string, number>} timeByCategory |
| 349 * @param {!WebInspector.TracingModel.Event} event |
| 350 */ |
| 351 _addTimeForCategory(timeByCategory, event) { |
| 352 if (!event.selfTime) |
| 353 return; |
| 354 var categoryName = this._categoryMapper(event); |
| 355 timeByCategory[categoryName] = (timeByCategory[categoryName] || 0) + event.s
elfTime; |
| 356 } |
39 }; | 357 }; |
40 | 358 |
41 WebInspector.TimelineFrameModel._mainFrameMarkers = [ | 359 WebInspector.TimelineFrameModel._mainFrameMarkers = [ |
42 WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation, | 360 WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation, |
43 WebInspector.TimelineModel.RecordType.InvalidateLayout, | 361 WebInspector.TimelineModel.RecordType.InvalidateLayout, WebInspector.TimelineM
odel.RecordType.BeginMainThreadFrame, |
44 WebInspector.TimelineModel.RecordType.BeginMainThreadFrame, | 362 WebInspector.TimelineModel.RecordType.ScrollLayer |
45 WebInspector.TimelineModel.RecordType.ScrollLayer | |
46 ]; | 363 ]; |
47 | 364 |
48 WebInspector.TimelineFrameModel.prototype = { | |
49 /** | |
50 * @return {!Array.<!WebInspector.TimelineFrame>} | |
51 */ | |
52 frames: function() | |
53 { | |
54 return this._frames; | |
55 }, | |
56 | |
57 /** | |
58 * @param {number} startTime | |
59 * @param {number} endTime | |
60 * @return {!Array.<!WebInspector.TimelineFrame>} | |
61 */ | |
62 filteredFrames: function(startTime, endTime) | |
63 { | |
64 /** | |
65 * @param {number} value | |
66 * @param {!WebInspector.TimelineFrame} object | |
67 * @return {number} | |
68 */ | |
69 function compareStartTime(value, object) | |
70 { | |
71 return value - object.startTime; | |
72 } | |
73 /** | |
74 * @param {number} value | |
75 * @param {!WebInspector.TimelineFrame} object | |
76 * @return {number} | |
77 */ | |
78 function compareEndTime(value, object) | |
79 { | |
80 return value - object.endTime; | |
81 } | |
82 var frames = this._frames; | |
83 var firstFrame = frames.lowerBound(startTime, compareEndTime); | |
84 var lastFrame = frames.lowerBound(endTime, compareStartTime); | |
85 return frames.slice(firstFrame, lastFrame); | |
86 }, | |
87 | |
88 /** | |
89 * @param {!WebInspector.TracingModel.Event} rasterTask | |
90 * @return {boolean} | |
91 */ | |
92 hasRasterTile: function(rasterTask) | |
93 { | |
94 var data = rasterTask.args["tileData"]; | |
95 if (!data) | |
96 return false; | |
97 var frameId = data["sourceFrameNumber"]; | |
98 var frame = frameId && this._frameById[frameId]; | |
99 if (!frame || !frame.layerTree) | |
100 return false; | |
101 return true; | |
102 }, | |
103 | |
104 /** | |
105 * @param {!WebInspector.TracingModel.Event} rasterTask | |
106 * @return Promise<?{rect: !DOMAgent.Rect, snapshot: !WebInspector.PaintProf
ilerSnapshot}>} | |
107 */ | |
108 rasterTilePromise: function(rasterTask) | |
109 { | |
110 if (!this._target) | |
111 return Promise.resolve(null); | |
112 var data = rasterTask.args["tileData"]; | |
113 var frameId = data["sourceFrameNumber"]; | |
114 var tileId = data["tileId"] && data["tileId"]["id_ref"]; | |
115 var frame = frameId && this._frameById[frameId]; | |
116 if (!frame || !frame.layerTree || !tileId) | |
117 return Promise.resolve(null); | |
118 | |
119 return frame.layerTree.layerTreePromise().then(layerTree => layerTree &&
layerTree.pictureForRasterTile(tileId)); | |
120 }, | |
121 | |
122 reset: function() | |
123 { | |
124 this._minimumRecordTime = Infinity; | |
125 this._frames = []; | |
126 this._frameById = {}; | |
127 this._lastFrame = null; | |
128 this._lastLayerTree = null; | |
129 this._mainFrameCommitted = false; | |
130 this._mainFrameRequested = false; | |
131 this._framePendingCommit = null; | |
132 this._lastBeginFrame = null; | |
133 this._lastNeedsBeginFrame = null; | |
134 this._framePendingActivation = null; | |
135 this._lastTaskBeginTime = null; | |
136 this._target = null; | |
137 this._sessionId = null; | |
138 this._currentTaskTimeByCategory = {}; | |
139 }, | |
140 | |
141 /** | |
142 * @param {number} startTime | |
143 */ | |
144 handleBeginFrame: function(startTime) | |
145 { | |
146 if (!this._lastFrame) | |
147 this._startFrame(startTime); | |
148 this._lastBeginFrame = startTime; | |
149 }, | |
150 | |
151 /** | |
152 * @param {number} startTime | |
153 */ | |
154 handleDrawFrame: function(startTime) | |
155 { | |
156 if (!this._lastFrame) { | |
157 this._startFrame(startTime); | |
158 return; | |
159 } | |
160 | |
161 // - if it wasn't drawn, it didn't happen! | |
162 // - only show frames that either did not wait for the main thread frame
or had one committed. | |
163 if (this._mainFrameCommitted || !this._mainFrameRequested) { | |
164 if (this._lastNeedsBeginFrame) { | |
165 var idleTimeEnd = this._framePendingActivation ? this._framePend
ingActivation.triggerTime : (this._lastBeginFrame || this._lastNeedsBeginFrame); | |
166 if (idleTimeEnd > this._lastFrame.startTime) { | |
167 this._lastFrame.idle = true; | |
168 this._startFrame(idleTimeEnd); | |
169 if (this._framePendingActivation) | |
170 this._commitPendingFrame(); | |
171 this._lastBeginFrame = null; | |
172 } | |
173 this._lastNeedsBeginFrame = null; | |
174 } | |
175 this._startFrame(startTime); | |
176 } | |
177 this._mainFrameCommitted = false; | |
178 }, | |
179 | |
180 handleActivateLayerTree: function() | |
181 { | |
182 if (!this._lastFrame) | |
183 return; | |
184 if (this._framePendingActivation && !this._lastNeedsBeginFrame) | |
185 this._commitPendingFrame(); | |
186 }, | |
187 | |
188 handleRequestMainThreadFrame: function() | |
189 { | |
190 if (!this._lastFrame) | |
191 return; | |
192 this._mainFrameRequested = true; | |
193 }, | |
194 | |
195 handleCompositeLayers: function() | |
196 { | |
197 if (!this._framePendingCommit) | |
198 return; | |
199 this._framePendingActivation = this._framePendingCommit; | |
200 this._framePendingCommit = null; | |
201 this._mainFrameRequested = false; | |
202 this._mainFrameCommitted = true; | |
203 }, | |
204 | |
205 /** | |
206 * @param {!WebInspector.TracingFrameLayerTree} layerTree | |
207 */ | |
208 handleLayerTreeSnapshot: function(layerTree) | |
209 { | |
210 this._lastLayerTree = layerTree; | |
211 }, | |
212 | |
213 /** | |
214 * @param {number} startTime | |
215 * @param {boolean} needsBeginFrame | |
216 */ | |
217 handleNeedFrameChanged: function(startTime, needsBeginFrame) | |
218 { | |
219 if (needsBeginFrame) | |
220 this._lastNeedsBeginFrame = startTime; | |
221 }, | |
222 | |
223 /** | |
224 * @param {number} startTime | |
225 */ | |
226 _startFrame: function(startTime) | |
227 { | |
228 if (this._lastFrame) | |
229 this._flushFrame(this._lastFrame, startTime); | |
230 this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime -
this._minimumRecordTime); | |
231 }, | |
232 | |
233 /** | |
234 * @param {!WebInspector.TimelineFrame} frame | |
235 * @param {number} endTime | |
236 */ | |
237 _flushFrame: function(frame, endTime) | |
238 { | |
239 frame._setLayerTree(this._lastLayerTree); | |
240 frame._setEndTime(endTime); | |
241 if (this._lastLayerTree) | |
242 this._lastLayerTree._setPaints(frame._paints); | |
243 if (this._frames.length && (frame.startTime !== this._frames.peekLast().
endTime || frame.startTime > frame.endTime)) | |
244 console.assert(false, `Inconsistent frame time for frame ${this._fra
mes.length} (${frame.startTime} - ${frame.endTime})`); | |
245 this._frames.push(frame); | |
246 if (typeof frame._mainFrameId === "number") | |
247 this._frameById[frame._mainFrameId] = frame; | |
248 }, | |
249 | |
250 _commitPendingFrame: function() | |
251 { | |
252 this._lastFrame._addTimeForCategories(this._framePendingActivation.timeB
yCategory); | |
253 this._lastFrame._paints = this._framePendingActivation.paints; | |
254 this._lastFrame._mainFrameId = this._framePendingActivation.mainFrameId; | |
255 this._framePendingActivation = null; | |
256 }, | |
257 | |
258 /** | |
259 * @param {!Array.<string>} types | |
260 * @param {!WebInspector.TimelineModel.Record} record | |
261 * @return {?WebInspector.TimelineModel.Record} record | |
262 */ | |
263 _findRecordRecursively: function(types, record) | |
264 { | |
265 if (types.indexOf(record.type()) >= 0) | |
266 return record; | |
267 if (!record.children()) | |
268 return null; | |
269 for (var i = 0; i < record.children().length; ++i) { | |
270 var result = this._findRecordRecursively(types, record.children()[i]
); | |
271 if (result) | |
272 return result; | |
273 } | |
274 return null; | |
275 }, | |
276 | |
277 /** | |
278 * @param {?WebInspector.Target} target | |
279 * @param {!Array.<!WebInspector.TracingModel.Event>} events | |
280 * @param {string} sessionId | |
281 */ | |
282 addTraceEvents: function(target, events, sessionId) | |
283 { | |
284 this._target = target; | |
285 this._sessionId = sessionId; | |
286 if (!events.length) | |
287 return; | |
288 if (events[0].startTime < this._minimumRecordTime) | |
289 this._minimumRecordTime = events[0].startTime; | |
290 for (var i = 0; i < events.length; ++i) | |
291 this._addTraceEvent(events[i]); | |
292 }, | |
293 | |
294 /** | |
295 * @param {!WebInspector.TracingModel.Event} event | |
296 */ | |
297 _addTraceEvent: function(event) | |
298 { | |
299 var eventNames = WebInspector.TimelineModel.RecordType; | |
300 | |
301 if (event.name === eventNames.SetLayerTreeId) { | |
302 var sessionId = event.args["sessionId"] || event.args["data"]["sessi
onId"]; | |
303 if (this._sessionId === sessionId) | |
304 this._layerTreeId = event.args["layerTreeId"] || event.args["dat
a"]["layerTreeId"]; | |
305 } else if (event.name === eventNames.TracingStartedInPage) { | |
306 this._mainThread = event.thread; | |
307 } else if (event.phase === WebInspector.TracingModel.Phase.SnapshotObjec
t && event.name === eventNames.LayerTreeHostImplSnapshot && parseInt(event.id, 0
) === this._layerTreeId) { | |
308 var snapshot = /** @type {!WebInspector.TracingModel.ObjectSnapshot}
*/ (event); | |
309 this.handleLayerTreeSnapshot(new WebInspector.TracingFrameLayerTree(
this._target, snapshot)); | |
310 } else { | |
311 this._processCompositorEvents(event); | |
312 if (event.thread === this._mainThread) | |
313 this._addMainThreadTraceEvent(event); | |
314 else if (this._lastFrame && event.selfTime && !WebInspector.TracingM
odel.isTopLevelEvent(event)) | |
315 this._lastFrame._addTimeForCategory(this._categoryMapper(event),
event.selfTime); | |
316 } | |
317 }, | |
318 | |
319 /** | |
320 * @param {!WebInspector.TracingModel.Event} event | |
321 */ | |
322 _processCompositorEvents: function(event) | |
323 { | |
324 var eventNames = WebInspector.TimelineModel.RecordType; | |
325 | |
326 if (event.args["layerTreeId"] !== this._layerTreeId) | |
327 return; | |
328 | |
329 var timestamp = event.startTime; | |
330 if (event.name === eventNames.BeginFrame) | |
331 this.handleBeginFrame(timestamp); | |
332 else if (event.name === eventNames.DrawFrame) | |
333 this.handleDrawFrame(timestamp); | |
334 else if (event.name === eventNames.ActivateLayerTree) | |
335 this.handleActivateLayerTree(); | |
336 else if (event.name === eventNames.RequestMainThreadFrame) | |
337 this.handleRequestMainThreadFrame(); | |
338 else if (event.name === eventNames.NeedsBeginFrameChanged) | |
339 this.handleNeedFrameChanged(timestamp, event.args["data"] && event.a
rgs["data"]["needsBeginFrame"]); | |
340 }, | |
341 | |
342 /** | |
343 * @param {!WebInspector.TracingModel.Event} event | |
344 */ | |
345 _addMainThreadTraceEvent: function(event) | |
346 { | |
347 var eventNames = WebInspector.TimelineModel.RecordType; | |
348 var timestamp = event.startTime; | |
349 var selfTime = event.selfTime || 0; | |
350 | |
351 if (WebInspector.TracingModel.isTopLevelEvent(event)) { | |
352 this._currentTaskTimeByCategory = {}; | |
353 this._lastTaskBeginTime = event.startTime; | |
354 } | |
355 if (!this._framePendingCommit && WebInspector.TimelineFrameModel._mainFr
ameMarkers.indexOf(event.name) >= 0) | |
356 this._framePendingCommit = new WebInspector.PendingFrame(this._lastT
askBeginTime || event.startTime, this._currentTaskTimeByCategory); | |
357 if (!this._framePendingCommit) { | |
358 this._addTimeForCategory(this._currentTaskTimeByCategory, event); | |
359 return; | |
360 } | |
361 this._addTimeForCategory(this._framePendingCommit.timeByCategory, event)
; | |
362 | |
363 if (event.name === eventNames.BeginMainThreadFrame && event.args["data"]
&& event.args["data"]["frameId"]) | |
364 this._framePendingCommit.mainFrameId = event.args["data"]["frameId"]
; | |
365 if (event.name === eventNames.Paint && event.args["data"]["layerId"] &&
event.picture && this._target) | |
366 this._framePendingCommit.paints.push(new WebInspector.LayerPaintEven
t(event, this._target)); | |
367 if (event.name === eventNames.CompositeLayers && event.args["layerTreeId
"] === this._layerTreeId) | |
368 this.handleCompositeLayers(); | |
369 }, | |
370 | |
371 /** | |
372 * @param {!Object.<string, number>} timeByCategory | |
373 * @param {!WebInspector.TracingModel.Event} event | |
374 */ | |
375 _addTimeForCategory: function(timeByCategory, event) | |
376 { | |
377 if (!event.selfTime) | |
378 return; | |
379 var categoryName = this._categoryMapper(event); | |
380 timeByCategory[categoryName] = (timeByCategory[categoryName] || 0) + eve
nt.selfTime; | |
381 }, | |
382 }; | |
383 | |
384 /** | 365 /** |
385 * @constructor | 366 * @unrestricted |
386 * @param {!WebInspector.Target} target | |
387 * @param {!WebInspector.TracingModel.ObjectSnapshot} snapshot | |
388 */ | 367 */ |
389 WebInspector.TracingFrameLayerTree = function(target, snapshot) | 368 WebInspector.TracingFrameLayerTree = class { |
390 { | 369 /** |
| 370 * @param {!WebInspector.Target} target |
| 371 * @param {!WebInspector.TracingModel.ObjectSnapshot} snapshot |
| 372 */ |
| 373 constructor(target, snapshot) { |
391 this._target = target; | 374 this._target = target; |
392 this._snapshot = snapshot; | 375 this._snapshot = snapshot; |
393 /** @type {!Array<!WebInspector.LayerPaintEvent>|undefined} */ | 376 /** @type {!Array<!WebInspector.LayerPaintEvent>|undefined} */ |
394 this._paints; | 377 this._paints; |
| 378 } |
| 379 |
| 380 /** |
| 381 * @return {!Promise<?WebInspector.TracingLayerTree>} |
| 382 */ |
| 383 layerTreePromise() { |
| 384 return this._snapshot.objectPromise().then(result => { |
| 385 if (!result) |
| 386 return null; |
| 387 var viewport = result['device_viewport_size']; |
| 388 var tiles = result['active_tiles']; |
| 389 var rootLayer = result['active_tree']['root_layer']; |
| 390 var layers = result['active_tree']['layers']; |
| 391 var layerTree = new WebInspector.TracingLayerTree(this._target); |
| 392 layerTree.setViewportSize(viewport); |
| 393 layerTree.setTiles(tiles); |
| 394 return new Promise( |
| 395 resolve => layerTree.setLayers(rootLayer, layers, this._paints || [],
() => resolve(layerTree))); |
| 396 }); |
| 397 } |
| 398 |
| 399 /** |
| 400 * @return {!Array<!WebInspector.LayerPaintEvent>} |
| 401 */ |
| 402 paints() { |
| 403 return this._paints || []; |
| 404 } |
| 405 |
| 406 /** |
| 407 * @param {!Array<!WebInspector.LayerPaintEvent>} paints |
| 408 */ |
| 409 _setPaints(paints) { |
| 410 this._paints = paints; |
| 411 } |
395 }; | 412 }; |
396 | 413 |
397 WebInspector.TracingFrameLayerTree.prototype = { | |
398 /** | |
399 * @return {!Promise<?WebInspector.TracingLayerTree>} | |
400 */ | |
401 layerTreePromise: function() | |
402 { | |
403 return this._snapshot.objectPromise().then(result => { | |
404 if (!result) | |
405 return null; | |
406 var viewport = result["device_viewport_size"]; | |
407 var tiles = result["active_tiles"]; | |
408 var rootLayer = result["active_tree"]["root_layer"]; | |
409 var layers = result["active_tree"]["layers"]; | |
410 var layerTree = new WebInspector.TracingLayerTree(this._target); | |
411 layerTree.setViewportSize(viewport); | |
412 layerTree.setTiles(tiles); | |
413 return new Promise(resolve => layerTree.setLayers(rootLayer, layers,
this._paints || [], () => resolve(layerTree))); | |
414 }); | |
415 }, | |
416 | |
417 /** | |
418 * @return {!Array<!WebInspector.LayerPaintEvent>} | |
419 */ | |
420 paints: function() | |
421 { | |
422 return this._paints || []; | |
423 }, | |
424 | |
425 /** | |
426 * @param {!Array<!WebInspector.LayerPaintEvent>} paints | |
427 */ | |
428 _setPaints: function(paints) | |
429 { | |
430 this._paints = paints; | |
431 } | |
432 }; | |
433 | |
434 | |
435 /** | 414 /** |
436 * @constructor | 415 * @unrestricted |
437 * @param {number} startTime | |
438 * @param {number} startTimeOffset | |
439 */ | 416 */ |
440 WebInspector.TimelineFrame = function(startTime, startTimeOffset) | 417 WebInspector.TimelineFrame = class { |
441 { | 418 /** |
| 419 * @param {number} startTime |
| 420 * @param {number} startTimeOffset |
| 421 */ |
| 422 constructor(startTime, startTimeOffset) { |
442 this.startTime = startTime; | 423 this.startTime = startTime; |
443 this.startTimeOffset = startTimeOffset; | 424 this.startTimeOffset = startTimeOffset; |
444 this.endTime = this.startTime; | 425 this.endTime = this.startTime; |
445 this.duration = 0; | 426 this.duration = 0; |
446 this.timeByCategory = {}; | 427 this.timeByCategory = {}; |
447 this.cpuTime = 0; | 428 this.cpuTime = 0; |
448 this.idle = false; | 429 this.idle = false; |
449 /** @type {?WebInspector.TracingFrameLayerTree} */ | 430 /** @type {?WebInspector.TracingFrameLayerTree} */ |
450 this.layerTree = null; | 431 this.layerTree = null; |
451 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ | 432 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ |
452 this._paints = []; | 433 this._paints = []; |
453 /** @type {number|undefined} */ | 434 /** @type {number|undefined} */ |
454 this._mainFrameId = undefined; | 435 this._mainFrameId = undefined; |
455 }; | 436 } |
456 | 437 |
457 WebInspector.TimelineFrame.prototype = { | 438 /** |
458 /** | 439 * @return {boolean} |
459 * @return {boolean} | 440 */ |
460 */ | 441 hasWarnings() { |
461 hasWarnings: function() | 442 var /** @const */ longFrameDurationThresholdMs = 22; |
462 { | 443 return !this.idle && this.duration > longFrameDurationThresholdMs; |
463 var /** @const */ longFrameDurationThresholdMs = 22; | 444 } |
464 return !this.idle && this.duration > longFrameDurationThresholdMs; | |
465 }, | |
466 | 445 |
467 /** | 446 /** |
468 * @param {number} endTime | 447 * @param {number} endTime |
469 */ | 448 */ |
470 _setEndTime: function(endTime) | 449 _setEndTime(endTime) { |
471 { | 450 this.endTime = endTime; |
472 this.endTime = endTime; | 451 this.duration = this.endTime - this.startTime; |
473 this.duration = this.endTime - this.startTime; | 452 } |
474 }, | |
475 | 453 |
476 /** | 454 /** |
477 * @param {?WebInspector.TracingFrameLayerTree} layerTree | 455 * @param {?WebInspector.TracingFrameLayerTree} layerTree |
478 */ | 456 */ |
479 _setLayerTree: function(layerTree) | 457 _setLayerTree(layerTree) { |
480 { | 458 this.layerTree = layerTree; |
481 this.layerTree = layerTree; | 459 } |
482 }, | |
483 | 460 |
484 /** | 461 /** |
485 * @param {!Object} timeByCategory | 462 * @param {!Object} timeByCategory |
486 */ | 463 */ |
487 _addTimeForCategories: function(timeByCategory) | 464 _addTimeForCategories(timeByCategory) { |
488 { | 465 for (var category in timeByCategory) |
489 for (var category in timeByCategory) | 466 this._addTimeForCategory(category, timeByCategory[category]); |
490 this._addTimeForCategory(category, timeByCategory[category]); | 467 } |
491 }, | |
492 | 468 |
493 /** | 469 /** |
494 * @param {string} category | 470 * @param {string} category |
495 * @param {number} time | 471 * @param {number} time |
496 */ | 472 */ |
497 _addTimeForCategory: function(category, time) | 473 _addTimeForCategory(category, time) { |
498 { | 474 this.timeByCategory[category] = (this.timeByCategory[category] || 0) + time; |
499 this.timeByCategory[category] = (this.timeByCategory[category] || 0) + t
ime; | 475 this.cpuTime += time; |
500 this.cpuTime += time; | 476 } |
501 }, | |
502 }; | 477 }; |
503 | 478 |
504 /** | 479 /** |
505 * @constructor | 480 * @unrestricted |
506 * @param {!WebInspector.TracingModel.Event} event | |
507 * @param {?WebInspector.Target} target | |
508 */ | 481 */ |
509 WebInspector.LayerPaintEvent = function(event, target) | 482 WebInspector.LayerPaintEvent = class { |
510 { | 483 /** |
| 484 * @param {!WebInspector.TracingModel.Event} event |
| 485 * @param {?WebInspector.Target} target |
| 486 */ |
| 487 constructor(event, target) { |
511 this._event = event; | 488 this._event = event; |
512 this._target = target; | 489 this._target = target; |
513 }; | 490 } |
514 | 491 |
515 WebInspector.LayerPaintEvent.prototype = { | 492 /** |
516 /** | 493 * @return {string} |
517 * @return {string} | 494 */ |
518 */ | 495 layerId() { |
519 layerId: function() | 496 return this._event.args['data']['layerId']; |
520 { | 497 } |
521 return this._event.args["data"]["layerId"]; | |
522 }, | |
523 | 498 |
524 /** | 499 /** |
525 * @return {!WebInspector.TracingModel.Event} | 500 * @return {!WebInspector.TracingModel.Event} |
526 */ | 501 */ |
527 event: function() | 502 event() { |
528 { | 503 return this._event; |
529 return this._event; | 504 } |
530 }, | |
531 | 505 |
532 /** | 506 /** |
533 * @return {!Promise<?{rect: !Array<number>, serializedPicture: string}>} | 507 * @return {!Promise<?{rect: !Array<number>, serializedPicture: string}>} |
534 */ | 508 */ |
535 picturePromise: function() | 509 picturePromise() { |
536 { | 510 return this._event.picture.objectPromise().then(result => { |
537 return this._event.picture.objectPromise().then(result => { | 511 if (!result) |
538 if (!result) | 512 return null; |
539 return null; | 513 var rect = result['params'] && result['params']['layer_rect']; |
540 var rect = result["params"] && result["params"]["layer_rect"]; | 514 var picture = result['skp64']; |
541 var picture = result["skp64"]; | 515 return rect && picture ? {rect: rect, serializedPicture: picture} : null; |
542 return rect && picture ? {rect: rect, serializedPicture: picture} :
null; | 516 }); |
543 }); | 517 } |
544 }, | |
545 | 518 |
546 /** | 519 /** |
547 * @return !Promise<?{rect: !Array<number>, snapshot: !WebInspector.PaintPro
filerSnapshot}>} | 520 * @return !Promise<?{rect: !Array<number>, snapshot: !WebInspector.PaintProfi
lerSnapshot}>} |
548 */ | 521 */ |
549 snapshotPromise: function() | 522 snapshotPromise() { |
550 { | 523 return this.picturePromise().then(picture => { |
551 return this.picturePromise().then(picture => { | 524 if (!picture || !this._target) |
552 if (!picture || !this._target) | 525 return null; |
553 return null; | 526 return WebInspector.PaintProfilerSnapshot.load(this._target, picture.seria
lizedPicture) |
554 return WebInspector.PaintProfilerSnapshot.load(this._target, picture
.serializedPicture).then(snapshot => snapshot ? {rect: picture.rect, snapshot: s
napshot} : null); | 527 .then(snapshot => snapshot ? {rect: picture.rect, snapshot: snapshot}
: null); |
555 }); | 528 }); |
556 } | 529 } |
557 }; | 530 }; |
558 | 531 |
559 /** | 532 /** |
560 * @constructor | 533 * @unrestricted |
561 * @param {number} triggerTime | |
562 * @param {!Object.<string, number>} timeByCategory | |
563 */ | 534 */ |
564 WebInspector.PendingFrame = function(triggerTime, timeByCategory) | 535 WebInspector.PendingFrame = class { |
565 { | 536 /** |
| 537 * @param {number} triggerTime |
| 538 * @param {!Object.<string, number>} timeByCategory |
| 539 */ |
| 540 constructor(triggerTime, timeByCategory) { |
566 /** @type {!Object.<string, number>} */ | 541 /** @type {!Object.<string, number>} */ |
567 this.timeByCategory = timeByCategory; | 542 this.timeByCategory = timeByCategory; |
568 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ | 543 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ |
569 this.paints = []; | 544 this.paints = []; |
570 /** @type {number|undefined} */ | 545 /** @type {number|undefined} */ |
571 this.mainFrameId = undefined; | 546 this.mainFrameId = undefined; |
572 this.triggerTime = triggerTime; | 547 this.triggerTime = triggerTime; |
| 548 } |
573 }; | 549 }; |
OLD | NEW |