Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(343)

Unified Diff: Source/devtools/front_end/elements/ElementsTreeOutline.js

Issue 701153002: DevTools: [Elements] Highlight DOM updates (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Fix tests (for ETO not wired to DOMModel) Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/devtools/front_end/elements/ElementsTreeOutline.js
diff --git a/Source/devtools/front_end/elements/ElementsTreeOutline.js b/Source/devtools/front_end/elements/ElementsTreeOutline.js
index a2839eab640a6ec4be6d3a533ada82b70466d99d..08ee91c6974157aefe86a31183383372212370d4 100644
--- a/Source/devtools/front_end/elements/ElementsTreeOutline.js
+++ b/Source/devtools/front_end/elements/ElementsTreeOutline.js
@@ -1053,6 +1053,15 @@ WebInspector.ElementsTreeElement.ChildrenDisplayMode = {
HasChildren: 2
}
+/**
+ * @param {!WebInspector.ElementsTreeElement} treeElement
+ */
+WebInspector.ElementsTreeElement.animateOnDOMUpdate = function(treeElement)
+{
+ var tagName = treeElement.listItemElement.querySelector(".webkit-html-tag-name");
+ WebInspector.runCSSAnimationOnce(tagName || treeElement.listItemElement, "dom-update-highlight");
+}
+
WebInspector.ElementsTreeElement.prototype = {
/**
* @return {!WebInspector.DOMNode}
@@ -1296,6 +1305,21 @@ WebInspector.ElementsTreeElement.prototype = {
},
/**
+ * @param {!WebInspector.DOMNode=} node
+ * @return {!WebInspector.ElementsTreeUpdater.UpdateInfo|undefined}
+ */
+ _updateInfo: function(node)
+ {
+ if (!WebInspector.settings.highlightDOMUpdates.get())
+ return undefined;
+ var updater = this.treeOutline._elementsTreeUpdater;
+ if (!updater)
+ return undefined;
+ var effectiveNode = node || this._node;
+ return updater._recentlyModifiedNodes.get(effectiveNode) || updater._recentlyModifiedParentNodes.get(effectiveNode);
+ },
+
+ /**
* @param {boolean} fullRefresh
* @param {?Array.<!WebInspector.DOMNode>} children
*/
@@ -1348,6 +1372,9 @@ WebInspector.ElementsTreeElement.prototype = {
} else {
// No existing element found, insert a new element.
var newElement = this.insertChildElement(child, i);
+ var updateRecord = this._updateInfo();
+ if (updateRecord)
+ WebInspector.ElementsTreeElement.animateOnDOMUpdate(newElement);
if (child === selectedNode)
elementToSelect = newElement;
// If a node was inserted in the middle of existing list dynamically we might need to increase the limit.
@@ -2285,6 +2312,10 @@ WebInspector.ElementsTreeElement.prototype = {
var attrValueElement = attrSpanElement.createChild("span", "webkit-html-attribute-value");
+ var updates = this._updateInfo();
+ if (updates && updates.isAttributeModified(name))
+ WebInspector.runCSSAnimationOnce(hasText ? attrValueElement : attrNameElement, "dom-update-highlight");
+
/**
* @this {WebInspector.ElementsTreeElement}
* @param {string} value
@@ -2356,14 +2387,34 @@ WebInspector.ElementsTreeElement.prototype = {
tagElement.createTextChild("<");
var tagNameElement = tagElement.createChild("span", isClosingTag ? "" : "webkit-html-tag-name");
tagNameElement.textContent = (isClosingTag ? "/" : "") + tagName;
- if (!isClosingTag && node.hasAttributes()) {
- var attributes = node.attributes();
- for (var i = 0; i < attributes.length; ++i) {
- var attr = attributes[i];
- tagElement.createTextChild(" ");
- this._buildAttributeDOM(tagElement, attr.name, attr.value, false, node, linkify);
+ if (!isClosingTag) {
+ if (node.hasAttributes()) {
+ var attributes = node.attributes();
+ for (var i = 0; i < attributes.length; ++i) {
+ var attr = attributes[i];
+ tagElement.createTextChild(" ");
+ this._buildAttributeDOM(tagElement, attr.name, attr.value, false, node, linkify);
+ }
+ }
+ var hasUpdates;
+ var updates = this._updateInfo();
+ if (updates) {
+ hasUpdates |= updates.hasRemovedAttributes();
+ var hasInlineText = this._childrenDisplayMode === WebInspector.ElementsTreeElement.ChildrenDisplayMode.InlineText;
+
+ hasUpdates |= (!hasInlineText || this.expanded) && updates.hasChangedChildren();
+
+ // Highlight the tag name, as the inserted node is not visible (either child of a collapsed tree element or empty inline text).
+ hasUpdates |= !this.expanded && updates.hasInsertedNodes() && (!hasInlineText || this._node.firstChild.nodeValue().length === 0);
+
+ // Highlight the tag name, as the inline text node value has been cleared.
+ // The respective empty node will be highlighted, but the highlight will not be visible to the user.
+ hasUpdates |= hasInlineText && (updates.isCharDataModified() || updates.hasChangedChildren()) && this._node.firstChild.nodeValue().length === 0;
}
+ if (hasUpdates)
+ WebInspector.runCSSAnimationOnce(tagNameElement, "dom-update-highlight");
}
+
tagElement.createTextChild(">");
parentElement.createTextChild("\u200B");
},
@@ -2375,7 +2426,6 @@ WebInspector.ElementsTreeElement.prototype = {
_convertWhitespaceToEntities: function(text)
{
var result = "";
- var resultLength = 0;
var lastIndexAfterEntity = 0;
var entityRanges = [];
var charToEntity = WebInspector.ElementsTreeOutline.MappedCharToEntity;
@@ -2442,6 +2492,12 @@ WebInspector.ElementsTreeElement.prototype = {
info.titleDOM.createTextChild("\u200B");
info.hasChildren = false;
this._buildTagDOM(info.titleDOM, tagName, true, false);
+ var updates = this._updateInfo();
+ if (updates && (updates.hasInsertedNodes() || updates.hasChangedChildren()))
+ WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight");
+ updates = this._updateInfo(this._node.firstChild);
+ if (updates && updates.isCharDataModified())
+ WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight");
break;
case WebInspector.ElementsTreeElement.ChildrenDisplayMode.NoChildren:
@@ -2471,6 +2527,9 @@ WebInspector.ElementsTreeElement.prototype = {
textNodeElement.textContent = result.text;
WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, "webkit-html-entity-value");
info.titleDOM.createTextChild("\"");
+ var updates = this._updateInfo();
+ if (updates && updates.isCharDataModified())
+ WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight");
}
break;
@@ -2499,6 +2558,7 @@ WebInspector.ElementsTreeElement.prototype = {
var cdataElement = info.titleDOM.createChild("span", "webkit-html-text-node");
cdataElement.createTextChild("<![CDATA[" + node.nodeValue() + "]]>");
break;
+
case Node.DOCUMENT_FRAGMENT_NODE:
var fragmentElement = info.titleDOM.createChild("span", "webkit-html-fragment");
if (node.isInShadowTree()) {
@@ -2747,18 +2807,18 @@ WebInspector.ElementsTreeUpdater = function(domModel, treeOutline)
{
domModel.addEventListener(WebInspector.DOMModel.Events.NodeInserted, this._nodeInserted, this);
domModel.addEventListener(WebInspector.DOMModel.Events.NodeRemoved, this._nodeRemoved, this);
- domModel.addEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributesUpdated, this);
- domModel.addEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributesUpdated, this);
+ domModel.addEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributeModified, this);
+ domModel.addEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributeRemoved, this);
domModel.addEventListener(WebInspector.DOMModel.Events.CharacterDataModified, this._characterDataModified, this);
domModel.addEventListener(WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdated, this);
domModel.addEventListener(WebInspector.DOMModel.Events.ChildNodeCountUpdated, this._childNodeCountUpdated, this);
this._domModel = domModel;
this._treeOutline = treeOutline;
- /** @type {!Set.<!WebInspector.DOMNode>} */
- this._recentlyModifiedNodes = new Set();
- /** @type {!Set.<!WebInspector.DOMNode>} */
- this._recentlyModifiedParentNodes = new Set();
+ /** @type {!Map.<!WebInspector.DOMNode, !WebInspector.ElementsTreeUpdater.UpdateInfo>} */
+ this._recentlyModifiedNodes = new Map();
+ /** @type {!Map.<!WebInspector.DOMNode, !WebInspector.ElementsTreeUpdater.UpdateInfo>} */
+ this._recentlyModifiedParentNodes = new Map();
}
WebInspector.ElementsTreeUpdater.prototype = {
@@ -2766,8 +2826,8 @@ WebInspector.ElementsTreeUpdater.prototype = {
{
this._domModel.removeEventListener(WebInspector.DOMModel.Events.NodeInserted, this._nodeInserted, this);
this._domModel.removeEventListener(WebInspector.DOMModel.Events.NodeRemoved, this._nodeRemoved, this);
- this._domModel.removeEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributesUpdated, this);
- this._domModel.removeEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributesUpdated, this);
+ this._domModel.removeEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributeModified, this);
+ this._domModel.removeEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributeRemoved, this);
this._domModel.removeEventListener(WebInspector.DOMModel.Events.CharacterDataModified, this._characterDataModified, this);
this._domModel.removeEventListener(WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdated, this);
this._domModel.removeEventListener(WebInspector.DOMModel.Events.ChildNodeCountUpdated, this._childNodeCountUpdated, this);
@@ -2775,33 +2835,47 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @param {?WebInspector.DOMNode} parentNode
+ * @return {!WebInspector.ElementsTreeUpdater.UpdateInfo}
*/
_parentNodeModified: function(parentNode)
{
if (!parentNode)
- return;
- this._recentlyModifiedParentNodes.add(parentNode);
+ return new WebInspector.ElementsTreeUpdater.UpdateInfo(); // Bogus info.
+
+ var record = this._recentlyModifiedParentNodes.get(parentNode);
+ if (!record) {
+ record = new WebInspector.ElementsTreeUpdater.UpdateInfo();
+ this._recentlyModifiedParentNodes.set(parentNode, record);
+ }
var treeElement = this._treeOutline.findTreeElement(parentNode);
if (treeElement) {
var oldDisplayMode = treeElement._childrenDisplayMode;
treeElement._updateChildrenDisplayMode();
if (treeElement._childrenDisplayMode !== oldDisplayMode)
- this._nodeModified(parentNode);
+ this._nodeModified(parentNode).childrenModified();
}
if (this._treeOutline._visible)
this._updateModifiedNodesSoon();
+
+ return record;
},
/**
* @param {!WebInspector.DOMNode} node
+ * @return {!WebInspector.ElementsTreeUpdater.UpdateInfo}
*/
_nodeModified: function(node)
{
- this._recentlyModifiedNodes.add(node);
if (this._treeOutline._visible)
this._updateModifiedNodesSoon();
+ var record = this._recentlyModifiedNodes.get(node);
+ if (!record) {
+ record = new WebInspector.ElementsTreeUpdater.UpdateInfo();
+ this._recentlyModifiedNodes.set(node, record);
+ }
+ return record;
},
/**
@@ -2822,10 +2896,19 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @param {!WebInspector.Event} event
*/
- _attributesUpdated: function(event)
+ _attributeModified: function(event)
{
var node = /** @type {!WebInspector.DOMNode} */ (event.data.node);
- this._nodeModified(node);
+ this._nodeModified(node).attributeModified(event.data.name);
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _attributeRemoved: function(event)
+ {
+ var node = /** @type {!WebInspector.DOMNode} */ (event.data.node);
+ this._nodeModified(node).attributeRemoved(event.data.name);
},
/**
@@ -2834,8 +2917,8 @@ WebInspector.ElementsTreeUpdater.prototype = {
_characterDataModified: function(event)
{
var node = /** @type {!WebInspector.DOMNode} */ (event.data);
- this._parentNodeModified(node.parentNode);
- this._nodeModified(node);
+ this._parentNodeModified(node.parentNode).charDataModified();
+ this._nodeModified(node).charDataModified();
},
/**
@@ -2844,7 +2927,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
_nodeInserted: function(event)
{
var node = /** @type {!WebInspector.DOMNode} */ (event.data);
- this._parentNodeModified(node.parentNode);
+ this._parentNodeModified(node.parentNode).nodeInserted(node);
},
/**
@@ -2855,7 +2938,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
var node = /** @type {!WebInspector.DOMNode} */ (event.data.node);
var parentNode = /** @type {!WebInspector.DOMNode} */ (event.data.parent);
this._treeOutline._resetClipboardIfNeeded(node);
- this._parentNodeModified(parentNode);
+ this._parentNodeModified(parentNode).childrenModified();
},
/**
@@ -2881,7 +2964,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
delete this._updateModifiedNodesTimeout;
}
- var updatedNodes = this._recentlyModifiedNodes.valuesArray().concat(this._recentlyModifiedParentNodes.valuesArray());
+ var updatedNodes = this._recentlyModifiedNodes.keysArray().concat(this._recentlyModifiedParentNodes.keysArray());
var hidePanelWhileUpdating = updatedNodes.length > 10;
if (hidePanelWhileUpdating) {
var treeOutlineContainerElement = this._treeOutline.element.parentNode;
@@ -2893,18 +2976,16 @@ WebInspector.ElementsTreeUpdater.prototype = {
// Document's children have changed, perform total update.
this._treeOutline.update();
} else {
- var nodes = this._recentlyModifiedNodes.valuesArray();
- for (var i = 0, size = nodes.length; i < size; ++i) {
- var nodeItem = this._treeOutline.findTreeElement(nodes[i]);
+ for (var node of this._recentlyModifiedNodes.keys()) {
+ var nodeItem = this._treeOutline.findTreeElement(node);
if (nodeItem)
- nodeItem.updateTitle();
+ nodeItem.updateTitle(false);
}
- var parentNodes = this._recentlyModifiedParentNodes.valuesArray();
- for (var i = 0, size = parentNodes.length; i < size; ++i) {
- var parentNodeItem = this._treeOutline.findTreeElement(parentNodes[i]);
+ for (var node of this._recentlyModifiedParentNodes.keys()) {
+ var parentNodeItem = this._treeOutline.findTreeElement(node);
if (parentNodeItem && parentNodeItem.populated)
- parentNodeItem.updateChildren();
+ parentNodeItem.updateChildren(false);
}
}
@@ -2932,6 +3013,108 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @constructor
+ */
+WebInspector.ElementsTreeUpdater.UpdateInfo = function()
+{
+}
+
+WebInspector.ElementsTreeUpdater.UpdateInfo.prototype = {
+ /**
+ * @param {string} attrName
+ */
+ attributeModified: function(attrName)
+ {
+ if (this._removedAttributes && this._removedAttributes.has(attrName))
+ this._removedAttributes.delete(attrName);
+ if (!this._modifiedAttributes)
+ this._modifiedAttributes = /** @type {!Set.<string>} */ (new Set());
+ this._modifiedAttributes.add(attrName);
+ },
+
+ /**
+ * @param {string} attrName
+ */
+ attributeRemoved: function(attrName)
+ {
+ if (this._modifiedAttributes && this._modifiedAttributes.has(attrName))
+ this._modifiedAttributes.delete(attrName);
+ if (!this._removedAttributes)
+ this._removedAttributes = /** @type {!Set.<string>} */ (new Set());
+ this._removedAttributes.add(attrName);
+ },
+
+ /**
+ * @param {!WebInspector.DOMNode} node
+ */
+ nodeInserted: function(node)
+ {
+ if (!this._insertedNodes)
+ this._insertedNodes = /** @type {!Set.<!WebInspector.DOMNode>} */ (new Set());
+ this._insertedNodes.add(/** @type {!WebInspector.DOMNode} */ (node));
+ },
+
+ charDataModified: function()
+ {
+ this._charDataModified = true;
+ },
+
+ childrenModified: function()
+ {
+ this._hasChangedChildren = true;
+ },
+
+ /**
+ * @param {string} attributeName
+ * @return {boolean}
+ */
+ isAttributeModified: function(attributeName)
+ {
+ return this._modifiedAttributes && this._modifiedAttributes.has(attributeName);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasRemovedAttributes: function()
+ {
+ return !!this._removedAttributes && !!this._removedAttributes.size;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasInsertedNodes: function()
+ {
+ return !!this._insertedNodes && !!this._insertedNodes.size;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isCharDataModified: function()
+ {
+ return !!this._charDataModified;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isNodeInserted: function(node)
+ {
+ return !!this._insertedNodes && this._insertedNodes.has(node);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ hasChangedChildren: function()
+ {
+ return !!this._hasChangedChildren;
+ }
+}
+
+/**
+ * @constructor
* @implements {WebInspector.Renderer}
*/
WebInspector.ElementsTreeOutline.Renderer = function()
« no previous file with comments | « Source/devtools/front_end/common/Settings.js ('k') | Source/devtools/front_end/elements/elementsTreeOutline.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698