Commit 06a925de authored by pdr@chromium.org's avatar pdr@chromium.org

Implement layout invalidation tracking in devtools

This patch adds layout invalidation tracking to the invalidation
tracking experiment. Our full design doc is at:
https://docs.google.com/document/d/1BXT3_gD--YdbIYMlTzNGsyUUMBCZJ7V5qGJhMA-pGrs/edit

This patch implements the invalidation tracking logic for layout
events. All layout invalidations are recorded and when a layout
occurs they are collected and displayed to the user. Style invalidations
occurring at the same time are not shown.

This patch is a fairly straightforward extension of [1] but for
layout events. The InspectorTest.findTimelineRecord function has been
added for capturing the N-th event of a specific type and all other
inspector tests updated to use it.

[1] https://src.chromium.org/viewvc/blink?view=rev&revision=184212

BUG=410701

Review URL: https://codereview.chromium.org/671913005

git-svn-id: svn://svn.chromium.org/blink/trunk@184269 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent e620af01
......@@ -279,14 +279,24 @@ InspectorTest.printTimelineRecordProperties = function(record)
};
InspectorTest.findFirstTimelineRecord = function(type)
{
return InspectorTest.findTimelineRecord(type, 0);
}
// Find the (n+1)th timeline record of a specific type.
InspectorTest.findTimelineRecord = function(type, n)
{
var result;
function findByType(record)
{
if (record.type() !== type)
return false;
result = record;
return true;
if (n === 0) {
result = record;
return true;
}
n--;
return false;
}
InspectorTest.timelineModel().forAllRecords(findByType);
return result;
......
Tests the Timeline API instrumentation of layout events with invalidations.
PASS
Running: testLocalFrame
Running: testSubframe
<!DOCTYPE HTML>
<html>
<head>
<script src="../../http/tests/inspector/inspector-test.js"></script>
<script src="../../http/tests/inspector/timeline-test.js"></script>
<script>
function display(callback)
{
requestAnimationFrame(function() {
document.getElementById("testElement").style.width = "100px";
var forceLayout1 = document.body.offsetTop;
document.getElementById("testElement").style.width = "110px";
var forceLayout2 = document.body.offsetTop;
if (window.testRunner)
testRunner.displayAsyncThen(callback);
});
}
function updateSubframeAndDisplay(callback)
{
requestAnimationFrame(function() {
frames[0].document.body.children[0].style.width = "10px";
var forceLayout1 = frames[0].document.body.offsetTop;
frames[0].document.body.children[0].style.width = "20px";
var forceLayout2 = frames[0].document.body.offsetTop;
if (window.testRunner)
testRunner.displayAsyncThen(callback);
});
}
function test()
{
var currentPanel = WebInspector.inspectorView.currentPanel();
InspectorTest.assertEquals(currentPanel._panelName, "timeline", "Current panel should be the timeline.");
Runtime.experiments.enableForTest("timelineInvalidationTracking");
InspectorTest.runTestSuite([
function testLocalFrame(next)
{
InspectorTest.invokeAsyncWithTimeline("display", function() {
var firstLayoutRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Layout);
var firstInvalidations = firstLayoutRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(firstInvalidations.length, 1);
InspectorTest.assertEquals(firstInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking);
InspectorTest.assertEquals(firstInvalidations[0].nodeName, "DIV id='testElement'");
InspectorTest.assertGreaterOrEqual(firstInvalidations[0].stackTrace.length, 1);
var secondLayoutRecord = InspectorTest.findTimelineRecord(WebInspector.TimelineModel.RecordType.Layout, 1);
var secondInvalidations = secondLayoutRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(secondInvalidations.length, 1);
InspectorTest.assertEquals(secondInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking);
InspectorTest.assertEquals(secondInvalidations[0].nodeName, "DIV id='testElement'");
InspectorTest.assertGreaterOrEqual(secondInvalidations[0].stackTrace.length, 1);
next();
});
},
function testSubframe(next)
{
InspectorTest.invokeAsyncWithTimeline("updateSubframeAndDisplay", function() {
var firstLayoutRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Layout);
var firstInvalidations = firstLayoutRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(firstInvalidations.length, 1);
InspectorTest.assertEquals(firstInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking);
InspectorTest.assertEquals(firstInvalidations[0].nodeName, "DIV");
InspectorTest.assertGreaterOrEqual(firstInvalidations[0].stackTrace.length, 1);
var secondLayoutRecord = InspectorTest.findTimelineRecord(WebInspector.TimelineModel.RecordType.Layout, 1);
var secondInvalidations = secondLayoutRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(secondInvalidations.length, 1);
InspectorTest.assertEquals(secondInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking);
InspectorTest.assertEquals(secondInvalidations[0].nodeName, "DIV");
InspectorTest.assertGreaterOrEqual(secondInvalidations[0].stackTrace.length, 1);
next();
});
}
]);
}
</script>
</head>
<body onload="runTest()">
<p>Tests the Timeline API instrumentation of layout events with invalidations.</p>
<div id="outerTestElement" style="display: inline-block;"><div id="testElement">PASS</div></div>
<iframe src="resources/timeline-iframe-paint.html" style="position: absolute; left: 40px; top: 40px; width: 100px; height: 100px; border: none"></iframe>
</body>
</html>
\ No newline at end of file
......@@ -58,25 +58,13 @@ function test()
function testSubframe(next)
{
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var secondPaintRecord = undefined;
function findSecondPaint(record)
{
if (record.type() !== WebInspector.TimelineModel.RecordType.Paint)
return false;
if (record === firstPaintRecord)
return false;
secondPaintRecord = record;
return true;
}
InspectorTest.timelineModel().forAllRecords(findSecondPaint);
// The first paint corresponds to the local frame and should have no invalidations.
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var firstInvalidations = firstPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(firstInvalidations, undefined);
// The second paint corresponds to the subframe and should have our layout/style invalidations.
var secondPaintRecord = InspectorTest.findTimelineRecord(WebInspector.TimelineModel.RecordType.Paint, 1);
var secondInvalidations = secondPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(secondInvalidations.length, 3);
InspectorTest.assertEquals(secondInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking);
......
......@@ -51,25 +51,13 @@ function test()
function testSubframe(next)
{
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var secondPaintRecord = undefined;
function findSecondPaint(record)
{
if (record.type() !== WebInspector.TimelineModel.RecordType.Paint)
return false;
if (record === firstPaintRecord)
return false;
secondPaintRecord = record;
return true;
}
InspectorTest.timelineModel().forAllRecords(findSecondPaint);
// The first paint corresponds to the local frame and should have no invalidations.
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var firstInvalidations = firstPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(firstInvalidations, undefined);
// The second paint corresponds to the subframe and should have our layout/style invalidations.
var secondPaintRecord = InspectorTest.findTimelineRecord(WebInspector.TimelineModel.RecordType.Paint, 1);
var secondInvalidations = secondPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(secondInvalidations.length, 3);
InspectorTest.assertEquals(secondInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking);
......
......@@ -48,26 +48,13 @@ function test()
function testSubframe(next)
{
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var secondPaintRecord = undefined;
function findSecondPaint(record)
{
if (record.type() !== WebInspector.TimelineModel.RecordType.Paint)
return false;
if (record === firstPaintRecord)
return false;
secondPaintRecord = record;
return true;
}
InspectorTest.timelineModel().forAllRecords(findSecondPaint);
// The first paint corresponds to the local frame and should have no invalidations.
var firstPaintRecord = InspectorTest.findFirstTimelineRecord(WebInspector.TimelineModel.RecordType.Paint);
var firstInvalidations = firstPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(firstInvalidations, undefined);
// The second paint corresponds to the subframe and should have our style invalidations.
var secondPaintRecord = InspectorTest.findTimelineRecord(WebInspector.TimelineModel.RecordType.Paint, 1);
var secondInvalidations = secondPaintRecord._event.invalidationTrackingEvents;
InspectorTest.assertEquals(secondInvalidations.length, 1);
InspectorTest.assertEquals(secondInvalidations[0].type, WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking);
......
......@@ -618,6 +618,7 @@ WebInspector.TracingTimelineModel.prototype = {
break;
case recordTypes.Layout:
this._invalidationTracker.didLayout(event);
var frameId = event.args["beginData"]["frame"];
event.initiator = this._layoutInvalidate[frameId];
// In case we have no closing Layout event, endData is not available.
......@@ -1196,6 +1197,25 @@ WebInspector.InvalidationTracker.prototype = {
this._lastStyleRecalcEventIndex = invalidationCount;
},
/**
* @param {!WebInspector.TracingModel.Event} layoutEvent
*/
didLayout: function(layoutEvent)
{
var layoutFrameId = layoutEvent.args["beginData"]["frame"];
var index = this._lastLayoutEventIndex;
var invalidationCount = this._invalidationEvents.length;
for (; index < invalidationCount; index++) {
var invalidation = this._invalidationEvents[index];
if (invalidation.type !== WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking)
continue;
if (invalidation.frameId === layoutFrameId)
this._addInvalidationTrackingEvent(layoutEvent, invalidation);
}
this._lastLayoutEventIndex = invalidationCount;
},
/**
* @param {!WebInspector.TracingModel.Event} paintEvent
*/
......@@ -1253,6 +1273,7 @@ WebInspector.InvalidationTracker.prototype = {
/** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */
this._invalidationEvents = [];
this._lastStyleRecalcEventIndex = 0;
this._lastLayoutEventIndex = 0;
this._lastPaintWithLayer = undefined;
this._didPaint = false;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment