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() |