Commit 46f4fe3f authored by apavlov@chromium.org's avatar apavlov@chromium.org

DevTools: [Elements] Highlight DOM updates

R=vsevik
BUG=424085

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

git-svn-id: svn://svn.chromium.org/blink/trunk@185115 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 690263ec
...@@ -507,6 +507,45 @@ InspectorTest.dumpElementsTree = function(rootNode, depth, resultsArray) ...@@ -507,6 +507,45 @@ InspectorTest.dumpElementsTree = function(rootNode, depth, resultsArray)
print(rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline, "", depth || 10000); print(rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline, "", depth || 10000);
}; };
InspectorTest.dumpDOMUpdateHighlights = function(rootNode, depth)
{
var treeOutline = InspectorTest.firstElementsTreeOutline();
treeOutline._updateModifiedNodes();
print(rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline, "", depth || 10000);
function print(treeItem, prefix, depth)
{
if (treeItem.listItemElement) {
var elementXPath = WebInspector.DOMPresentationUtils.xPath(treeItem._node, true);
var highlightedElements = treeItem.listItemElement.querySelectorAll(".dom-update-highlight");
for (var i = 0; i < highlightedElements.length; ++i) {
var element = highlightedElements[i];
var classList = element.classList;
var xpath = elementXPath;
if (classList.contains("webkit-html-attribute-name")) {
xpath += "/@" + element.textContent + " (empty)";
} else if (classList.contains("webkit-html-attribute-value")) {
name = element.parentElement.querySelector(".webkit-html-attribute-name").textContent;
xpath += "/@" + name + " " + element.textContent;
} else if (classList.contains("webkit-html-text-node")) {
xpath += "/text() \"" + element.textContent + "\"";
}
InspectorTest.addResult(prefix + xpath);
}
}
if (!treeItem.expanded)
return;
var children = treeItem.children;
var newPrefix = treeItem === treeItem.treeOutline ? "" : prefix + " ";
for (var i = 0; depth && children && i < children.length; ++i) {
if (!children[i]._elementCloseTag)
print(children[i], newPrefix, depth - 1);
}
}
}
InspectorTest.expandElementsTree = function(callback) InspectorTest.expandElementsTree = function(callback)
{ {
var expandedSomething = false; var expandedSomething = false;
......
Tests DOM update highlights in the DOM tree.
Running: testDumpInitial
========= Original ========
Running: testSetAttributeOtherValue
//*[@id="attrTest"]/@attrfoo bar
Running: testSetAttributeEmpty
//*[@id="attrTest"]/@attrfoo (empty)
Running: testAddAttribute
//*[@id="attrTest"]/@attrbar newBar
Running: testRemoveAttribute
//*[@id="attrTest"]
Running: testAppendChildToEmpty
//*[@id="childTest"]
Running: testAppendChildToExpanded
//*[@id="child2"]
Running: testRemoveChild1
//*[@id="childTest"]
Running: testRemoveChild2
//*[@id="childTest"]
Running: testSetTextContent
//*[@id="textTest"]/text() "Text"
Running: testSetTextNodeTextContent
//*[@id="textTest"]/text() "NewText"
Running: testRemoveInlineTextNode
//*[@id="textTest"]
Running: testSetTextContentWithEmptyText
//*[@id="textTest"]/text() "Text"
Running: testClearTextNodeTextContent
//*[@id="textTest"]
//*[@id="textTest"]/text() ""
<html>
<head>
<script src="../../http/tests/inspector/inspector-test.js"></script>
<script src="../../http/tests/inspector/elements-test.js"></script>
<script>
function appendChild(parentId, id)
{
var e = document.createElement("span");
e.id = id;
document.getElementById(parentId).appendChild(e);
}
function remove(id)
{
document.getElementById(id).remove();
}
function removeFirstChild(id)
{
document.getElementById(id).firstChild.remove();
}
function setAttribute(id, name, value)
{
var e = document.getElementById(id);
if (value === undefined)
e.removeAttribute(name);
else
e.setAttribute(name, value);
}
function setTextContent(id, content)
{
document.getElementById(id).textContent = content;
}
function setFirstChildTextContent(id, content)
{
document.getElementById(id).firstChild.textContent = content;
}
function test()
{
var containerNode;
var attrTestNode;
var childTestNode;
var textTestNode;
InspectorTest.runTestSuite([
function testDumpInitial(next)
{
function callback(node)
{
containerNode = InspectorTest.expandedNodeWithId("container");
attrTestNode = InspectorTest.expandedNodeWithId("attrTest");
childTestNode = InspectorTest.expandedNodeWithId("childTest");
textTestNode = InspectorTest.expandedNodeWithId("textTest");
InspectorTest.addResult("========= Original ========");
InspectorTest.dumpDOMUpdateHighlights(containerNode);
next();
}
InspectorTest.expandElementsTree(callback);
},
function testSetAttributeOtherValue(next)
{
runAndDumpHighlights("setAttribute('attrTest', 'attrFoo', 'bar')", attrTestNode, next);
},
function testSetAttributeEmpty(next)
{
runAndDumpHighlights("setAttribute('attrTest', 'attrFoo', '')", attrTestNode, next);
},
function testAddAttribute(next)
{
runAndDumpHighlights("setAttribute('attrTest', 'attrBar', 'newBar')", attrTestNode, next);
},
function testRemoveAttribute(next)
{
runAndDumpHighlights("setAttribute('attrTest', 'attrFoo')", attrTestNode, next);
},
function testAppendChildToEmpty(next)
{
runAndDumpHighlights("appendChild('childTest', 'child1')", childTestNode, callback);
function callback()
{
// Expand the #childTest node.
InspectorTest.expandElementsTree(next);
}
},
function testAppendChildToExpanded(next)
{
runAndDumpHighlights("appendChild('childTest', 'child2')", childTestNode, next);
},
function testRemoveChild1(next)
{
runAndDumpHighlights("remove('child1')", childTestNode, next);
},
function testRemoveChild2(next)
{
runAndDumpHighlights("remove('child2')", childTestNode, next);
},
function testSetTextContent(next)
{
runAndDumpHighlights("setTextContent('textTest', 'Text')", textTestNode, next);
},
function testSetTextNodeTextContent(next)
{
runAndDumpHighlights("setFirstChildTextContent('textTest', 'NewText')", textTestNode, next);
},
function testRemoveInlineTextNode(next)
{
runAndDumpHighlights("removeFirstChild('textTest')", textTestNode, next);
},
function testSetTextContentWithEmptyText(next)
{
runAndDumpHighlights("setTextContent('textTest', 'Text')", textTestNode, next);
},
function testClearTextNodeTextContent(next)
{
runAndDumpHighlights("setFirstChildTextContent('textTest', '')", textTestNode, next);
}
]);
function runAndDumpHighlights(script, root, next)
{
InspectorTest.evaluateInPage(script, callback);
function callback()
{
// InspectorTest.dumpElementsTree(root);
InspectorTest.dumpDOMUpdateHighlights(root);
var treeOutline = InspectorTest.firstElementsTreeOutline();
var highlights = treeOutline._element.getElementsByClassName("dom-update-highlight");
for (var i = 0; i < highlights.length; ++i)
highlights[i].classList.remove("dom-update-highlight");
next();
}
}
}
</script>
</head>
<body onload="runTest()">
<p>
Tests DOM update highlights in the DOM tree.
</p>
<div id="container">
<div id="attrTest" attrFoo="foo"></div>
<div id="childTest"></div>
<div id="textTest"></div>
</div>
</body>
</html>
...@@ -91,6 +91,7 @@ WebInspector.Settings = function() ...@@ -91,6 +91,7 @@ WebInspector.Settings = function()
this.disableOverridesWarning = this.createSetting("disableOverridesWarning", false); this.disableOverridesWarning = this.createSetting("disableOverridesWarning", false);
this.testPath = this.createSetting("testPath", ""); this.testPath = this.createSetting("testPath", "");
this.frameViewerHideChromeWindow = this.createSetting("frameViewerHideChromeWindow", false); this.frameViewerHideChromeWindow = this.createSetting("frameViewerHideChromeWindow", false);
this.highlightDOMUpdates = this.createSetting("highlightDOMUpdates", true);
// Rendering options // Rendering options
this.showPaintRects = this.createSetting("showPaintRects", false); this.showPaintRects = this.createSetting("showPaintRects", false);
......
...@@ -271,3 +271,16 @@ body.inactive button.text-button, .text-button:disabled { ...@@ -271,3 +271,16 @@ body.inactive button.text-button, .text-button:disabled {
.outline-disclosure .nowrap li { .outline-disclosure .nowrap li {
word-wrap: normal; word-wrap: normal;
} }
/* DOM update highlight */
@-webkit-keyframes dom-update-highlight-animation {
from { background-color: rgb(255, 242, 114); }
90% { background-color: rgb(255, 242, 114); }
to { background-color: inherit; }
}
.dom-update-highlight {
-webkit-animation: dom-update-highlight-animation 2s 1;
color: red !important;
}
...@@ -94,6 +94,13 @@ ...@@ -94,6 +94,13 @@
"title": "Show rulers", "title": "Show rulers",
"settingName": "showMetricsRulers", "settingName": "showMetricsRulers",
"settingType": "checkbox" "settingType": "checkbox"
},
{
"type": "ui-setting",
"section": "Elements",
"title": "Highlight DOM updates",
"settingName": "highlightDOMUpdates",
"settingType": "checkbox"
} }
], ],
"dependencies": [ "dependencies": [
......
...@@ -462,6 +462,7 @@ function Symbol(description) {} ...@@ -462,6 +462,7 @@ function Symbol(description) {}
/** /**
* @interface * @interface
* @extends $jscomp.Iterable.<T>
* @template T * @template T
*/ */
var Iterator = function() { } var Iterator = function() { }
...@@ -470,14 +471,20 @@ Iterator.prototype = { ...@@ -470,14 +471,20 @@ Iterator.prototype = {
/** /**
* @return {{done: boolean, value: (T|undefined)}} * @return {{done: boolean, value: (T|undefined)}}
*/ */
next: function() { } next: function() { },
// FIXME: This should be removed once transpilation is not required for closure compiler ES6
$$iterator: function() { }
} }
// FIXME: $jscomp.Iterable hack below should be removed once transpilation is not required for closure compiler ES6
/** /**
* @constructor * @constructor
* @implements $jscomp.Iterable.<!Array.<K|V>>
* @param {!Array.<!Array.<K|V>>|!Iterator.<!Array.<K|V>>=} iterable
* @template K, V * @template K, V
*/ */
var Map = function() { } var Map = function(iterable) { }
Map.prototype = { Map.prototype = {
/** /**
...@@ -502,6 +509,11 @@ Map.prototype = { ...@@ -502,6 +509,11 @@ Map.prototype = {
*/ */
values: function() { }, values: function() { },
/**
* @return {!Array.<!Array.<K|V>>}
*/
entries: function() { },
/** /**
* @param {K} key * @param {K} key
* @return {V} * @return {V}
...@@ -519,7 +531,10 @@ Map.prototype = { ...@@ -519,7 +531,10 @@ Map.prototype = {
/** /**
* @return {number} * @return {number}
*/ */
get size() { } get size() { },
// FIXME: This should be removed once transpilation is not required for closure compiler ES6
$$iterator: function() { }
} }
// FIXME: $jscomp.Iterable hack below should be removed once transpilation is not required for closure compiler ES6 // FIXME: $jscomp.Iterable hack below should be removed once transpilation is not required for closure compiler ES6
......
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