Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js |
| index 1c057b0091fc9b1669ae0b1d40af2b4dc885b432..cd867e7f4469d3bc464bd9806409426ba64c7bc8 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js |
| @@ -136,6 +136,7 @@ WebInspector.ConsoleView = function() |
| this._prompt.renderAsBlock(); |
| var proxyElement = this._prompt.attach(this._promptElement); |
| proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false); |
| + proxyElement.addEventListener("input", this._promptInput.bind(this), false); |
| this._consoleHistorySetting = WebInspector.settings.createLocalSetting("consoleHistory", []); |
| var historyData = this._consoleHistorySetting.get(); |
| @@ -154,6 +155,11 @@ WebInspector.ConsoleView = function() |
| this._initConsoleMessages(); |
| WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this._executionContextChanged, this); |
| + |
| + this._messagesElement.addEventListener("mousedown", this._updateStickToBottomOnMouseDown.bind(this), false); |
| + this._messagesElement.addEventListener("mouseup", this._updateStickToBottomOnMouseUp.bind(this), false); |
| + this._messagesElement.addEventListener("mouseleave", this._updateStickToBottomOnMouseUp.bind(this), false); |
| + this._messagesElement.addEventListener("wheel", this._updateStickToBottomOnWheel.bind(this), false); |
| } |
| WebInspector.ConsoleView.persistedHistorySize = 300; |
| @@ -344,7 +350,7 @@ WebInspector.ConsoleView.prototype = { |
| restoreScrollPositions: function() |
| { |
| - if (this._viewport.scrolledToBottom()) |
| + if (this._viewport.stickToBottom()) |
| this._immediatelyScrollToBottom(); |
| else |
| WebInspector.Widget.prototype.restoreScrollPositions.call(this); |
| @@ -354,7 +360,7 @@ WebInspector.ConsoleView.prototype = { |
| { |
| this._scheduleViewportRefresh(); |
| this._hidePromptSuggestBox(); |
| - if (this._viewport.scrolledToBottom()) |
| + if (this._viewport.stickToBottom()) |
| this._immediatelyScrollToBottom(); |
| for (var i = 0; i < this._visibleViewMessages.length; ++i) |
| this._visibleViewMessages[i].onResize(); |
| @@ -374,6 +380,10 @@ WebInspector.ConsoleView.prototype = { |
| */ |
| function invalidateViewport() |
| { |
| + if (this._muteViewportUpdates) { |
| + this._maybeDirtyWhileMuted = true; |
| + return Promise.resolve(); |
| + } |
| if (this._needsFullUpdate) { |
| this._updateMessageList(); |
| delete this._needsFullUpdate; |
| @@ -382,12 +392,28 @@ WebInspector.ConsoleView.prototype = { |
| } |
| return Promise.resolve(); |
| } |
| + if (this._muteViewportUpdates) { |
| + this._maybeDirtyWhileMuted = true; |
| + this._scheduleViewportRefreshForTest(true); |
| + return; |
| + } else { |
| + this._scheduleViewportRefreshForTest(false); |
| + } |
| this._viewportThrottler.schedule(invalidateViewport.bind(this)); |
| }, |
| + /** |
| + * @param {boolean} muted |
| + */ |
| + _scheduleViewportRefreshForTest: function(muted) |
| + { |
| + // This functions is sniffed in tests. |
| + }, |
| + |
| _immediatelyScrollToBottom: function() |
| { |
| // This will scroll viewport and trigger its refresh. |
| + this._viewport.setStickToBottom(true); |
| this._promptElement.scrollIntoView(true); |
| }, |
| @@ -744,7 +770,9 @@ WebInspector.ConsoleView.prototype = { |
| _promptKeyDown: function(event) |
| { |
| - if (isEnterKey(event)) { |
| + if (event.key === "PageUp") { |
|
luoe
2016/08/12 18:18:12
Remove this
|
| + this._updateStickToBottomOnWheel(); |
| + } else if (isEnterKey(event)) { |
| this._enterKeyPressed(event); |
| return; |
| } |
| @@ -995,6 +1023,112 @@ WebInspector.ConsoleView.prototype = { |
| highlightNode.scrollIntoViewIfNeeded(); |
| }, |
| + _updateStickToBottomOnMouseDown: function() |
| + { |
| + this._muteViewportUpdates = true; |
| + this._viewport.setStickToBottom(false); |
| + if (this._waitForScrollTimeout) { |
| + clearTimeout(this._waitForScrollTimeout); |
| + delete this._waitForScrollTimeout; |
| + } |
| + }, |
| + |
| + _updateStickToBottomOnMouseUp: function() |
| + { |
| + if (!this._muteViewportUpdates) |
| + return; |
| + |
| + var selectedRange = cloneRange(this._viewport.getVisibleRange()); |
| + var isSelectionInPrompt = selectedRange && selectedRange.startContainer === this._promptElement; |
| + var isMessagesSelected = selectedRange && this._messagesElement.isSelfOrAncestor(selectedRange.startContainer); |
| + |
| + // Delay querying isScrolledToBottom to give time for smooth scroll |
| + // events to arrive. The value for the longest timeout duration is |
| + // retrieved from crbug.com/575409. Force the viewport to stop sticking |
| + // and allow keyboard navigation of the viewport (native behavior) when |
| + // the user selects a new range inside viewport messages. |
| + if (isMessagesSelected && !isSelectionInPrompt && !rangesAreEqual(selectedRange, this._lastSelectedRange)) |
| + this._waitForScrollTimeout = setTimeout(updateViewportState.bind(this, false), 200); |
| + else |
| + this._waitForScrollTimeout = setTimeout(updateViewportState.bind(this), 200); |
| + this._lastSelectedRange = selectedRange; |
| + |
| + /** |
| + * @param {?{startContainer: ?Node, startOffset: number, endContainer: ?Node, endOffset: number, collapsed: boolean}} range1 |
| + * @param {?{startContainer: ?Node, startOffset: number, endContainer: ?Node, endOffset: number, collapsed: boolean}} range2 |
| + * @return {boolean} |
| + */ |
| + function rangesAreEqual(range1, range2) |
| + { |
| + var collapsedOrNull1 = !range1 || range1.collapsed; |
| + var collapsedOrNull2 = !range2 || range2.collapsed; |
| + if (collapsedOrNull1 && collapsedOrNull2) |
| + return true; |
| + else if (collapsedOrNull1 !== collapsedOrNull2) |
| + return false; |
| + return range1.startContainer === range2.startContainer && |
| + range1.startOffset === range2.startOffset && |
| + range1.endContainer === range2.endContainer && |
| + range1.endOffset === range2.endOffset && |
| + range1.collapsed === range2.collapsed; |
| + } |
| + |
| + /** |
| + * @param {?Range} range |
| + * @return {?{startContainer: ?Node, startOffset: number, endContainer: ?Node, endOffset: number, collapsed: boolean}} |
| + */ |
| + function cloneRange(range) |
| + { |
| + // Return a safe copy of Range data just in case the native Range |
| + // has changed without notice after DOM mutation. |
| + if (!range) |
| + return range; |
| + return { |
| + startContainer: range.startContainer, |
| + startOffset: range.startOffset, |
| + endContainer: range.endContainer, |
| + endOffset: range.endOffset, |
| + collapsed: range.collapsed |
| + }; |
| + } |
| + |
| + /** |
| + * @this {!WebInspector.ConsoleView} |
| + * @param {boolean=} shouldStickToBottom |
| + */ |
| + function updateViewportState(shouldStickToBottom) |
| + { |
| + if (typeof shouldStickToBottom === "undefined") |
| + shouldStickToBottom = this._messagesElement.isScrolledToBottom(); |
| + this._viewport.setStickToBottom(shouldStickToBottom); |
| + this._muteViewportUpdates = false; |
| + if (this._maybeDirtyWhileMuted) { |
| + this._scheduleViewportRefresh(); |
| + delete this._maybeDirtyWhileMuted; |
| + } |
| + delete this._waitForScrollTimeout; |
| + this._updateViewportStickinessForTest(); |
| + } |
| + }, |
| + |
| + _updateViewportStickinessForTest: function() |
| + { |
| + // This method is sniffed in tests. |
| + }, |
| + |
| + _updateStickToBottomOnWheel: function() |
| + { |
| + this._updateStickToBottomOnMouseDown(); |
| + this._updateStickToBottomOnMouseUp(); |
| + }, |
| + |
| + _promptInput: function(event) |
| + { |
| + // Scroll to the bottom, except when the prompt is the only visible item. |
| + if (this.itemCount() !== 0 && this._viewport.firstVisibleIndex() !== this.itemCount()) |
| + this._immediatelyScrollToBottom(); |
| + }, |
| + |
| __proto__: WebInspector.VBox.prototype |
| } |