Title: [201778] trunk/Source/WebInspectorUI
- Revision
- 201778
- Author
- [email protected]
- Date
- 2016-06-07 17:32:08 -0700 (Tue, 07 Jun 2016)
Log Message
Web Inspector: reduce timer churn when processing many DOM.attributeModified messages
https://bugs.webkit.org/show_bug.cgi?id=158491
<rdar://problem/25561452>
Reviewed by Timothy Hatcher.
When the backend sends thousands of DOM.attributeModified events to the frontend, it
slows to a crawl. This is partly because redundant messages are being sent, and
because the frontend is taking too long to render attribute updates in the elements tab.
This patch is a first step to improve performance by reducing unnecessary work. It
coalesces all attribute state updates to only happen once per animation frame. This
reduces timer churn because we previously used a debouncing timer with interval of 0ms,
and that had to be cleared and restarted on every call. This change also eliminates
forced layouts when updating the selection highlights, since the DOM tree outline has
been reflowed by the time we start updating selections in a requestAnimationFrame callback.
There is still a lot of optimization to be done here, but this reduces the problem
considerably by keeping the event loop clear and making it obvious which selection
update operations are still too expensive.
* UserInterface/Base/Utilities.js:
Add a 'onNextFrame' proxy to Object. It works like debounce, except it coalesces calls
up until the next animation frame rather than a fixed timeout. It also does not extend
the timeout interval for each call.
* UserInterface/Views/DOMTreeUpdater.js:
(WebInspector.DOMTreeUpdater.prototype._attributesUpdated):
(WebInspector.DOMTreeUpdater.prototype._characterDataModified):
(WebInspector.DOMTreeUpdater.prototype._nodeInserted):
(WebInspector.DOMTreeUpdater.prototype._nodeRemoved):
(WebInspector.DOMTreeUpdater.prototype._updateModifiedNodes):
Update on the next frame rather than on a zero delay timeout.
* UserInterface/Views/TreeOutline.js:
(WebInspector.TreeOutline.WebInspector.TreeElement.prototype.didChange):
(WebInspector.TreeOutline.WebInspector.TreeElement.prototype._fireDidChange):
Update on the next frame rather than on a zero delay timeout.
Modified Paths
Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (201777 => 201778)
--- trunk/Source/WebInspectorUI/ChangeLog 2016-06-08 00:06:28 UTC (rev 201777)
+++ trunk/Source/WebInspectorUI/ChangeLog 2016-06-08 00:32:08 UTC (rev 201778)
@@ -1,3 +1,44 @@
+2016-06-07 Brian Burg <[email protected]>
+
+ Web Inspector: reduce timer churn when processing many DOM.attributeModified messages
+ https://bugs.webkit.org/show_bug.cgi?id=158491
+ <rdar://problem/25561452>
+
+ Reviewed by Timothy Hatcher.
+
+ When the backend sends thousands of DOM.attributeModified events to the frontend, it
+ slows to a crawl. This is partly because redundant messages are being sent, and
+ because the frontend is taking too long to render attribute updates in the elements tab.
+
+ This patch is a first step to improve performance by reducing unnecessary work. It
+ coalesces all attribute state updates to only happen once per animation frame. This
+ reduces timer churn because we previously used a debouncing timer with interval of 0ms,
+ and that had to be cleared and restarted on every call. This change also eliminates
+ forced layouts when updating the selection highlights, since the DOM tree outline has
+ been reflowed by the time we start updating selections in a requestAnimationFrame callback.
+
+ There is still a lot of optimization to be done here, but this reduces the problem
+ considerably by keeping the event loop clear and making it obvious which selection
+ update operations are still too expensive.
+
+ * UserInterface/Base/Utilities.js:
+ Add a 'onNextFrame' proxy to Object. It works like debounce, except it coalesces calls
+ up until the next animation frame rather than a fixed timeout. It also does not extend
+ the timeout interval for each call.
+
+ * UserInterface/Views/DOMTreeUpdater.js:
+ (WebInspector.DOMTreeUpdater.prototype._attributesUpdated):
+ (WebInspector.DOMTreeUpdater.prototype._characterDataModified):
+ (WebInspector.DOMTreeUpdater.prototype._nodeInserted):
+ (WebInspector.DOMTreeUpdater.prototype._nodeRemoved):
+ (WebInspector.DOMTreeUpdater.prototype._updateModifiedNodes):
+ Update on the next frame rather than on a zero delay timeout.
+
+ * UserInterface/Views/TreeOutline.js:
+ (WebInspector.TreeOutline.WebInspector.TreeElement.prototype.didChange):
+ (WebInspector.TreeOutline.WebInspector.TreeElement.prototype._fireDidChange):
+ Update on the next frame rather than on a zero delay timeout.
+
2016-06-07 Nikita Vasilyev <[email protected]>
REGRESSION (r158219): Web Inspector: Border under Memory content view is too thick
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (201777 => 201778)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js 2016-06-08 00:06:28 UTC (rev 201777)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js 2016-06-08 00:32:08 UTC (rev 201778)
@@ -1273,6 +1273,38 @@
this[debounceTimeoutSymbol] = undefined;
}
});
+
+ const requestAnimationFrameSymbol = Symbol("peform-on-animation-frame");
+ const requestAnimationFrameProxySymbol = Symbol("perform-on-animation-frame-proxy");
+
+ Object.defineProperty(Object.prototype, "onNextFrame",
+ {
+ get: function()
+ {
+ if (!this[requestAnimationFrameProxySymbol]) {
+ this[requestAnimationFrameProxySymbol] = new Proxy(this, {
+ get(target, property, receiver) {
+ return (...args) => {
+ let original = target[property];
+ console.assert(typeof original === "function");
+
+ if (original[requestAnimationFrameSymbol])
+ return;
+
+ let performWork = () => {
+ original[requestAnimationFrameSymbol] = undefined;
+ original.apply(target, args);
+ };
+
+ original[requestAnimationFrameSymbol] = requestAnimationFrame(performWork);
+ };
+ }
+ });
+ }
+
+ return this[requestAnimationFrameProxySymbol];
+ }
+ });
})();
function appendWebInspectorSourceURL(string)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js (201777 => 201778)
--- trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js 2016-06-08 00:06:28 UTC (rev 201777)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js 2016-06-08 00:32:08 UTC (rev 201778)
@@ -57,28 +57,28 @@
{
this._recentlyModifiedNodes.push({node: event.data.node, updated: true, attribute: event.data.name});
if (this._treeOutline._visible)
- this.soon._updateModifiedNodes();
+ this.onNextFrame._updateModifiedNodes();
},
_characterDataModified: function(event)
{
this._recentlyModifiedNodes.push({node: event.data.node, updated: true});
if (this._treeOutline._visible)
- this.soon._updateModifiedNodes();
+ this.onNextFrame._updateModifiedNodes();
},
_nodeInserted: function(event)
{
this._recentlyModifiedNodes.push({node: event.data.node, parent: event.data.parent, inserted: true});
if (this._treeOutline._visible)
- this.soon._updateModifiedNodes();
+ this.onNextFrame._updateModifiedNodes();
},
_nodeRemoved: function(event)
{
this._recentlyModifiedNodes.push({node: event.data.node, parent: event.data.parent, removed: true});
if (this._treeOutline._visible)
- this.soon._updateModifiedNodes();
+ this.onNextFrame._updateModifiedNodes();
},
_childNodeCountUpdated: function(event)
@@ -90,8 +90,6 @@
_updateModifiedNodes: function()
{
- this._updateModifiedNodes.cancelDebounce();
-
let updatedParentTreeElements = [];
for (let recentlyModifiedNode of this._recentlyModifiedNodes) {
let parent = recentlyModifiedNode.parent;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js (201777 => 201778)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js 2016-06-08 00:06:28 UTC (rev 201777)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js 2016-06-08 00:32:08 UTC (rev 201778)
@@ -800,8 +800,6 @@
_fireDidChange()
{
- this._didChangeTimeoutIdentifier = undefined;
-
if (this.treeOutline)
this.treeOutline._treeElementDidChange(this);
}
@@ -811,9 +809,7 @@
if (!this.treeOutline)
return;
- // Prevent telling the TreeOutline multiple times in a row by delaying it with a timeout.
- if (!this._didChangeTimeoutIdentifier)
- this._didChangeTimeoutIdentifier = setTimeout(this._fireDidChange.bind(this), 0);
+ this.onNextFrame._fireDidChange();
}
_setListItemNodeContent()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes