Changeset 201833 in webkit
- Timestamp:
- Jun 8, 2016, 2:17:54 PM (10 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 3 edited
-
ChangeLog (modified) (1 diff)
-
UserInterface/Views/DOMTreeElement.js (modified) (7 diffs)
-
UserInterface/Views/DOMTreeUpdater.js (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r201829 r201833 1 2016-06-08 Brian Burg <bburg@apple.com> 2 3 Web Inspector: reduce redundant attribute modification updates in DOMTreeUpdater and DOMTreeElement 4 https://bugs.webkit.org/show_bug.cgi?id=158504 5 <rdar://problem/25561452> 6 7 Reviewed by Timothy Hatcher. 8 9 When the frontend gets lots of DOM.attributeModified events, it forwards these on to 10 DOMTreeUpdater, which pushes a record for every single modification. It then updates 11 the DOM elements with the attibute changes on an animation frame. However, since it 12 doesn't do any deduplication of the modification records, a lot of time is wasted 13 on updating DOMTreeElements with intermediate (non-final) attribute values. 14 15 This patch rewrites DOMTreeUpdater to precisely track which nodes and attributes 16 of each node need to be updated on the next animation frame. This is done using 17 Sets and Maps that only hold onto the most recent attribute values rather than 18 pushing a record object for every single mutation. 19 20 This improves the performance of the Elements tab on an SVG particle simulator 21 dramatically so that the Inspector will not immediately hang. It still only achieves 22 a few updates per second in this case, so there is still optimization to be done on 23 the frontend and throttling to be done on the backend. 24 25 * UserInterface/Views/DOMTreeElement.js: 26 (WebInspector.DOMTreeElement): 27 (WebInspector.DOMTreeElement.prototype.attributeDidChange): 28 (WebInspector.DOMTreeElement.prototype._buildAttributeDOM): 29 (WebInspector.DOMTreeElement.prototype._markNodeChanged): 30 (WebInspector.DOMTreeElement.prototype._nodeChangedAnimationEnd): 31 (WebInspector.DOMTreeElement.prototype._fireDidChange): 32 (WebInspector.DOMTreeElement.prototype.nodeStateChanged): Deleted. 33 Simplify the list of modified attributes a little bit. This still uses a worklist 34 approach, so it's possible that duplicate updates for the same attribute could accumulate 35 if DOMTreeUpdater pushes updates faster than DOMTreeElement can render them. 36 37 * UserInterface/Views/DOMTreeUpdater.js: 38 (WebInspector.DOMTreeUpdater): 39 (WebInspector.DOMTreeUpdater.prototype._attributesUpdated): 40 (WebInspector.DOMTreeUpdater.prototype._characterDataModified): 41 (WebInspector.DOMTreeUpdater.prototype._nodeAttributeModified): 42 (WebInspector.DOMTreeUpdater.prototype._nodeInserted): 43 (WebInspector.DOMTreeUpdater.prototype._nodeRemoved): 44 (WebInspector.DOMTreeUpdater.prototype._childNodeCountUpdated): 45 (WebInspector.DOMTreeUpdater.prototype._updateModifiedNodes): 46 (WebInspector.DOMTreeUpdater.prototype._reset): 47 Rewrite this class to separately track insertions, deletions, and modifications. Use 48 Sets and Maps so redundant entries are not kept around. Split the main work loop 49 and use fewer enum-like properties to control how each DOM element change is handled. 50 51 Attempt to update all inserted children before modifying their attributes. This 52 wasn't done previously, but enough duplicate attribute modifications occurred that 53 usually some of them would be processed after being added to the tree. There is only 54 one chance to do this now. 55 1 56 2016-06-08 Nikita Vasilyev <nvasilyev@apple.com> 2 57 -
trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js
r200808 r201833 1 1 /* 2 * Copyright (C) 2007, 2008, 2013, 2015 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008, 2013, 2015, 2016 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> 4 4 * Copyright (C) 2009 Joseph Pecoraro … … 43 43 this._expandedChildrenLimit = WebInspector.DOMTreeElement.InitialChildrenLimit; 44 44 45 this._ nodeStateChanges = [];45 this._recentlyModifiedAttributes = []; 46 46 this._boundNodeChangedAnimationEnd = this._nodeChangedAnimationEnd.bind(this); 47 47 … … 198 198 } 199 199 200 nodeStateChanged(change) 201 { 202 if (!change) 203 return; 204 205 this._nodeStateChanges.push(change); 200 attributeDidChange(name) 201 { 202 this._recentlyModifiedAttributes.push({name}); 206 203 } 207 204 … … 1149 1146 attrSpanElement.append("\""); 1150 1147 1151 for (let change of this._nodeStateChanges) {1152 if ( change.type === WebInspector.DOMTreeElement.ChangeType.Attribute && change.attribute === name)1153 change.element = hasText ? attrValueElement : attrNameElement;1148 for (let attribute of this._recentlyModifiedAttributes) { 1149 if (attribute.name === name) 1150 attribute.element = hasText ? attrValueElement : attrNameElement; 1154 1151 } 1155 1152 } … … 1499 1496 _markNodeChanged() 1500 1497 { 1501 for (let change of this._nodeStateChanges) {1502 let element = change.element;1498 for (let attribute of this._recentlyModifiedAttributes) { 1499 let element = attribute.element; 1503 1500 if (!element) 1504 1501 continue; … … 1516 1513 element.removeEventListener("animationend", this._boundNodeChangedAnimationEnd); 1517 1514 1518 for (let i = this._ nodeStateChanges.length - 1; i >= 0; --i) {1519 if (this._ nodeStateChanges[i].element === element)1520 this._ nodeStateChanges.splice(i, 1);1515 for (let i = this._recentlyModifiedAttributes.length - 1; i >= 0; --i) { 1516 if (this._recentlyModifiedAttributes[i].element === element) 1517 this._recentlyModifiedAttributes.splice(i, 1); 1521 1518 } 1522 1519 } … … 1534 1531 super._fireDidChange(); 1535 1532 1536 if (this._nodeStateChanges) 1537 this._markNodeChanged(); 1533 this._markNodeChanged(); 1538 1534 } 1539 1535 -
trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js
r201778 r201833 1 1 /* 2 * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.2 * Copyright (C) 2007, 2008, 2013, 2016 Apple Inc. All rights reserved. 3 3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> 4 4 * Copyright (C) 2009 Joseph Pecoraro … … 40 40 41 41 this._treeOutline = treeOutline; 42 this._recentlyModifiedNodes = []; 42 43 this._recentlyInsertedNodes = new Map; 44 this._recentlyDeletedNodes = new Map; 45 this._recentlyModifiedNodes = new Set; 46 // Map from attribute names to nodes that had the attributes. 47 this._recentlyModifiedAttributes = new Map; 48 49 // Dummy "attribute" that is used to track textContent changes. 50 this._textContentAttributeSymbol = Symbol("text-content-attribute"); 43 51 }; 44 52 … … 56 64 _attributesUpdated: function(event) 57 65 { 58 this._recentlyModifiedNodes.push({node: event.data.node, updated: true, attribute: event.data.name}); 59 if (this._treeOutline._visible) 60 this.onNextFrame._updateModifiedNodes(); 66 let {node, name} = event.data; 67 this._nodeAttributeModified(node, name); 61 68 }, 62 69 63 70 _characterDataModified: function(event) 64 71 { 65 this._recentlyModifiedNodes.push({node: event.data.node, updated: true}); 72 let {node} = event.data; 73 this._nodeAttributeModified(node, this._textContentAttributeSymbol); 74 }, 75 76 _nodeAttributeModified: function(node, attribute) 77 { 78 if (!this._recentlyModifiedAttributes.has(attribute)) 79 this._recentlyModifiedAttributes.set(attribute, new Set); 80 this._recentlyModifiedAttributes.get(attribute).add(node); 81 this._recentlyModifiedNodes.add(node); 82 66 83 if (this._treeOutline._visible) 67 84 this.onNextFrame._updateModifiedNodes(); 68 },85 }, 69 86 70 87 _nodeInserted: function(event) 71 88 { 72 this._recently ModifiedNodes.push({node: event.data.node, parent: event.data.parent, inserted: true});89 this._recentlyInsertedNodes.set(event.data.node, {parent: event.data.parent}); 73 90 if (this._treeOutline._visible) 74 91 this.onNextFrame._updateModifiedNodes(); … … 77 94 _nodeRemoved: function(event) 78 95 { 79 this._recently ModifiedNodes.push({node: event.data.node, parent: event.data.parent, removed: true});96 this._recentlyDeletedNodes.set(event.data.node, {parent: event.data.parent}); 80 97 if (this._treeOutline._visible) 81 98 this.onNextFrame._updateModifiedNodes(); … … 91 108 _updateModifiedNodes: function() 92 109 { 93 let updatedParentTreeElements = []; 94 for (let recentlyModifiedNode of this._recentlyModifiedNodes) { 95 let parent = recentlyModifiedNode.parent; 96 let node = recentlyModifiedNode.node; 97 let changeInfo = null; 98 if (recentlyModifiedNode.attribute) 99 changeInfo = {type: WebInspector.DOMTreeElement.ChangeType.Attribute, attribute: recentlyModifiedNode.attribute}; 110 // Update for insertions and deletions before attribute modifications. This ensures 111 // tree elements get created for newly attached children before we try to update them. 112 let parentElementsToUpdate = new Set; 113 let markNodeParentForUpdate = (value, key, map) => { 114 let parentNode = value.parent; 115 let parentTreeElement = this._treeOutline.findTreeElement(parentNode); 116 if (parentTreeElement) 117 parentElementsToUpdate.add(parentTreeElement); 118 }; 119 this._recentlyInsertedNodes.forEach(markNodeParentForUpdate); 120 this._recentlyDeletedNodes.forEach(markNodeParentForUpdate); 100 121 101 if (recentlyModifiedNode.updated) { 102 let nodeTreeElement = this._treeOutline.findTreeElement(node); 103 if (!nodeTreeElement) 122 for (let parentTreeElement of parentElementsToUpdate) { 123 parentTreeElement.updateTitle(); 124 parentTreeElement.updateChildren(); 125 } 126 127 for (let node of this._recentlyModifiedNodes.values()) { 128 let nodeTreeElement = this._treeOutline.findTreeElement(node); 129 if (!nodeTreeElement) 130 return; 131 132 for (let [attribute, nodes] of this._recentlyModifiedAttributes.entries()) { 133 // Don't report textContent changes as attribute modifications. 134 if (attribute === this._textContentAttributeSymbol) 104 135 continue; 105 136 106 if (changeInfo) 107 nodeTreeElement.nodeStateChanged(changeInfo); 108 109 nodeTreeElement.updateTitle(); 137 if (nodes.has(node)) 138 nodeTreeElement.attributeDidChange(attribute); 110 139 } 111 140 112 if (!parent) 113 continue; 114 115 let parentNodeItem = this._treeOutline.findTreeElement(parent); 116 if (parentNodeItem && !parentNodeItem.alreadyUpdatedChildren) { 117 parentNodeItem.updateTitle(); 118 parentNodeItem.updateChildren(); 119 parentNodeItem.alreadyUpdatedChildren = true; 120 updatedParentTreeElements.push(parentNodeItem); 121 } 141 nodeTreeElement.updateTitle(); 122 142 } 123 143 124 for (let i = 0; i < updatedParentTreeElements.length; ++i)125 updatedParentTreeElements[i].alreadyUpdatedChildren = null;126 127 this._recentlyModified Nodes = [];144 this._recentlyInsertedNodes.clear(); 145 this._recentlyDeletedNodes.clear(); 146 this._recentlyModifiedNodes.clear(); 147 this._recentlyModifiedAttributes.clear(); 128 148 }, 129 149 … … 131 151 { 132 152 WebInspector.domTreeManager.hideDOMNodeHighlight(); 133 this._recentlyModifiedNodes = []; 153 154 this._recentlyInsertedNodes.clear(); 155 this._recentlyDeletedNodes.clear(); 156 this._recentlyModifiedNodes.clear(); 157 this._recentlyModifiedAttributes.clear(); 134 158 } 135 159 };
Note:
See TracChangeset
for help on using the changeset viewer.