Index: Source/devtools/front_end/timeline/TracingTimelineModel.js |
diff --git a/Source/devtools/front_end/timeline/TracingTimelineModel.js b/Source/devtools/front_end/timeline/TracingTimelineModel.js |
index 7284b459a7adaebb9cbfb8240d6949d8ea35a66b..8386730c6e755b2cdf6312e409833534c9f465f6 100644 |
--- a/Source/devtools/front_end/timeline/TracingTimelineModel.js |
+++ b/Source/devtools/front_end/timeline/TracingTimelineModel.js |
@@ -31,6 +31,9 @@ WebInspector.TracingTimelineModel.RecordType = { |
DrawFrame: "DrawFrame", |
ScheduleStyleRecalculation: "ScheduleStyleRecalculation", |
RecalculateStyles: "RecalculateStyles", |
+ StyleInvalidationTracking: "StyleInvalidationTracking", |
+ LayoutInvalidationTracking: "LayoutInvalidationTracking", |
+ PaintInvalidationTracking: "PaintInvalidationTracking", |
InvalidateLayout: "InvalidateLayout", |
Layout: "Layout", |
UpdateLayer: "UpdateLayer", |
@@ -116,8 +119,9 @@ WebInspector.TracingTimelineModel.prototype = { |
* @param {boolean} captureStacks |
* @param {boolean} captureMemory |
* @param {boolean} capturePictures |
+ * @param {boolean} captureInvalidationTracking |
*/ |
- startRecording: function(captureStacks, captureMemory, capturePictures) |
+ startRecording: function(captureStacks, captureMemory, capturePictures, captureInvalidationTracking) |
{ |
function disabledByDefault(category) |
{ |
@@ -144,6 +148,10 @@ WebInspector.TracingTimelineModel.prototype = { |
disabledByDefault("devtools.timeline.picture"), |
disabledByDefault("blink.graphics_context_annotations")]); |
} |
+ if (captureInvalidationTracking) { |
+ categoriesArray = categoriesArray.concat([ |
+ disabledByDefault("devtools.timeline.invalidationTracking")]); |
+ } |
var categories = categoriesArray.join(","); |
this._startRecordingWithCategories(categories); |
}, |
@@ -410,6 +418,7 @@ WebInspector.TracingTimelineModel.prototype = { |
this._sendRequestEvents = {}; |
this._timerEvents = {}; |
this._requestAnimationFrameEvents = {}; |
+ this._invalidationTracker = new WebInspector.InvalidationTracker(); |
this._layoutInvalidate = {}; |
this._lastScheduleStyleRecalculation = {}; |
this._webSocketCreateEvents = {}; |
@@ -522,6 +531,35 @@ WebInspector.TracingTimelineModel.prototype = { |
this._lastRecalculateStylesEvent = event; |
break; |
+ case recordTypes.StyleInvalidationTracking: |
+ this._invalidationTracker.addStyleInvalidation({ |
+ frameId: event.args.data.frame, |
+ nodeId: event.args.data.nodeId, |
+ nodeName: event.args.data.nodeName, |
+ callstack: event.args.data.callstack |
+ }); |
+ break; |
+ |
+ case recordTypes.LayoutInvalidationTracking: |
+ this._invalidationTracker.addLayoutInvalidation({ |
+ frameId: event.args.data.frame, |
+ rendererId: event.args.data.rendererId, |
+ nodeId: event.args.data.nodeId, |
+ nodeName: event.args.data.nodeName, |
+ callstack: event.args.data.callstack |
+ }); |
+ break; |
+ |
+ case recordTypes.PaintInvalidationTracking: |
+ this._invalidationTracker.addPaintInvalidation({ |
+ frameId: event.args.data.frame, |
+ rendererId: event.args.data.rendererId, |
+ nodeId: event.args.data.nodeId, |
+ nodeName: event.args.data.nodeName, |
+ paintId: event.args.data.paintId |
+ }); |
+ break; |
+ |
case recordTypes.InvalidateLayout: |
// Consider style recalculation as a reason for layout invalidation, |
// but only if we had no earlier layout invalidation records. |
@@ -563,6 +601,7 @@ WebInspector.TracingTimelineModel.prototype = { |
break; |
case recordTypes.Paint: |
+ this._invalidationTracker.updatePaint(event); |
event.highlightQuad = event.args["data"]["clip"]; |
event.backendNodeId = event.args["data"]["nodeId"]; |
var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer); |
@@ -1050,3 +1089,122 @@ WebInspector.TracingTimelineSaver.prototype = { |
this._writeNextChunk(stream); |
} |
} |
+ |
+/** |
+ * TODO: Properly doc this class. |
+ * |
+ * Track style, layout, and paint invalidation events. |
+ */ |
+WebInspector.InvalidationTracker = function() |
+{ |
+ // Invalidation events for the current frame. |
+ this._styleTrackingEvents = {}; |
+ this._layoutTrackingEvents = {}; |
+ this._paintTrackingEvents = {}; |
+ |
+ // FIXME: This is used to clear out the tracking event maps but it may |
+ // not be sufficient if there are multiple frame ids. |
+ this._lastFrameId = -1; |
+ |
+ this._didPaint = false; |
+} |
+ |
+WebInspector.InvalidationTracker.prototype = { |
+ |
+ addStyleInvalidation: function(invalidation) |
+ { |
+ this._startNewFrameIfNeeded(); |
+ |
+ var frameId = invalidation.frameId; |
+ this._lastFrameId = frameId; |
+ |
+ if (!invalidation.nodeId) { |
+ console.error('Style invalidation lacks node information.'); |
+ console.error(invalidation); |
+ } |
+ |
+ if (this._styleTrackingEvents[frameId] === undefined) |
+ this._styleTrackingEvents[frameId] = []; |
+ this._styleTrackingEvents[frameId].push(invalidation); |
+ }, |
+ |
+ addLayoutInvalidation: function(invalidation) |
+ { |
+ this._startNewFrameIfNeeded(); |
+ |
+ var frameId = invalidation.frameId; |
+ this._lastFrameId = frameId; |
+ |
+ if (!invalidation.nodeId && !invalidation.rendererId) { |
+ console.error('Layout invalidation lacks node or renderer information.'); |
+ console.error(invalidation); |
+ } |
+ |
+ if (this._layoutTrackingEvents[frameId] === undefined) |
+ this._layoutTrackingEvents[frameId] = []; |
+ this._layoutTrackingEvents[frameId].push(invalidation); |
+ }, |
+ |
+ addPaintInvalidation: function(invalidation) |
+ { |
+ this._startNewFrameIfNeeded(); |
+ |
+ var frameId = invalidation.frameId; |
+ this._lastFrameId = frameId; |
+ |
+ if (!invalidation.nodeId && !invalidation.rendererId) { |
+ console.error('Paint invalidation lacks node or renderer information.'); |
+ console.error(invalidation); |
+ } |
+ |
+ if (this._paintTrackingEvents[frameId] === undefined) |
+ this._paintTrackingEvents[frameId] = []; |
+ this._paintTrackingEvents[frameId].push(invalidation); |
+ }, |
+ |
+ updatePaint: function(paintEvent) |
+ { |
+ this._didPaint = true; |
+ |
+ var frameId = paintEvent.args.data.frame; |
+ var paintId = paintEvent.args.data.paintId; |
+ |
+ paintEvent.styleInvalidationTrackingEvents = []; |
+ paintEvent.layoutInvalidationTrackingEvents = []; |
+ |
+ var styleTrackingEvents = this._styleTrackingEvents[frameId] || []; |
+ var layoutTrackingEvents = this._layoutTrackingEvents[frameId] || []; |
+ var paintTrackingEvents = this._paintTrackingEvents[frameId] || []; |
+ |
+ paintTrackingEvents.forEach(function(paintInvalidation) { |
+ if (paintInvalidation.paintId != paintId) |
+ return; |
+ |
+ // Collect style and layout invalidations that caused this paint. |
+ styleTrackingEvents.forEach(function(invalidation) { |
+ if (invalidation.nodeId == paintInvalidation.nodeId) { |
+ if (paintEvent.styleInvalidationTrackingEvents.indexOf(invalidation) == -1) |
+ paintEvent.styleInvalidationTrackingEvents.push(invalidation); |
+ } |
+ }); |
+ layoutTrackingEvents.forEach(function(invalidation) { |
+ if (invalidation.nodeId == paintInvalidation.nodeId |
+ || invalidation.rendererId == paintInvalidation.rendererId) { |
+ if (paintEvent.layoutInvalidationTrackingEvents.indexOf(invalidation) == -1) |
+ paintEvent.layoutInvalidationTrackingEvents.push(invalidation); |
+ } |
+ }); |
+ }); |
+ }, |
+ |
+ // If there's a new frame, save off all current invalidations. |
+ _startNewFrameIfNeeded: function() |
+ { |
+ if (!this._didPaint) |
+ return; |
+ this._styleTrackingEvents[this._lastFrameId] = undefined; |
+ this._layoutTrackingEvents[this._lastFrameId] = undefined; |
+ this._paintTrackingEvents[this._lastFrameId] = undefined; |
+ this._didPaint = false; |
+ } |
+} |