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

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: Reworked the way titles are updated (through updateTitle() now) 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 c5e2d6f8f3eff068f09bcb413942338cdb083757..1ad9d011bcb06da51ffef765b0237b8812a7a16f 100644
--- a/Source/devtools/front_end/elements/ElementsTreeOutline.js
+++ b/Source/devtools/front_end/elements/ElementsTreeOutline.js
@@ -1018,8 +1018,9 @@ WebInspector.ElementsTreeOutline.PseudoStateDecorator.prototype = {
* @extends {TreeElement}
* @param {!WebInspector.DOMNode} node
* @param {boolean=} elementCloseTag
+ * @param {boolean=} isUpdated
*/
-WebInspector.ElementsTreeElement = function(node, elementCloseTag)
+WebInspector.ElementsTreeElement = function(node, elementCloseTag, isUpdated)
{
// The title will be updated in onattach.
TreeElement.call(this, "", node);
@@ -1030,6 +1031,7 @@ WebInspector.ElementsTreeElement = function(node, elementCloseTag)
if (this._node.nodeType() == Node.ELEMENT_NODE && !elementCloseTag)
this._canAddAttributes = true;
+ this._isUpdated = isUpdated;
this._searchQuery = null;
this._expandedChildrenLimit = WebInspector.ElementsTreeElement.InitialChildrenLimit;
}
@@ -1151,7 +1153,7 @@ WebInspector.ElementsTreeElement.prototype = {
this._expandedChildrenLimit = x;
if (this.treeOutline && !this._updateChildrenInProgress)
- this._updateChildren(true, this.children);
+ this._updateChildren(true, undefined, this.children);
},
get expandedChildCount()
@@ -1179,7 +1181,7 @@ WebInspector.ElementsTreeElement.prototype = {
if (index >= this.expandedChildrenLimit) {
this._expandedChildrenLimit = index + 1;
- this._updateChildren(true, this.children);
+ this._updateChildren(true, undefined, this.children);
}
// Whether index-th child is visible in the children tree
@@ -1244,24 +1246,26 @@ WebInspector.ElementsTreeElement.prototype = {
/**
* @param {boolean=} fullRefresh
+ * @param {!WebInspector.ElementsTreeUpdater.Record=} updates
*/
- updateChildren: function(fullRefresh)
+ updateChildren: function(fullRefresh, updates)
{
if (!this.hasChildren)
return;
console.assert(!this._elementCloseTag);
- this._node.getChildNodes(this._updateChildren.bind(this, fullRefresh || false));
+ this._node.getChildNodes(this._updateChildren.bind(this, fullRefresh || false, updates));
},
/**
* @param {!WebInspector.DOMNode} child
* @param {number} index
* @param {boolean=} closingTag
+ * @param {boolean=} isUpdated
* @return {!WebInspector.ElementsTreeElement}
*/
- insertChildElement: function(child, index, closingTag)
+ insertChildElement: function(child, index, closingTag, isUpdated)
{
- var newElement = new WebInspector.ElementsTreeElement(child, closingTag);
+ var newElement = new WebInspector.ElementsTreeElement(child, closingTag, isUpdated);
newElement.selectable = this.treeOutline._selectEnabled;
this.insertChild(newElement, index);
return newElement;
@@ -1278,9 +1282,10 @@ WebInspector.ElementsTreeElement.prototype = {
/**
* @param {boolean} fullRefresh
+ * @param {!WebInspector.ElementsTreeUpdater.Record|undefined} childUpdates
* @param {?Array.<!WebInspector.DOMNode>} children
*/
- _updateChildren: function(fullRefresh, children)
+ _updateChildren: function(fullRefresh, childUpdates, children)
{
if (!children || this._updateChildrenInProgress || !this.treeOutline._visible)
return;
@@ -1327,7 +1332,7 @@ WebInspector.ElementsTreeElement.prototype = {
} else {
// No existing element found, insert a new element.
if (treeChildIndex < this.expandedChildrenLimit) {
- var newElement = this.insertChildElement(child, treeChildIndex);
+ var newElement = this.insertChildElement(child, treeChildIndex, false, !!childUpdates && childUpdates.isNodeInserted(child));
pfeldman 2014/11/07 12:54:15 Can you do something like: if (childUpdates.isNod
apavlov 2014/11/10 09:52:56 Done something along these lines with mыrging...
if (child === selectedNode)
elementToSelect = newElement;
if (this.expandedChildCount > this.expandedChildrenLimit)
@@ -1360,7 +1365,7 @@ WebInspector.ElementsTreeElement.prototype = {
}
var elementToSelect = updateChildrenOfNode.call(this);
- this.updateTitle();
+ this.updateTitle(false, childUpdates);
this._adjustCollapsedRange();
var lastChild = this.children[this.children.length - 1];
@@ -2162,14 +2167,16 @@ WebInspector.ElementsTreeElement.prototype = {
/**
* @param {boolean=} onlySearchQueryChanged
+ * @param {!WebInspector.ElementsTreeUpdater.Record=} updates
*/
- updateTitle: function(onlySearchQueryChanged)
+ updateTitle: function(onlySearchQueryChanged, updates)
{
// If we are editing, return early to prevent canceling the edit.
// After editing is committed updateTitle will be called.
if (this._editing)
return;
+ this._updates = updates;
if (onlySearchQueryChanged) {
if (this._highlightResult)
this._updateSearchHighlight(false);
@@ -2185,6 +2192,7 @@ WebInspector.ElementsTreeElement.prototype = {
delete this._highlightResult;
}
+ delete this._isUpdated;
delete this.selectionElement;
if (this.selected)
this.updateSelection();
@@ -2293,6 +2301,9 @@ WebInspector.ElementsTreeElement.prototype = {
var attrValueElement = attrSpanElement.createChild("span", "webkit-html-attribute-value");
+ if (this._updates && this._updates.isAttributeModified(name))
+ WebInspector.runCSSAnimationOnce(hasText ? attrValueElement : attrNameElement, "dom-update-highlight");
+
/**
* @this {WebInspector.ElementsTreeElement}
* @param {string} value
@@ -2364,14 +2375,19 @@ 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);
+ }
}
+ if (this._isUpdated || (this._updates && (this._updates.hasRemovedAttributes() || this._updates.hasChangedChildren())))
+ WebInspector.runCSSAnimationOnce(tagNameElement, "dom-update-highlight");
}
+
tagElement.createTextChild(">");
parentElement.createTextChild("\u200B");
},
@@ -2454,6 +2470,8 @@ WebInspector.ElementsTreeElement.prototype = {
info.titleDOM.createTextChild("\u200B");
this._buildTagDOM(info.titleDOM, tagName, true, false);
info.hasChildren = false;
+ if (this._updates && (this._updates.isCharDataModified() || this._updates.hasInsertedNodes()))
+ WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight");
}
break;
@@ -2477,6 +2495,8 @@ WebInspector.ElementsTreeElement.prototype = {
textNodeElement.textContent = result.text;
WebInspector.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, "webkit-html-entity-value");
info.titleDOM.createTextChild("\"");
+ if (this._isUpdated || (this._updates && this._updates.isCharDataModified()))
+ WebInspector.runCSSAnimationOnce(textNodeElement, "dom-update-highlight");
}
break;
@@ -2505,6 +2525,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()) {
@@ -2735,18 +2756,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.Record>} */
+ this._recentlyModifiedNodes = new Map();
+ /** @type {!Map.<!WebInspector.DOMNode, !WebInspector.ElementsTreeUpdater.Record>} */
+ this._recentlyModifiedParentNodes = new Map();
}
WebInspector.ElementsTreeUpdater.prototype = {
@@ -2754,8 +2775,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);
@@ -2763,12 +2784,19 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @param {?WebInspector.DOMNode} parentNode
+ * @param {!WebInspector.ElementsTreeUpdater.Record.ChangeType} changeType
+ * @param {string|!WebInspector.DOMNode=} relatedTarget
*/
- _parentNodeModified: function(parentNode)
+ _parentNodeModified: function(parentNode, changeType, relatedTarget)
{
if (!parentNode)
return;
- this._recentlyModifiedParentNodes.add(parentNode);
+ var record = this._recentlyModifiedParentNodes.get(parentNode);
+ if (!record) {
+ record = new WebInspector.ElementsTreeUpdater.Record();
+ this._recentlyModifiedParentNodes.set(parentNode, record);
+ }
+ record.merge(parentNode, changeType, relatedTarget);
var treeElement = this._treeOutline.findTreeElement(parentNode);
if (treeElement) {
@@ -2776,7 +2804,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
var oldShowInlineText = treeElement._showInlineText();
treeElement._updateHasChildren();
if (treeElement.hasChildren !== oldHasChildren || oldShowInlineText || treeElement._showInlineText())
- this._nodeModified(parentNode);
+ this._nodeModified(parentNode, changeType, relatedTarget);
}
if (this._treeOutline._visible)
@@ -2785,10 +2813,17 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @param {!WebInspector.DOMNode} node
+ * @param {!WebInspector.ElementsTreeUpdater.Record.ChangeType} changeType
+ * @param {string|!WebInspector.DOMNode=} relatedTarget
*/
- _nodeModified: function(node)
+ _nodeModified: function(node, changeType, relatedTarget)
{
- this._recentlyModifiedNodes.add(node);
+ var record = this._recentlyModifiedNodes.get(node);
+ if (!record) {
+ record = new WebInspector.ElementsTreeUpdater.Record();
+ this._recentlyModifiedNodes.set(node, record);
+ }
+ record.merge(node, changeType, relatedTarget);
if (this._treeOutline._visible)
this._updateModifiedNodesSoon();
},
@@ -2811,10 +2846,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, WebInspector.ElementsTreeUpdater.Record.ChangeType.AttrModified, event.data.name);
pfeldman 2014/11/07 12:54:15 Lets inline record creation here.
apavlov 2014/11/10 09:52:56 This will result in one record per single DOM upda
+ },
+
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _attributeRemoved: function(event)
+ {
+ var node = /** @type {!WebInspector.DOMNode} */ (event.data.node);
+ this._nodeModified(node, WebInspector.ElementsTreeUpdater.Record.ChangeType.AttrRemoved, event.data.name);
},
/**
@@ -2823,8 +2867,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, WebInspector.ElementsTreeUpdater.Record.ChangeType.CharDataModified, node);
+ this._nodeModified(node, WebInspector.ElementsTreeUpdater.Record.ChangeType.CharDataModified);
},
/**
@@ -2833,7 +2877,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
_nodeInserted: function(event)
{
var node = /** @type {!WebInspector.DOMNode} */ (event.data);
- this._parentNodeModified(node.parentNode);
+ this._parentNodeModified(node.parentNode, WebInspector.ElementsTreeUpdater.Record.ChangeType.NodeInserted, node);
},
/**
@@ -2844,7 +2888,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, WebInspector.ElementsTreeUpdater.Record.ChangeType.ChildNodeRemoved);
},
/**
@@ -2853,7 +2897,7 @@ WebInspector.ElementsTreeUpdater.prototype = {
_childNodeCountUpdated: function(event)
{
var node = /** @type {!WebInspector.DOMNode} */ (event.data);
- this._parentNodeModified(node);
+ this._parentNodeModified(node, WebInspector.ElementsTreeUpdater.Record.ChangeType.ChildNodeCountUpdated);
pfeldman 2014/11/07 12:54:15 Lets not use this signal.
apavlov 2014/11/10 09:52:56 Done.
},
_updateModifiedNodesSoon: function()
@@ -2870,7 +2914,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;
@@ -2882,18 +2926,19 @@ WebInspector.ElementsTreeUpdater.prototype = {
// Document's children have changed, perform total update.
this._treeOutline.update();
} else {
- var nodes = this._recentlyModifiedNodes.valuesArray();
+ var highlightDOMUpdates = WebInspector.settings.highlightDOMUpdates.get();
+ var nodes = this._recentlyModifiedNodes.keysArray();
for (var i = 0, size = nodes.length; i < size; ++i) {
var nodeItem = this._treeOutline.findTreeElement(nodes[i]);
if (nodeItem)
- nodeItem.updateTitle();
+ nodeItem.updateTitle(false, highlightDOMUpdates ? this._recentlyModifiedNodes.get(nodes[i]) : undefined);
pfeldman 2014/11/07 12:54:15 updateTitle can reach out for it on its own
apavlov 2014/11/10 09:52:56 Done.
}
- var parentNodes = this._recentlyModifiedParentNodes.valuesArray();
+ var parentNodes = this._recentlyModifiedParentNodes.keysArray();
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, highlightDOMUpdates ? this._recentlyModifiedParentNodes.get(parentNodes[i]) : undefined);
pfeldman 2014/11/07 12:54:16 ditto
apavlov 2014/11/10 09:52:56 Done.
}
}
@@ -2921,6 +2966,122 @@ WebInspector.ElementsTreeUpdater.prototype = {
/**
* @constructor
+ */
+WebInspector.ElementsTreeUpdater.Record = function()
+{
+ this._removedAttributeCount = 0;
+}
+
+WebInspector.ElementsTreeUpdater.Record.prototype = {
pfeldman 2014/11/07 12:54:15 Since you merge those, I'd call it UpdateInfo
apavlov 2014/11/10 09:52:56 Done.
+ /**
+ * @param {!WebInspector.DOMNode} node
+ * @param {!WebInspector.ElementsTreeUpdater.Record.ChangeType} changeType
+ * @param {!WebInspector.DOMNode|string|undefined} relatedTarget
+ */
+ merge: function(node, changeType, relatedTarget)
+ {
+ var attrName;
+ switch (changeType) {
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.AttrModified:
+ attrName = /** @type {string} */ (relatedTarget);
+ if (this.removedAttributes && this.removedAttributes.has(attrName)) {
+ this.removedAttributes.delete(attrName);
+ --this._removedAttributeCount;
+ }
+ if (!this.modifiedAttributes)
pfeldman 2014/11/07 12:54:15 make them all private.
apavlov 2014/11/10 09:52:56 Done.
+ this.modifiedAttributes = /** @type {!Set.<string>} */ (new Set());
+ this.modifiedAttributes.add(attrName);
+ break;
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.AttrRemoved:
+ attrName = /** @type {string} */ (relatedTarget);
+ 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;
+ break;
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.NodeInserted:
+ if (!this.insertedNodes)
+ this.insertedNodes = /** @type {!Set.<!WebInspector.DOMNode>} */ (new Set());
+ this.insertedNodes.add(/** @type {!WebInspector.DOMNode} */ (relatedTarget));
+ break;
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.CharDataModified:
+ this._charDataModified = true;
+ break;
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.ChildNodeRemoved:
+ case WebInspector.ElementsTreeUpdater.Record.ChangeType.ChildNodeCountUpdated:
+ this._hasChangedChildren = true;
+ break;
+ default:
+ console.error("Invalid change type: " + changeType);
+ }
+ },
+
+ /**
+ * @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.Record.ChangeType = {
+ AttrModified: 1,
+ AttrRemoved: 2,
+ CharDataModified: 3,
+ NodeInserted: 4,
+ ChildNodeRemoved: 5,
+ ChildNodeCountUpdated: 6
+}
+
+/**
+ * @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