Chromium Code Reviews| 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..7701b0ef24107d42267b3bcf948e72af3abfb01d 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,19 @@ WebInspector.ElementsTreeElement.prototype = { |
| }, |
| /** |
| + * @param {!WebInspector.DOMNode=} node |
| + * @return {!WebInspector.ElementsTreeUpdater.UpdateInfo|undefined} |
| + */ |
| + _updateInfo: function(node) |
| + { |
| + if (!WebInspector.settings.highlightDOMUpdates.get()) |
| + return undefined; |
| + var effectiveNode = node || this._node; |
| + var updater = this.treeOutline._elementsTreeUpdater; |
| + return updater._recentlyModifiedNodes.get(effectiveNode) || updater._recentlyModifiedParentNodes.get(effectiveNode); |
| + }, |
| + |
| + /** |
| * @param {boolean} fullRefresh |
| * @param {?Array.<!WebInspector.DOMNode>} children |
| */ |
| @@ -1348,6 +1370,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. |
| @@ -1356,7 +1381,7 @@ WebInspector.ElementsTreeElement.prototype = { |
| } |
| } |
| - this.updateTitle(); |
| + this.updateTitle(false); |
|
pfeldman
2014/11/10 15:38:20
revert
apavlov
2014/11/10 16:07:37
Done.
|
| this._adjustCollapsedRange(); |
| if (this._node.nodeType() === Node.ELEMENT_NODE && this._hasChildTreeElements()) |
| @@ -2154,8 +2179,9 @@ WebInspector.ElementsTreeElement.prototype = { |
| /** |
| * @param {boolean=} onlySearchQueryChanged |
| + * @param {!WebInspector.ElementsTreeUpdater.UpdateInfo=} updates |
| */ |
| - updateTitle: function(onlySearchQueryChanged) |
| + updateTitle: function(onlySearchQueryChanged, updates) |
|
pfeldman
2014/11/10 15:38:20
revert
apavlov
2014/11/10 16:07:37
Done.
|
| { |
| // If we are editing, return early to prevent canceling the edit. |
| // After editing is committed updateTitle will be called. |
| @@ -2285,6 +2311,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 +2386,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 +2425,6 @@ WebInspector.ElementsTreeElement.prototype = { |
| _convertWhitespaceToEntities: function(text) |
| { |
| var result = ""; |
| - var resultLength = 0; |
| var lastIndexAfterEntity = 0; |
| var entityRanges = []; |
| var charToEntity = WebInspector.ElementsTreeOutline.MappedCharToEntity; |
| @@ -2442,6 +2491,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 +2526,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 +2557,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 +2806,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 +2825,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); |
| @@ -2780,18 +2839,25 @@ WebInspector.ElementsTreeUpdater.prototype = { |
| { |
| if (!parentNode) |
| return; |
| - this._recentlyModifiedParentNodes.add(parentNode); |
| + |
| + 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; |
| }, |
| /** |
| @@ -2799,9 +2865,14 @@ WebInspector.ElementsTreeUpdater.prototype = { |
| */ |
| _nodeModified: function(node) |
|
pfeldman
2014/11/10 15:38:20
annotate
apavlov
2014/11/10 16:07:37
Done.
|
| { |
| - 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 +2893,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 +2914,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 +2924,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 +2935,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 +2961,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 +2973,18 @@ WebInspector.ElementsTreeUpdater.prototype = { |
| // Document's children have changed, perform total update. |
| this._treeOutline.update(); |
| } else { |
| - var nodes = this._recentlyModifiedNodes.valuesArray(); |
| + var nodes = this._recentlyModifiedNodes.keysArray(); |
|
vsevik
2014/11/10 16:00:51
Let's switch to for-of while you are here.
apavlov
2014/11/10 16:07:37
Done.
|
| for (var i = 0, size = nodes.length; i < size; ++i) { |
| var nodeItem = this._treeOutline.findTreeElement(nodes[i]); |
| if (nodeItem) |
| - nodeItem.updateTitle(); |
| + nodeItem.updateTitle(false); |
| } |
| - var parentNodes = this._recentlyModifiedParentNodes.valuesArray(); |
| + var parentNodes = this._recentlyModifiedParentNodes.keysArray(); |
|
vsevik
2014/11/10 16:00:51
Let's switch to for-of while you are here.
apavlov
2014/11/10 16:07:37
Done.
|
| for (var i = 0, size = parentNodes.length; i < size; ++i) { |
| var parentNodeItem = this._treeOutline.findTreeElement(parentNodes[i]); |
| if (parentNodeItem && parentNodeItem.populated) |
| - parentNodeItem.updateChildren(); |
| + parentNodeItem.updateChildren(false); |
| } |
| } |
| @@ -2932,6 +3012,123 @@ WebInspector.ElementsTreeUpdater.prototype = { |
| /** |
| * @constructor |
| + */ |
| +WebInspector.ElementsTreeUpdater.UpdateInfo = function() |
| +{ |
| + this._removedAttributeCount = 0; |
|
pfeldman
2014/11/10 15:38:20
Can we use this._removedAttributes size?
apavlov
2014/11/10 16:07:37
Done.
|
| +} |
| + |
| +WebInspector.ElementsTreeUpdater.UpdateInfo.prototype = { |
| + /** |
| + * @param {string} attrName |
| + */ |
| + attributeModified: function(attrName) |
| + { |
| + if (this._removedAttributes && this._removedAttributes.has(attrName)) { |
| + this._removedAttributes.delete(attrName); |
| + --this._removedAttributeCount; |
| + } |
| + 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); |
| + ++this._removedAttributeCount; |
| + }, |
| + |
| + /** |
| + * @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._removedAttributeCount; |
| + }, |
| + |
| + /** |
| + * @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; |
| + } |
| +} |
| + |
| +/** |
| + * @enum {number} |
| + */ |
| +WebInspector.ElementsTreeUpdater.UpdateInfo.ChangeType = { |
|
pfeldman
2014/11/10 15:38:20
remove
apavlov
2014/11/10 16:07:37
Done.
|
| + NoChange: 0, |
| + AttrModified: 1, |
| + AttrRemoved: 2, |
| + ChildrenModified: 3, |
| + NodeInserted: 4 |
| +} |
| + |
| +/** |
| + * @constructor |
| * @implements {WebInspector.Renderer} |
| */ |
| WebInspector.ElementsTreeOutline.Renderer = function() |