1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 /** 6 * @constructor 7 * @extends {WebInspector.TimelineUIUtils} 8 */ 9 WebInspector.TracingTimelineUIUtils = function() 10 { 11 WebInspector.TimelineUIUtils.call(this); 12 } 13 14 WebInspector.TracingTimelineUIUtils.prototype = { 15 /** 16 * @param {!WebInspector.TimelineModel.Record} record 17 * @return {boolean} 18 */ 19 isBeginFrame: function(record) 20 { 21 return record.type() === WebInspector.TracingTimelineModel.RecordType.BeginFrame; 22 }, 23 24 /** 25 * @param {!WebInspector.TimelineModel.Record} record 26 * @return {boolean} 27 */ 28 isProgram: function(record) 29 { 30 return record.type() === WebInspector.TracingTimelineModel.RecordType.Program; 31 }, 32 33 /** 34 * @param {string} recordType 35 * @return {boolean} 36 */ 37 isCoalescable: function(recordType) 38 { 39 return !!WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[recordType]; 40 }, 41 42 /** 43 * @param {!WebInspector.TimelineModel.Record} record 44 * @return {boolean} 45 */ 46 isEventDivider: function(record) 47 { 48 return WebInspector.TracingTimelineUIUtils.isMarkerEvent(record.traceEvent()); 49 }, 50 51 /** 52 * @param {!WebInspector.TimelineModel.Record} record 53 * @return {?Object} 54 */ 55 countersForRecord: function(record) 56 { 57 return record.type() === WebInspector.TracingTimelineModel.RecordType.UpdateCounters ? record.data() : null; 58 }, 59 60 /** 61 * @param {!WebInspector.TimelineModel.Record} record 62 * @return {?Object} 63 */ 64 highlightQuadForRecord: function(record) 65 { 66 return record.traceEvent().highlightQuad || null; 67 }, 68 69 /** 70 * @param {!WebInspector.TimelineModel.Record} record 71 * @return {string} 72 */ 73 titleForRecord: function(record) 74 { 75 var event = record.traceEvent(); 76 return WebInspector.TracingTimelineUIUtils.eventTitle(event, record.timelineModel()); 77 }, 78 79 /** 80 * @param {!WebInspector.TimelineModel.Record} record 81 * @return {!WebInspector.TimelineCategory} 82 */ 83 categoryForRecord: function(record) 84 { 85 return WebInspector.TracingTimelineUIUtils.eventStyle(record.traceEvent()).category; 86 }, 87 88 /** 89 * @param {!WebInspector.TimelineModel.Record} record 90 * @param {!WebInspector.Linkifier} linkifier 91 * @return {?Node} 92 */ 93 buildDetailsNode: function(record, linkifier) 94 { 95 return WebInspector.TracingTimelineUIUtils.buildDetailsNodeForTraceEvent(record.traceEvent(), linkifier); 96 }, 97 98 /** 99 * @param {!WebInspector.TimelineModel.Record} record 100 * @param {!WebInspector.TimelineModel} model 101 * @param {!WebInspector.Linkifier} linkifier 102 * @param {function(!DocumentFragment)} callback 103 */ 104 generateDetailsContent: function(record, model, linkifier, callback) 105 { 106 if (!(model instanceof WebInspector.TracingTimelineModel)) 107 throw new Error("Illegal argument."); 108 var tracingTimelineModel = /** @type {!WebInspector.TracingTimelineModel} */ (model); 109 WebInspector.TracingTimelineUIUtils.buildTraceEventDetails(record.traceEvent(), tracingTimelineModel, linkifier, callback); 110 }, 111 112 /** 113 * @return {!Element} 114 */ 115 createBeginFrameDivider: function() 116 { 117 return this.createEventDivider(WebInspector.TracingTimelineModel.RecordType.BeginFrame); 118 }, 119 120 /** 121 * @param {string} recordType 122 * @param {string=} title 123 * @return {!Element} 124 */ 125 createEventDivider: function(recordType, title) 126 { 127 return WebInspector.TracingTimelineUIUtils._createEventDivider(recordType, title); 128 }, 129 130 /** 131 * @param {!WebInspector.TimelineModel.Record} record 132 * @param {!RegExp} regExp 133 * @return {boolean} 134 */ 135 testContentMatching: function(record, regExp) 136 { 137 var traceEvent = record.traceEvent(); 138 var title = WebInspector.TracingTimelineUIUtils.eventStyle(traceEvent).title; 139 var tokens = [title]; 140 for (var argName in traceEvent.args) { 141 var argValue = traceEvent.args[argName]; 142 for (var key in argValue) 143 tokens.push(argValue[key]); 144 } 145 return regExp.test(tokens.join("|")); 146 }, 147 148 /** 149 * @param {!Object} total 150 * @param {!WebInspector.TimelineModel.Record} record 151 */ 152 aggregateTimeForRecord: function(total, record) 153 { 154 var traceEvent = record.traceEvent(); 155 var model = record._model; 156 WebInspector.TracingTimelineUIUtils._aggregatedStatsForTraceEvent(total, model, traceEvent); 157 }, 158 159 /** 160 * @return {!WebInspector.TimelineModel.Filter} 161 */ 162 hiddenRecordsFilter: function() 163 { 164 return new WebInspector.TimelineRecordVisibleTypeFilter(WebInspector.TracingTimelineUIUtils._visibleTypes()); 165 }, 166 167 /** 168 * @return {?WebInspector.TimelineModel.Filter} 169 */ 170 hiddenEmptyRecordsFilter: function() 171 { 172 var hiddenEmptyRecords = [WebInspector.TimelineModel.RecordType.EventDispatch]; 173 return new WebInspector.TimelineRecordHiddenEmptyTypeFilter(hiddenEmptyRecords); 174 }, 175 176 __proto__: WebInspector.TimelineUIUtils.prototype 177 } 178 179 /** 180 * @constructor 181 * @param {string} title 182 * @param {!WebInspector.TimelineCategory} category 183 * @param {boolean=} hidden 184 */ 185 WebInspector.TimelineRecordStyle = function(title, category, hidden) 186 { 187 this.title = title; 188 this.category = category; 189 this.hidden = !!hidden; 190 } 191 192 /** 193 * @return {!Object.<string, !WebInspector.TimelineRecordStyle>} 194 */ 195 WebInspector.TracingTimelineUIUtils._initEventStyles = function() 196 { 197 if (WebInspector.TracingTimelineUIUtils._eventStylesMap) 198 return WebInspector.TracingTimelineUIUtils._eventStylesMap; 199 200 var recordTypes = WebInspector.TracingTimelineModel.RecordType; 201 var categories = WebInspector.TimelineUIUtils.categories(); 202 203 var eventStyles = {}; 204 eventStyles[recordTypes.Program] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Other"), categories["other"]); 205 eventStyles[recordTypes.EventDispatch] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Event"), categories["scripting"]); 206 eventStyles[recordTypes.RequestMainThreadFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Request Main Thread Frame"), categories["rendering"], true); 207 eventStyles[recordTypes.BeginFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Frame Start"), categories["rendering"], true); 208 eventStyles[recordTypes.BeginMainThreadFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Frame Start (main thread)"), categories["rendering"], true); 209 eventStyles[recordTypes.DrawFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Draw Frame"), categories["rendering"], true); 210 eventStyles[recordTypes.ScheduleStyleRecalculation] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Schedule Style Recalculation"), categories["rendering"], true); 211 eventStyles[recordTypes.RecalculateStyles] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Recalculate Style"), categories["rendering"]); 212 eventStyles[recordTypes.InvalidateLayout] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Invalidate Layout"), categories["rendering"], true); 213 eventStyles[recordTypes.Layout] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Layout"), categories["rendering"]); 214 eventStyles[recordTypes.PaintSetup] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint Setup"), categories["painting"]); 215 eventStyles[recordTypes.UpdateLayer] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Update Layer"), categories["painting"], true); 216 eventStyles[recordTypes.UpdateLayerTree] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Update Layer Tree"), categories["rendering"]); 217 eventStyles[recordTypes.Paint] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"), categories["painting"]); 218 eventStyles[recordTypes.RasterTask] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Paint"), categories["painting"]); 219 eventStyles[recordTypes.ScrollLayer] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Scroll"), categories["rendering"]); 220 eventStyles[recordTypes.CompositeLayers] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Composite Layers"), categories["painting"]); 221 eventStyles[recordTypes.ParseHTML] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Parse HTML"), categories["loading"]); 222 eventStyles[recordTypes.TimerInstall] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Install Timer"), categories["scripting"]); 223 eventStyles[recordTypes.TimerRemove] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Remove Timer"), categories["scripting"]); 224 eventStyles[recordTypes.TimerFire] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Timer Fired"), categories["scripting"]); 225 eventStyles[recordTypes.XHRReadyStateChange] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("XHR Ready State Change"), categories["scripting"]); 226 eventStyles[recordTypes.XHRLoad] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("XHR Load"), categories["scripting"]); 227 eventStyles[recordTypes.EvaluateScript] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Evaluate Script"), categories["scripting"]); 228 eventStyles[recordTypes.MarkLoad] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Load event"), categories["scripting"], true); 229 eventStyles[recordTypes.MarkDOMContent] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOMContentLoaded event"), categories["scripting"], true); 230 eventStyles[recordTypes.MarkFirstPaint] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("First paint"), categories["painting"], true); 231 eventStyles[recordTypes.TimeStamp] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Timestamp"), categories["scripting"]); 232 eventStyles[recordTypes.ConsoleTime] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Console Time"), categories["scripting"]); 233 eventStyles[recordTypes.ResourceSendRequest] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Send Request"), categories["loading"]); 234 eventStyles[recordTypes.ResourceReceiveResponse] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive Response"), categories["loading"]); 235 eventStyles[recordTypes.ResourceFinish] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Finish Loading"), categories["loading"]); 236 eventStyles[recordTypes.ResourceReceivedData] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive Data"), categories["loading"]); 237 eventStyles[recordTypes.FunctionCall] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Function Call"), categories["scripting"]); 238 eventStyles[recordTypes.GCEvent] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("GC Event"), categories["scripting"]); 239 eventStyles[recordTypes.JSFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("JS Frame"), categories["scripting"]); 240 eventStyles[recordTypes.RequestAnimationFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Request Animation Frame"), categories["scripting"]); 241 eventStyles[recordTypes.CancelAnimationFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Cancel Animation Frame"), categories["scripting"]); 242 eventStyles[recordTypes.FireAnimationFrame] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Animation Frame Fired"), categories["scripting"]); 243 eventStyles[recordTypes.WebSocketCreate] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Create WebSocket"), categories["scripting"]); 244 eventStyles[recordTypes.WebSocketSendHandshakeRequest] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Send WebSocket Handshake"), categories["scripting"]); 245 eventStyles[recordTypes.WebSocketReceiveHandshakeResponse] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Receive WebSocket Handshake"), categories["scripting"]); 246 eventStyles[recordTypes.WebSocketDestroy] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Destroy WebSocket"), categories["scripting"]); 247 eventStyles[recordTypes.EmbedderCallback] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Embedder Callback"), categories["scripting"]); 248 eventStyles[recordTypes.DecodeImage] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Image Decode"), categories["painting"]); 249 eventStyles[recordTypes.ResizeImage] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Image Resize"), categories["painting"]); 250 WebInspector.TracingTimelineUIUtils._eventStylesMap = eventStyles; 251 return eventStyles; 252 } 253 254 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes = {}; 255 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.Layout] = 1; 256 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.Paint] = 1; 257 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.RasterTask] = 1; 258 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.DecodeImage] = 1; 259 WebInspector.TracingTimelineUIUtils._coalescableRecordTypes[WebInspector.TracingTimelineModel.RecordType.ResizeImage] = 1; 260 261 /** 262 * @param {!WebInspector.TracingModel.Event} event 263 * @return {!{title: string, category: !WebInspector.TimelineCategory}} 264 */ 265 WebInspector.TracingTimelineUIUtils.eventStyle = function(event) 266 { 267 var eventStyles = WebInspector.TracingTimelineUIUtils._initEventStyles(); 268 if (event.category === WebInspector.TracingModel.ConsoleEventCategory) 269 return { title: event.name, category: WebInspector.TimelineUIUtils.categories()["scripting"] }; 270 271 var result = eventStyles[event.name]; 272 if (!result) { 273 result = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Unknown: %s", event.name), WebInspector.TimelineUIUtils.categories()["other"]); 274 eventStyles[event.name] = result; 275 } 276 return result; 277 } 278 279 /** 280 * @param {!WebInspector.TracingModel.Event} event 281 * @return {string} 282 */ 283 WebInspector.TracingTimelineUIUtils.markerEventColor = function(event) 284 { 285 var red = "rgb(255, 0, 0)"; 286 var blue = "rgb(0, 0, 255)"; 287 var orange = "rgb(255, 178, 23)"; 288 var green = "rgb(0, 130, 0)"; 289 290 if (event.category === WebInspector.TracingModel.ConsoleEventCategory) 291 return orange; 292 293 var recordTypes = WebInspector.TracingTimelineModel.RecordType; 294 var eventName = event.name; 295 switch (eventName) { 296 case recordTypes.MarkDOMContent: return blue; 297 case recordTypes.MarkLoad: return red; 298 case recordTypes.MarkFirstPaint: return green; 299 case recordTypes.TimeStamp: return orange; 300 } 301 return green; 302 } 303 304 /** 305 * @param {!WebInspector.TracingModel.Event} event 306 * @param {!WebInspector.TimelineModel} model 307 * @return {string} 308 */ 309 WebInspector.TracingTimelineUIUtils.eventTitle = function(event, model) 310 { 311 var title = WebInspector.TracingTimelineUIUtils.eventStyle(event).title; 312 if (event.category === WebInspector.TracingModel.ConsoleEventCategory) 313 return title; 314 if (event.name === WebInspector.TracingTimelineModel.RecordType.TimeStamp) 315 return WebInspector.UIString("%s: %s", title, event.args["data"]["message"]); 316 if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(event)) { 317 var startTime = Number.millisToString(event.startTime - model.minimumRecordTime()); 318 return WebInspector.UIString("%s at %s", title, startTime); 319 } 320 return title; 321 } 322 323 /** 324 * @param {!WebInspector.TracingModel.Event} event 325 * @return {boolean} 326 */ 327 WebInspector.TracingTimelineUIUtils.isMarkerEvent = function(event) 328 { 329 var recordTypes = WebInspector.TracingTimelineModel.RecordType; 330 switch (event.name) { 331 case recordTypes.TimeStamp: 332 case recordTypes.MarkFirstPaint: 333 return true; 334 case recordTypes.MarkDOMContent: 335 case recordTypes.MarkLoad: 336 return event.args["data"]["isMainFrame"]; 337 default: 338 return false; 339 } 340 } 341 342 /** 343 * @param {!WebInspector.TracingModel.Event} event 344 * @param {!WebInspector.Linkifier} linkifier 345 * @return {?Node} 346 */ 347 WebInspector.TracingTimelineUIUtils.buildDetailsNodeForTraceEvent = function(event, linkifier) 348 { 349 var recordType = WebInspector.TracingTimelineModel.RecordType; 350 var target = event.thread.target(); 351 var details; 352 var detailsText; 353 var eventData = event.args["data"]; 354 switch (event.name) { 355 case recordType.GCEvent: 356 var delta = event.args["usedHeapSizeBefore"] - event.args["usedHeapSizeAfter"]; 357 detailsText = WebInspector.UIString("%s collected", Number.bytesToString(delta)); 358 break; 359 case recordType.TimerFire: 360 detailsText = eventData["timerId"]; 361 break; 362 case recordType.FunctionCall: 363 details = linkifyLocation(eventData["scriptId"], eventData["scriptName"], eventData["scriptLine"], 0); 364 break; 365 case recordType.JSFrame: 366 details = linkifyLocation(eventData["scriptId"], eventData["url"], eventData["lineNumber"], eventData["columnNumber"]); 367 detailsText = WebInspector.CPUProfileDataModel.beautifyFunctionName(eventData["functionName"]); 368 break; 369 case recordType.FireAnimationFrame: 370 detailsText = eventData["id"]; 371 break; 372 case recordType.EventDispatch: 373 detailsText = eventData ? eventData["type"] : null; 374 break; 375 case recordType.Paint: 376 var width = WebInspector.TimelineUIUtils.quadWidth(eventData.clip); 377 var height = WebInspector.TimelineUIUtils.quadHeight(eventData.clip); 378 if (width && height) 379 detailsText = WebInspector.UIString("%d\u2009\u00d7\u2009%d", width, height); 380 break; 381 case recordType.TimerInstall: 382 case recordType.TimerRemove: 383 details = linkifyTopCallFrame(); 384 detailsText = eventData["timerId"]; 385 break; 386 case recordType.RequestAnimationFrame: 387 case recordType.CancelAnimationFrame: 388 details = linkifyTopCallFrame(); 389 detailsText = eventData["id"]; 390 break; 391 case recordType.ParseHTML: 392 case recordType.RecalculateStyles: 393 details = linkifyTopCallFrame(); 394 break; 395 case recordType.EvaluateScript: 396 var url = eventData["url"]; 397 if (url) 398 details = linkifyLocation("", url, eventData["lineNumber"], 0); 399 break; 400 case recordType.XHRReadyStateChange: 401 case recordType.XHRLoad: 402 case recordType.ResourceSendRequest: 403 var url = eventData["url"]; 404 if (url) 405 detailsText = WebInspector.displayNameForURL(url); 406 break; 407 case recordType.ResourceReceivedData: 408 case recordType.ResourceReceiveResponse: 409 case recordType.ResourceFinish: 410 var initiator = event.initiator; 411 if (initiator) { 412 var url = initiator.args["data"]["url"]; 413 if (url) 414 detailsText = WebInspector.displayNameForURL(url); 415 } 416 break; 417 case recordType.EmbedderCallback: 418 detailsText = eventData["callbackName"]; 419 break; 420 421 case recordType.PaintImage: 422 case recordType.DecodeImage: 423 case recordType.ResizeImage: 424 case recordType.DecodeLazyPixelRef: 425 var url = event.imageURL; 426 if (url) 427 detailsText = WebInspector.displayNameForURL(url); 428 break; 429 430 default: 431 if (event.category === WebInspector.TracingModel.ConsoleEventCategory) 432 detailsText = null; 433 else 434 details = linkifyTopCallFrame(); 435 break; 436 } 437 438 if (detailsText) { 439 if (details) 440 details.textContent = detailsText; 441 else 442 details = document.createTextNode(detailsText); 443 } 444 return details; 445 446 /** 447 * @param {string} scriptId 448 * @param {string} url 449 * @param {number} lineNumber 450 * @param {number=} columnNumber 451 */ 452 function linkifyLocation(scriptId, url, lineNumber, columnNumber) 453 { 454 if (!url) 455 return null; 456 457 // FIXME(62725): stack trace line/column numbers are one-based. 458 return linkifier.linkifyScriptLocation(target, scriptId, url, lineNumber - 1, (columnNumber ||1) - 1, "timeline-details"); 459 } 460 461 /** 462 * @return {?Element} 463 */ 464 function linkifyTopCallFrame() 465 { 466 var stackTrace = event.stackTrace; 467 if (!stackTrace) { 468 var initiator = event.initiator; 469 if (initiator) 470 stackTrace = initiator.stackTrace; 471 } 472 if (!stackTrace || !stackTrace.length) 473 return null; 474 return linkifier.linkifyConsoleCallFrame(target, stackTrace[0], "timeline-details"); 475 } 476 } 477 478 /** 479 * @param {!WebInspector.TracingModel.Event} event 480 * @param {!WebInspector.TracingTimelineModel} model 481 * @param {!WebInspector.Linkifier} linkifier 482 * @param {function(!DocumentFragment)} callback 483 */ 484 WebInspector.TracingTimelineUIUtils.buildTraceEventDetails = function(event, model, linkifier, callback) 485 { 486 var target = event.thread.target(); 487 var relatedNode = null; 488 var barrier = new CallbackBarrier(); 489 if (!event.previewElement && target) { 490 if (event.imageURL) 491 WebInspector.DOMPresentationUtils.buildImagePreviewContents(target, event.imageURL, false, barrier.createCallback(saveImage)); 492 else if (event.picture) 493 WebInspector.TracingTimelineUIUtils.buildPicturePreviewContent(event, barrier.createCallback(saveImage)); 494 } 495 if (event.backendNodeId && target) 496 target.domModel.pushNodesByBackendIdsToFrontend([event.backendNodeId], barrier.createCallback(setRelatedNode)); 497 barrier.callWhenDone(callbackWrapper); 498 499 /** 500 * @param {!Element=} element 501 */ 502 function saveImage(element) 503 { 504 event.previewElement = element || null; 505 } 506 507 /** 508 * @param {?Array.<!DOMAgent.NodeId>} nodeIds 509 */ 510 function setRelatedNode(nodeIds) 511 { 512 if (nodeIds) 513 relatedNode = target.domModel.nodeForId(nodeIds[0]); 514 } 515 516 function callbackWrapper() 517 { 518 callback(WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously(event, model, linkifier, relatedNode)); 519 } 520 } 521 522 /** 523 * @param {!WebInspector.TracingModel.Event} event 524 * @param {!WebInspector.TracingTimelineModel} model 525 * @param {!WebInspector.Linkifier} linkifier 526 * @param {?WebInspector.DOMNode} relatedNode 527 * @return {!DocumentFragment} 528 */ 529 WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = function(event, model, linkifier, relatedNode) 530 { 531 var fragment = document.createDocumentFragment(); 532 var stats = {}; 533 var hasChildren = WebInspector.TracingTimelineUIUtils._aggregatedStatsForTraceEvent(stats, model, event); 534 var pieChart = hasChildren ? 535 WebInspector.TimelineUIUtils.generatePieChart(stats, WebInspector.TracingTimelineUIUtils.eventStyle(event).category, event.selfTime) : 536 WebInspector.TimelineUIUtils.generatePieChart(stats); 537 fragment.appendChild(pieChart); 538 539 var recordTypes = WebInspector.TracingTimelineModel.RecordType; 540 541 // The messages may vary per event.name; 542 var callSiteStackTraceLabel; 543 var callStackLabel; 544 var relatedNodeLabel; 545 546 var contentHelper = new WebInspector.TimelineDetailsContentHelper(event.thread.target(), linkifier, true); 547 contentHelper.appendTextRow(WebInspector.UIString("Self Time"), Number.millisToString(event.selfTime, true)); 548 contentHelper.appendTextRow(WebInspector.UIString("Start Time"), Number.millisToString((event.startTime - model.minimumRecordTime()))); 549 var eventData = event.args["data"]; 550 var initiator = event.initiator; 551 552 switch (event.name) { 553 case recordTypes.GCEvent: 554 var delta = event.args["usedHeapSizeBefore"] - event.args["usedHeapSizeAfter"]; 555 contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(delta)); 556 break; 557 case recordTypes.TimerFire: 558 callSiteStackTraceLabel = WebInspector.UIString("Timer installed"); 559 // Fall-through intended. 560 561 case recordTypes.TimerInstall: 562 case recordTypes.TimerRemove: 563 contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), eventData["timerId"]); 564 if (event.name === recordTypes.TimerInstall) { 565 contentHelper.appendTextRow(WebInspector.UIString("Timeout"), Number.millisToString(eventData["timeout"])); 566 contentHelper.appendTextRow(WebInspector.UIString("Repeats"), !eventData["singleShot"]); 567 } 568 break; 569 case recordTypes.FireAnimationFrame: 570 callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested"); 571 contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), eventData["id"]); 572 break; 573 case recordTypes.FunctionCall: 574 if (eventData["scriptName"]) 575 contentHelper.appendLocationRow(WebInspector.UIString("Location"), eventData["scriptName"], eventData["scriptLine"]); 576 break; 577 case recordTypes.ResourceSendRequest: 578 case recordTypes.ResourceReceiveResponse: 579 case recordTypes.ResourceReceivedData: 580 case recordTypes.ResourceFinish: 581 var url = (event.name === recordTypes.ResourceSendRequest) ? eventData["url"] : initiator.args["data"]["url"]; 582 if (url) 583 contentHelper.appendElementRow(WebInspector.UIString("Resource"), WebInspector.linkifyResourceAsNode(url)); 584 if (eventData["requestMethod"]) 585 contentHelper.appendTextRow(WebInspector.UIString("Request Method"), eventData["requestMethod"]); 586 if (typeof eventData["statusCode"] === "number") 587 contentHelper.appendTextRow(WebInspector.UIString("Status Code"), eventData["statusCode"]); 588 if (eventData["mimeType"]) 589 contentHelper.appendTextRow(WebInspector.UIString("MIME Type"), eventData["mimeType"]); 590 if (eventData["encodedDataLength"]) 591 contentHelper.appendTextRow(WebInspector.UIString("Encoded Data Length"), WebInspector.UIString("%d Bytes", eventData["encodedDataLength"])); 592 break; 593 case recordTypes.EvaluateScript: 594 var url = eventData["url"]; 595 if (url) 596 contentHelper.appendLocationRow(WebInspector.UIString("Script"), url, eventData["lineNumber"]); 597 break; 598 case recordTypes.Paint: 599 var clip = eventData["clip"]; 600 contentHelper.appendTextRow(WebInspector.UIString("Location"), WebInspector.UIString("(%d, %d)", clip[0], clip[1])); 601 var clipWidth = WebInspector.TimelineUIUtils.quadWidth(clip); 602 var clipHeight = WebInspector.TimelineUIUtils.quadHeight(clip); 603 contentHelper.appendTextRow(WebInspector.UIString("Dimensions"), WebInspector.UIString("%d %d", clipWidth, clipHeight)); 604 // Fall-through intended. 605 606 case recordTypes.PaintSetup: 607 case recordTypes.Rasterize: 608 case recordTypes.ScrollLayer: 609 relatedNodeLabel = WebInspector.UIString("Layer root"); 610 break; 611 case recordTypes.PaintImage: 612 case recordTypes.DecodeLazyPixelRef: 613 case recordTypes.DecodeImage: 614 case recordTypes.ResizeImage: 615 case recordTypes.DrawLazyPixelRef: 616 relatedNodeLabel = WebInspector.UIString("Image element"); 617 if (event.imageURL) 618 contentHelper.appendElementRow(WebInspector.UIString("Image URL"), WebInspector.linkifyResourceAsNode(event.imageURL)); 619 break; 620 case recordTypes.RecalculateStyles: // We don't want to see default details. 621 contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), event.args["elementCount"]); 622 callStackLabel = WebInspector.UIString("Styles recalculation forced"); 623 break; 624 case recordTypes.Layout: 625 var beginData = event.args["beginData"]; 626 contentHelper.appendTextRow(WebInspector.UIString("Nodes that need layout"), beginData["dirtyObjects"]); 627 contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), beginData["totalObjects"]); 628 contentHelper.appendTextRow(WebInspector.UIString("Layout scope"), 629 beginData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document")); 630 callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated"); 631 callStackLabel = WebInspector.UIString("Layout forced"); 632 relatedNodeLabel = WebInspector.UIString("Layout root"); 633 break; 634 case recordTypes.ConsoleTime: 635 contentHelper.appendTextRow(WebInspector.UIString("Message"), event.name); 636 break; 637 case recordTypes.WebSocketCreate: 638 case recordTypes.WebSocketSendHandshakeRequest: 639 case recordTypes.WebSocketReceiveHandshakeResponse: 640 case recordTypes.WebSocketDestroy: 641 var initiatorData = initiator ? initiator.args["data"] : eventData; 642 if (typeof initiatorData["webSocketURL"] !== "undefined") 643 contentHelper.appendTextRow(WebInspector.UIString("URL"), initiatorData["webSocketURL"]); 644 if (typeof initiatorData["webSocketProtocol"] !== "undefined") 645 contentHelper.appendTextRow(WebInspector.UIString("WebSocket Protocol"), initiatorData["webSocketProtocol"]); 646 if (typeof eventData["message"] !== "undefined") 647 contentHelper.appendTextRow(WebInspector.UIString("Message"), eventData["message"]); 648 break; 649 case recordTypes.EmbedderCallback: 650 contentHelper.appendTextRow(WebInspector.UIString("Callback Function"), eventData["callbackName"]); 651 break; 652 default: 653 var detailsNode = WebInspector.TracingTimelineUIUtils.buildDetailsNodeForTraceEvent(event, linkifier); 654 if (detailsNode) 655 contentHelper.appendElementRow(WebInspector.UIString("Details"), detailsNode); 656 break; 657 } 658 659 if (relatedNode) 660 contentHelper.appendElementRow(relatedNodeLabel || WebInspector.UIString("Related node"), WebInspector.DOMPresentationUtils.linkifyNodeReference(relatedNode)); 661 662 if (eventData && eventData["scriptName"] && event.name !== recordTypes.FunctionCall) 663 contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), eventData["scriptName"], eventData["scriptLine"]); 664 665 if (initiator) { 666 var callSiteStackTrace = initiator.stackTrace; 667 if (callSiteStackTrace) 668 contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), callSiteStackTrace); 669 } 670 var eventStackTrace = event.stackTrace; 671 if (eventStackTrace) 672 contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), eventStackTrace); 673 674 var warning = event.warning; 675 if (warning) { 676 var div = document.createElement("div"); 677 div.textContent = warning; 678 contentHelper.appendElementRow(WebInspector.UIString("Warning"), div); 679 } 680 if (event.previewElement) 681 contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement); 682 fragment.appendChild(contentHelper.element); 683 return fragment; 684 } 685 686 /** 687 * @param {!Object} total 688 * @param {!WebInspector.TracingTimelineModel} model 689 * @param {!WebInspector.TracingModel.Event} event 690 * @return {boolean} 691 */ 692 WebInspector.TracingTimelineUIUtils._aggregatedStatsForTraceEvent = function(total, model, event) 693 { 694 var events = model.inspectedTargetEvents(); 695 /** 696 * @param {number} startTime 697 * @param {!WebInspector.TracingModel.Event} e 698 * @return {number} 699 */ 700 function eventComparator(startTime, e) 701 { 702 return startTime - e.startTime; 703 } 704 var index = events.binaryIndexOf(event.startTime, eventComparator); 705 if (index < 0) 706 return false; 707 var hasChildren = false; 708 var endTime = event.endTime; 709 if (endTime) { 710 for (var i = index; i < events.length; i++) { 711 var nextEvent = events[i]; 712 if (nextEvent.startTime >= endTime) 713 break; 714 if (!nextEvent.selfTime) 715 continue; 716 if (nextEvent.thread !== event.thread) 717 continue; 718 if (i > index) 719 hasChildren = true; 720 var categoryName = WebInspector.TracingTimelineUIUtils.eventStyle(nextEvent).category.name; 721 total[categoryName] = (total[categoryName] || 0) + nextEvent.selfTime; 722 } 723 } 724 return hasChildren; 725 } 726 727 /** 728 * @param {!WebInspector.TracingModel.Event} event 729 * @param {function(!Element=)} callback 730 */ 731 WebInspector.TracingTimelineUIUtils.buildPicturePreviewContent = function(event, callback) 732 { 733 734 new WebInspector.LayerPaintEvent(event).loadPicture(onSnapshotLoaded); 735 /** 736 * @param {?Array.<number>} rect 737 * @param {?WebInspector.PaintProfilerSnapshot} snapshot 738 */ 739 function onSnapshotLoaded(rect, snapshot) 740 { 741 if (!snapshot) { 742 callback(); 743 return; 744 } 745 snapshot.requestImage(null, null, 1, onGotImage); 746 snapshot.dispose(); 747 } 748 749 /** 750 * @param {string=} imageURL 751 */ 752 function onGotImage(imageURL) 753 { 754 if (!imageURL) { 755 callback(); 756 return; 757 } 758 var container = document.createElement("div"); 759 container.className = "image-preview-container"; 760 var img = container.createChild("img"); 761 img.src = imageURL; 762 callback(container); 763 } 764 } 765 766 /** 767 * @param {string} recordType 768 * @param {string=} title 769 * @return {!Element} 770 */ 771 WebInspector.TracingTimelineUIUtils._createEventDivider = function(recordType, title) 772 { 773 var eventDivider = document.createElement("div"); 774 eventDivider.className = "resources-event-divider"; 775 var recordTypes = WebInspector.TracingTimelineModel.RecordType; 776 777 if (recordType === recordTypes.MarkDOMContent) 778 eventDivider.className += " resources-blue-divider"; 779 else if (recordType === recordTypes.MarkLoad) 780 eventDivider.className += " resources-red-divider"; 781 else if (recordType === recordTypes.MarkFirstPaint) 782 eventDivider.className += " resources-green-divider"; 783 else if (recordType === recordTypes.TimeStamp || recordType === recordTypes.ConsoleTime) 784 eventDivider.className += " resources-orange-divider"; 785 else if (recordType === recordTypes.BeginFrame) 786 eventDivider.className += " timeline-frame-divider"; 787 788 if (title) 789 eventDivider.title = title; 790 791 return eventDivider; 792 } 793 794 /** 795 * @return {!Array.<string>} 796 */ 797 WebInspector.TracingTimelineUIUtils._visibleTypes = function() 798 { 799 var eventStyles = WebInspector.TracingTimelineUIUtils._initEventStyles(); 800 var result = []; 801 for (var name in eventStyles) { 802 if (!eventStyles[name].hidden) 803 result.push(name); 804 } 805 return result; 806 } 807 808 /** 809 * @return {!WebInspector.TracingTimelineModel.Filter} 810 */ 811 WebInspector.TracingTimelineUIUtils.hiddenEventsFilter = function() 812 { 813 return new WebInspector.TracingTimelineModel.InclusiveEventNameFilter(WebInspector.TracingTimelineUIUtils._visibleTypes()); 814 } 815