Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js b/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js |
| index 4774a14ae7dce4b0afb8b21e4199ece8f6738f12..f9d3729abeb0cdc995fd85460ea60a3d1568dbdf 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js |
| @@ -59,9 +59,13 @@ WebInspector.ViewportControl = function(provider) |
| this._renderedItems = []; |
| this._anchorSelection = null; |
| this._headSelection = null; |
| - this._stickToBottom = false; |
| - this._scrolledToBottom = true; |
| this._itemCount = 0; |
| + |
| + // Listen for any changes to descendants and trigger a refresh. This ensures |
| + // that items updated asynchronously will not break stick-to-bottom behavior |
| + // if they change the scroll height. |
| + this._observer = new MutationObserver(this.refresh.bind(this)); |
| + this._observerConfig = { childList: true, subtree: true }; |
| } |
| /** |
| @@ -145,9 +149,9 @@ WebInspector.ViewportControl.prototype = { |
| /** |
| * @return {boolean} |
| */ |
| - scrolledToBottom: function() |
| + stickToBottom: function() |
| { |
| - return this._scrolledToBottom; |
| + return this._stickToBottom; |
| }, |
| /** |
| @@ -156,6 +160,10 @@ WebInspector.ViewportControl.prototype = { |
| setStickToBottom: function(value) |
| { |
| this._stickToBottom = value; |
| + if (this._stickToBottom) |
| + this._observer.observe(this._contentElement, this._observerConfig); |
| + else |
| + this._observer.disconnect(); |
| }, |
| /** |
| @@ -376,6 +384,14 @@ WebInspector.ViewportControl.prototype = { |
| refresh: function() |
| { |
| + this._observer.disconnect(); |
| + this._innerRefresh(); |
| + if (this._stickToBottom) |
| + this._observer.observe(this._contentElement, this._observerConfig); |
| + }, |
| + |
| + _innerRefresh: function() |
| + { |
| if (!this._visibleHeight()) |
| return; // Do nothing for invisible controls. |
| @@ -396,7 +412,6 @@ WebInspector.ViewportControl.prototype = { |
| var visibleFrom = this.element.scrollTop; |
| var visibleHeight = this._visibleHeight(); |
| - this._scrolledToBottom = this.element.isScrolledToBottom(); |
| var isInvalidating = !this._cumulativeHeights; |
| for (var i = 0; i < this._renderedItems.length; ++i) { |
| @@ -408,17 +423,19 @@ WebInspector.ViewportControl.prototype = { |
| var oldFirstVisibleIndex = this._firstVisibleIndex; |
| var oldLastVisibleIndex = this._lastVisibleIndex; |
| - var shouldStickToBottom = this._stickToBottom && this._scrolledToBottom; |
| - |
| - if (shouldStickToBottom) { |
| - this._lastVisibleIndex = this._itemCount - 1; |
| + // When the viewport is scrolled to the bottom and the last visible message sits above the |
| + // text prompt, we may overestimate the number of next visible indices when new messages are |
|
lushnikov
2016/08/02 00:10:48
mb underestimate, not overestimate?
luoe
2016/08/02 19:17:15
I think this might depend on the situation. If we
|
| + // added. This stickToBottom check avoids extra calls to refresh() in those cases. |
| + if (this._stickToBottom) { |
| this._firstVisibleIndex = Math.max(this._itemCount - Math.ceil(visibleHeight / this._provider.minimumRowHeight()), 0); |
| + this._lastVisibleIndex = this._itemCount - 1; |
| } else { |
| this._firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, visibleFrom + 1), 0); |
| // Proactively render more rows in case some of them will be collapsed without triggering refresh. @see crbug.com/390169 |
| this._lastVisibleIndex = this._firstVisibleIndex + Math.ceil(visibleHeight / this._provider.minimumRowHeight()) - 1; |
| this._lastVisibleIndex = Math.min(this._lastVisibleIndex, this._itemCount - 1); |
| } |
| + |
| var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0; |
| var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.length - 1] - this._cumulativeHeights[this._lastVisibleIndex]; |
| @@ -442,7 +459,7 @@ WebInspector.ViewportControl.prototype = { |
| // Should be the last call in the method as it might force layout. |
| if (shouldRestoreSelection) |
| this._restoreSelection(selection); |
| - if (shouldStickToBottom) |
| + if (this._stickToBottom) |
| this.element.scrollTop = 10000000; |
| }, |
| @@ -612,8 +629,11 @@ WebInspector.ViewportControl.prototype = { |
| */ |
| forceScrollItemToBeFirst: function(index) |
| { |
| + this.setStickToBottom(false); |
| this._rebuildCumulativeHeightsIfNeeded(); |
| this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; |
| + if (this.element.isScrolledToBottom()) |
| + this.setStickToBottom(true); |
| this.refresh(); |
| }, |
| @@ -622,8 +642,11 @@ WebInspector.ViewportControl.prototype = { |
| */ |
| forceScrollItemToBeLast: function(index) |
| { |
| + this.setStickToBottom(false); |
| this._rebuildCumulativeHeightsIfNeeded(); |
| this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeight(); |
| + if (this.element.isScrolledToBottom()) |
| + this.setStickToBottom(true); |
| this.refresh(); |
| }, |