Web Inspector: Flash DOM node attribute on change
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Aug 2015 02:20:02 +0000 (02:20 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 14 Aug 2015 02:20:02 +0000 (02:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147973

Reviewed by Timothy Hatcher.

Whenever an attribute on a DOM node changes, flash the attribute value.
If that value doesn't exist, flash the attribute name instead.

* UserInterface/Views/DOMTreeElement.js:
(WebInspector.DOMTreeElement):
(WebInspector.DOMTreeElement.prototype.nodeChanged):
(WebInspector.DOMTreeElement.prototype._buildAttributeDOM):
If the node has been marked with a general change, mark the attribute element for animation.
(WebInspector.DOMTreeElement.prototype._markNodeChanged.animationEnd):
(WebInspector.DOMTreeElement.prototype._markNodeChanged):
Adds a class to the given element that applies a simple background flash animation.
(WebInspector.DOMTreeElement.prototype._fireDidChange):
Add the animation class once all building of the represented DOM object for that node is done.

* UserInterface/Views/DOMTreeOutline.css:
(@keyframes node-state-changed):
Applies a semi-transparent background that fades to default.
(.node-state-changed):

* UserInterface/Views/DOMTreeUpdater.js:
(WebInspector.DOMTreeUpdater.prototype._attributesUpdated):
Now passes along the name of the modified attribute.
(WebInspector.DOMTreeUpdater.prototype._updateModifiedNodes):
If the modified node object has an attribute member, mark the node as being generally changed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188429 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css
Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js

index 2dab468..79fbd9c 100644 (file)
@@ -1,5 +1,37 @@
 2015-08-13  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Flash DOM node attribute on change
+        https://bugs.webkit.org/show_bug.cgi?id=147973
+
+        Reviewed by Timothy Hatcher.
+
+        Whenever an attribute on a DOM node changes, flash the attribute value.
+        If that value doesn't exist, flash the attribute name instead.
+
+        * UserInterface/Views/DOMTreeElement.js:
+        (WebInspector.DOMTreeElement):
+        (WebInspector.DOMTreeElement.prototype.nodeChanged):
+        (WebInspector.DOMTreeElement.prototype._buildAttributeDOM):
+        If the node has been marked with a general change, mark the attribute element for animation.
+        (WebInspector.DOMTreeElement.prototype._markNodeChanged.animationEnd):
+        (WebInspector.DOMTreeElement.prototype._markNodeChanged):
+        Adds a class to the given element that applies a simple background flash animation.
+        (WebInspector.DOMTreeElement.prototype._fireDidChange):
+        Add the animation class once all building of the represented DOM object for that node is done.
+
+        * UserInterface/Views/DOMTreeOutline.css:
+        (@keyframes node-state-changed):
+        Applies a semi-transparent background that fades to default.
+        (.node-state-changed):
+
+        * UserInterface/Views/DOMTreeUpdater.js:
+        (WebInspector.DOMTreeUpdater.prototype._attributesUpdated):
+        Now passes along the name of the modified attribute.
+        (WebInspector.DOMTreeUpdater.prototype._updateModifiedNodes):
+        If the modified node object has an attribute member, mark the node as being generally changed.
+
+2015-08-13  Devin Rousso  <drousso@apple.com>
+
         REGRESSION (r184000): Web Inspector: Stripped whitespace after editing CSS in Styles sidebar
         https://bugs.webkit.org/show_bug.cgi?id=145679
 
index e8eab6f..0714965 100644 (file)
@@ -41,6 +41,7 @@ WebInspector.DOMTreeElement = class DOMTreeElement extends WebInspector.TreeElem
             this._canAddAttributes = true;
         this._searchQuery = null;
         this._expandedChildrenLimit = WebInspector.DOMTreeElement.InitialChildrenLimit;
+        this._nodeStateChanges = [];
     }
 
     isCloseTag()
@@ -192,6 +193,14 @@ WebInspector.DOMTreeElement = class DOMTreeElement extends WebInspector.TreeElem
         return count;
     }
 
+    nodeStateChanged(change)
+    {
+        if (!change)
+            return;
+
+        this._nodeStateChanges.push(change);
+    }
+
     showChildNode(node)
     {
         console.assert(!this._elementCloseTag);
@@ -1070,41 +1079,43 @@ WebInspector.DOMTreeElement = class DOMTreeElement extends WebInspector.TreeElem
 
     _buildAttributeDOM(parentElement, name, value, node)
     {
-        var hasText = (value.length > 0);
-        var attrSpanElement = parentElement.createChild("span", "html-attribute");
-        var attrNameElement = attrSpanElement.createChild("span", "html-attribute-name");
+        let hasText = (value.length > 0);
+        let attrSpanElement = parentElement.createChild("span", "html-attribute");
+        let attrNameElement = attrSpanElement.createChild("span", "html-attribute-name");
         attrNameElement.textContent = name;
-
+        let attrValueElement = null;
         if (hasText)
             attrSpanElement.append("=\u200B\"");
 
         if (name === "src" || name === "href") {
-            var baseURL = node.ownerDocument ? node.ownerDocument.documentURL : null;
-            var rewrittenURL = absoluteURL(value, baseURL);
-
+            let baseURL = node.ownerDocument ? node.ownerDocument.documentURL : null;
+            let rewrittenURL = absoluteURL(value, baseURL);
             value = value.insertWordBreakCharacters();
-
             if (!rewrittenURL) {
-                var attrValueElement = attrSpanElement.createChild("span", "html-attribute-value");
+                attrValueElement = attrSpanElement.createChild("span", "html-attribute-value");
                 attrValueElement.textContent = value;
             } else {
                 if (value.startsWith("data:"))
                     value = value.trimMiddle(60);
 
-                var linkElement = document.createElement("a");
-                linkElement.href = rewrittenURL;
-                linkElement.textContent = value;
-
-                attrSpanElement.appendChild(linkElement);
+                attrValueElement = document.createElement("a");
+                attrValueElement.href = rewrittenURL;
+                attrValueElement.textContent = value;
+                attrSpanElement.appendChild(attrValueElement);
             }
         } else {
             value = value.insertWordBreakCharacters();
-            var attrValueElement = attrSpanElement.createChild("span", "html-attribute-value");
+            attrValueElement = attrSpanElement.createChild("span", "html-attribute-value");
             attrValueElement.textContent = value;
         }
 
         if (hasText)
             attrSpanElement.append("\"");
+
+        for (let change of this._nodeStateChanges) {
+            if (change.type === WebInspector.DOMTreeElement.ChangeType.Attribute && change.attribute === name)
+                change.element = hasText ? attrValueElement : attrNameElement;
+        }
     }
 
     _buildTagDOM(parentElement, tagName, isClosingTag, isDistinctTreeElement)
@@ -1449,6 +1460,34 @@ WebInspector.DOMTreeElement = class DOMTreeElement extends WebInspector.TreeElem
         WebInspector.highlightRangesWithStyleClass(this.title, matchRanges, WebInspector.DOMTreeElement.SearchHighlightStyleClassName, this._highlightResult);
     }
 
+    _markNodeChanged()
+    {
+        function animationEnd() {
+            this.classList.remove("node-state-changed");
+            this.removeEventListener("animationEnd", animationEnd);
+        }
+
+        for (let change of this._nodeStateChanges) {
+            let element = change.element;
+            if (!element)
+                continue;
+
+            element.classList.remove("node-state-changed");
+            element.addEventListener("animationEnd", animationEnd);
+            element.classList.add("node-state-changed");
+        }
+
+        this._nodeStateChanges = [];
+    }
+
+    _fireDidChange()
+    {
+        super._fireDidChange();
+
+        if (this._nodeStateChanges)
+            this._markNodeChanged();
+    }
+
     handleEvent(event)
     {
         if (event.type === "dragstart" && this._editing)
@@ -1472,5 +1511,9 @@ WebInspector.DOMTreeElement.EditTagBlacklist = [
     "html", "head", "body"
 ].keySet();
 
+WebInspector.DOMTreeElement.ChangeType = {
+    Attribute: "dom-tree-element-change-type-attribute"
+};
+
 WebInspector.DOMTreeElement.SearchHighlightStyleClassName = "search-highlight";
 WebInspector.DOMTreeElement.BouncyHighlightStyleClassName = "bouncy-highlight";
index 6b82b25..3724c98 100644 (file)
     background-color: hsla(53, 83%, 53%, 0.2);
     border-bottom: 1px solid hsl(47, 82%, 60%);
 }
+
+@keyframes node-state-changed {
+    from { background-color: hsla(212, 92%, 54%, 0.5); }
+}
+
+.node-state-changed {
+    animation: node-state-changed 1s cubic-bezier(0, 0, 0.25, 1);
+    border-radius: 3px;
+}
index ad26841..ee5d2b7 100644 (file)
@@ -55,7 +55,7 @@ WebInspector.DOMTreeUpdater.prototype = {
 
     _attributesUpdated: function(event)
     {
-        this._recentlyModifiedNodes.push({node: event.data.node, updated: true});
+        this._recentlyModifiedNodes.push({node: event.data.node, updated: true, attribute: event.data.name});
         if (this._treeOutline._visible)
             this._updateModifiedNodesSoon();
     },
@@ -99,26 +99,32 @@ WebInspector.DOMTreeUpdater.prototype = {
     {
         if (this._updateModifiedNodesTimeout) {
             clearTimeout(this._updateModifiedNodesTimeout);
-            delete this._updateModifiedNodesTimeout;
+            this._updateModifiedNodesTimeout = null;
         }
 
-        var updatedParentTreeElements = [];
+        let updatedParentTreeElements = [];
+        for (let recentlyModifiedNode of this._recentlyModifiedNodes) {
+            let parent = recentlyModifiedNode.parent;
+            let node = recentlyModifiedNode.node;
+            let changeInfo = null;
+            if (recentlyModifiedNode.attribute)
+                changeInfo = {type: WebInspector.DOMTreeElement.ChangeType.Attribute, attribute: recentlyModifiedNode.attribute};
 
-        for (var i = 0; i < this._recentlyModifiedNodes.length; ++i) {
-            var parent = this._recentlyModifiedNodes[i].parent;
-            var node = this._recentlyModifiedNodes[i].node;
+            if (recentlyModifiedNode.updated) {
+                let nodeTreeElement = this._treeOutline.findTreeElement(node);
+                if (!nodeTreeElement)
+                    continue;
 
-            if (this._recentlyModifiedNodes[i].updated) {
-                var nodeItem = this._treeOutline.findTreeElement(node);
-                if (nodeItem)
-                    nodeItem.updateTitle();
-                continue;
+                if (changeInfo)
+                    nodeTreeElement.nodeStateChanged(changeInfo);
+
+                nodeTreeElement.updateTitle();
             }
 
             if (!parent)
                 continue;
 
-            var parentNodeItem = this._treeOutline.findTreeElement(parent);
+            let parentNodeItem = this._treeOutline.findTreeElement(parent);
             if (parentNodeItem && !parentNodeItem.alreadyUpdatedChildren) {
                 parentNodeItem.updateTitle();
                 parentNodeItem.updateChildren();
@@ -127,8 +133,8 @@ WebInspector.DOMTreeUpdater.prototype = {
             }
         }
 
-        for (var i = 0; i < updatedParentTreeElements.length; ++i)
-            delete updatedParentTreeElements[i].alreadyUpdatedChildren;
+        for (let i = 0; i < updatedParentTreeElements.length; ++i)
+            updatedParentTreeElements[i].alreadyUpdatedChildren = null;
 
         this._recentlyModifiedNodes = [];
     },