| 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 2cd5efd8681d9011c0faab9ec83ef93ac7d996b8..fa2894db584fef2d2fc56e0792f892f09b78a228 100644
|
| --- a/Source/devtools/front_end/timeline/TracingTimelineModel.js
|
| +++ b/Source/devtools/front_end/timeline/TracingTimelineModel.js
|
| @@ -47,6 +47,11 @@ WebInspector.TracingTimelineModel.RecordType = {
|
| ScrollLayer: "ScrollLayer",
|
| CompositeLayers: "CompositeLayers",
|
|
|
| + StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking",
|
| + LayoutInvalidationTracking: "LayoutInvalidationTracking",
|
| + LayerInvalidationTracking: "LayerInvalidationTracking",
|
| + PaintInvalidationTracking: "PaintInvalidationTracking",
|
| +
|
| ParseHTML: "ParseHTML",
|
|
|
| TimerInstall: "TimerInstall",
|
| @@ -138,7 +143,9 @@ WebInspector.TracingTimelineModel.prototype = {
|
| WebInspector.TracingModel.ConsoleEventCategory
|
| ];
|
| if (captureCauses) {
|
| - categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
|
| + categoriesArray = categoriesArray.concat([
|
| + disabledByDefault("devtools.timeline.stack"),
|
| + disabledByDefault("devtools.timeline.invalidationTracking")]);
|
| if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) {
|
| this._jsProfilerStarted = true;
|
| this._currentTarget = WebInspector.context.flavor(WebInspector.Target);
|
| @@ -475,6 +482,7 @@ WebInspector.TracingTimelineModel.prototype = {
|
| this._sendRequestEvents = {};
|
| this._timerEvents = {};
|
| this._requestAnimationFrameEvents = {};
|
| + this._invalidationTracker = new WebInspector.InvalidationTracker();
|
| this._layoutInvalidate = {};
|
| this._lastScheduleStyleRecalculation = {};
|
| this._webSocketCreateEvents = {};
|
| @@ -591,6 +599,46 @@ WebInspector.TracingTimelineModel.prototype = {
|
| this._lastRecalculateStylesEvent = event;
|
| break;
|
|
|
| + case recordTypes.StyleRecalcInvalidationTracking:
|
| + this._invalidationTracker.addInvalidation({
|
| + type: recordTypes.StyleRecalcInvalidationTracking,
|
| + frameId: event.args.data.frame,
|
| + nodeId: event.args.data.nodeId,
|
| + nodeName: event.args.data.nodeName,
|
| + reason: event.args.data.reason,
|
| + callstack: event.args.data.callstack
|
| + });
|
| + break;
|
| +
|
| + case recordTypes.LayoutInvalidationTracking:
|
| + this._invalidationTracker.addInvalidation({
|
| + type: recordTypes.LayoutInvalidationTracking,
|
| + frameId: event.args.data.frame,
|
| + nodeId: event.args.data.nodeId,
|
| + nodeName: event.args.data.nodeName,
|
| + callstack: event.args.data.callstack
|
| + });
|
| + break;
|
| +
|
| + case recordTypes.LayerInvalidationTracking:
|
| + this._invalidationTracker.addInvalidation({
|
| + type: recordTypes.LayerInvalidationTracking,
|
| + frameId: event.args.data.frame,
|
| + paintId: event.args.data.paintId,
|
| + reason: event.args.data.reason
|
| + });
|
| + break;
|
| +
|
| + case recordTypes.PaintInvalidationTracking:
|
| + this._invalidationTracker.addInvalidation({
|
| + type: recordTypes.PaintInvalidationTracking,
|
| + frameId: event.args.data.frame,
|
| + 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.
|
| @@ -635,6 +683,7 @@ WebInspector.TracingTimelineModel.prototype = {
|
| break;
|
|
|
| case recordTypes.Paint:
|
| + this._invalidationTracker.didPaint(event);
|
| event.highlightQuad = event.args["data"]["clip"];
|
| event.backendNodeId = event.args["data"]["nodeId"];
|
| var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
|
| @@ -1103,3 +1152,88 @@ WebInspector.TracingTimelineSaver.prototype = {
|
| */
|
| onError: function(reader, event) { },
|
| }
|
| +
|
| +/**
|
| + * Track invalidation events across frames.
|
| + */
|
| +WebInspector.InvalidationTracker = function()
|
| +{
|
| + this._invalidationEvents = [];
|
| + this._lastPaintWithLayer = undefined;
|
| + this._didPaint = false;
|
| +}
|
| +
|
| +WebInspector.InvalidationTracker.prototype = {
|
| +
|
| + addInvalidation: function(invalidation)
|
| + {
|
| + this._startNewFrameIfNeeded();
|
| + if (!invalidation.nodeId && !invalidation.paintId) {
|
| + console.error('Invalidation lacks node information.');
|
| + console.error(invalidation);
|
| + }
|
| +
|
| + // Record the paintIds for style recalc or layout invalidations.
|
| + // FIXME: This O(n^2) loop could be optimized with a map.
|
| + if (invalidation.type == WebInspector.TracingTimelineModel.RecordType.PaintInvalidationTracking) {
|
| + this._invalidationEvents.forEach(function(invalidationToUpdate) {
|
| + if (invalidationToUpdate.nodeId != invalidation.nodeId)
|
| + return;
|
| + switch (invalidationToUpdate.type) {
|
| + case WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking:
|
| + invalidationToUpdate.paintId = invalidation.paintId;
|
| + break;
|
| + case WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking:
|
| + invalidationToUpdate.paintId = invalidation.paintId;
|
| + break;
|
| + }
|
| + });
|
| + } else {
|
| + this._invalidationEvents.push(invalidation);
|
| + }
|
| + },
|
| +
|
| + didPaint: function(paintEvent)
|
| + {
|
| + this._didPaint = true;
|
| +
|
| + // If a paint doesn't have a corresponding graphics layer id, it paints into it's parent so
|
| + // add an effectivePaintId to these events.
|
| + // FIXME: The parent layer is assumed to be the last paint event that had a layerId set. Is that right?
|
| + var layerId = paintEvent.args.data.layerId;
|
| + if (layerId)
|
| + this._lastPaintWithLayer = paintEvent;
|
| +
|
| + if (!this._lastPaintWithLayer) {
|
| + console.error("Failed to find the paint container for a paint event.");
|
| + return;
|
| + }
|
| +
|
| + var effectivePaintId = this._lastPaintWithLayer.args.data.nodeId;
|
| + this._processPaint(paintEvent, effectivePaintId);
|
| + },
|
| +
|
| + _processPaint: function(paintEvent, effectivePaintId)
|
| + {
|
| + var frameId = paintEvent.args.data.frame;
|
| +
|
| + this._invalidationEvents.forEach(function(invalidation) {
|
| + if (invalidation.paintId == effectivePaintId && invalidation.frameId == frameId) {
|
| + if (!paintEvent.invalidationTrackingEvents)
|
| + paintEvent.invalidationTrackingEvents = [];
|
| + paintEvent.invalidationTrackingEvents.push(invalidation);
|
| + }
|
| + });
|
| + },
|
| +
|
| + _startNewFrameIfNeeded: function()
|
| + {
|
| + if (!this._didPaint)
|
| + return;
|
| +
|
| + // Prepare for the next frame.
|
| + this._invalidationEvents = [];
|
| + this._lastPaintWithLayer = undefined;
|
| + this._didPaint = false;
|
| + }
|
| +}
|
|
|