Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/ViewportControl.js

Issue 1863313002: DevTools: [Console] fix console stick-to-bottom behavior in case of multiline messages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/LayoutTests/inspector/console/console-viewport-stick-to-bottom-expected.txt ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 this.element.addEventListener("copy", this._onCopy.bind(this), false); 54 this.element.addEventListener("copy", this._onCopy.bind(this), false);
55 this.element.addEventListener("dragstart", this._onDragStart.bind(this), fal se); 55 this.element.addEventListener("dragstart", this._onDragStart.bind(this), fal se);
56 56
57 this._firstVisibleIndex = 0; 57 this._firstVisibleIndex = 0;
58 this._lastVisibleIndex = -1; 58 this._lastVisibleIndex = -1;
59 this._renderedItems = []; 59 this._renderedItems = [];
60 this._anchorSelection = null; 60 this._anchorSelection = null;
61 this._headSelection = null; 61 this._headSelection = null;
62 this._stickToBottom = false; 62 this._stickToBottom = false;
63 this._scrolledToBottom = true; 63 this._scrolledToBottom = true;
64 this._itemCount = 0;
64 } 65 }
65 66
66 /** 67 /**
67 * @interface 68 * @interface
68 */ 69 */
69 WebInspector.ViewportControl.Provider = function() 70 WebInspector.ViewportControl.Provider = function()
70 { 71 {
71 } 72 }
72 73
73 WebInspector.ViewportControl.Provider.prototype = { 74 WebInspector.ViewportControl.Provider.prototype = {
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 */ 189 */
189 contentElement: function() 190 contentElement: function()
190 { 191 {
191 return this._contentElement; 192 return this._contentElement;
192 }, 193 },
193 194
194 invalidate: function() 195 invalidate: function()
195 { 196 {
196 delete this._cumulativeHeights; 197 delete this._cumulativeHeights;
197 delete this._cachedProviderElements; 198 delete this._cachedProviderElements;
199 this._itemCount = this._provider.itemCount();
198 this.refresh(); 200 this.refresh();
199 }, 201 },
200 202
201 /** 203 /**
202 * @param {number} index 204 * @param {number} index
203 * @return {?WebInspector.ViewportElement} 205 * @return {?WebInspector.ViewportElement}
204 */ 206 */
205 _providerElement: function(index) 207 _providerElement: function(index)
206 { 208 {
207 if (!this._cachedProviderElements) 209 if (!this._cachedProviderElements)
208 this._cachedProviderElements = new Array(this._provider.itemCount()) ; 210 this._cachedProviderElements = new Array(this._itemCount);
209 var element = this._cachedProviderElements[index]; 211 var element = this._cachedProviderElements[index];
210 if (!element) { 212 if (!element) {
211 element = this._provider.itemElement(index); 213 element = this._provider.itemElement(index);
212 this._cachedProviderElements[index] = element; 214 this._cachedProviderElements[index] = element;
213 } 215 }
214 return element; 216 return element;
215 }, 217 },
216 218
217 _rebuildCumulativeHeightsIfNeeded: function() 219 _rebuildCumulativeHeightsIfNeeded: function()
218 { 220 {
219 if (this._cumulativeHeights) 221 if (this._cumulativeHeights)
220 return; 222 return;
221 var itemCount = this._provider.itemCount(); 223 if (!this._itemCount)
222 if (!itemCount)
223 return; 224 return;
224 var firstVisibleIndex = this._firstVisibleIndex; 225 var firstVisibleIndex = this._firstVisibleIndex;
225 var lastVisibleIndex = this._lastVisibleIndex; 226 var lastVisibleIndex = this._lastVisibleIndex;
226 var height = 0; 227 var height = 0;
227 this._cumulativeHeights = new Int32Array(itemCount); 228 this._cumulativeHeights = new Int32Array(this._itemCount);
228 for (var i = 0; i < itemCount; ++i) { 229 for (var i = 0; i < this._itemCount; ++i) {
229 if (firstVisibleIndex <= i && i <= lastVisibleIndex) 230 if (firstVisibleIndex <= i && i <= lastVisibleIndex)
230 height += this._renderedItems[i - firstVisibleIndex].element().o ffsetHeight; 231 height += this._renderedItems[i - firstVisibleIndex].element().o ffsetHeight;
231 else 232 else
232 height += this._provider.fastHeight(i); 233 height += this._provider.fastHeight(i);
233 this._cumulativeHeights[i] = height; 234 this._cumulativeHeights[i] = height;
234 } 235 }
235 }, 236 },
236 237
237 /** 238 /**
238 * @param {number} index 239 * @param {number} index
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 var topOverlap = range.intersectsNode(this._topGapElement) && this._topG apElement._active; 304 var topOverlap = range.intersectsNode(this._topGapElement) && this._topG apElement._active;
304 var bottomOverlap = range.intersectsNode(this._bottomGapElement) && this ._bottomGapElement._active; 305 var bottomOverlap = range.intersectsNode(this._bottomGapElement) && this ._bottomGapElement._active;
305 if (!topOverlap && !bottomOverlap && !hasVisibleSelection) { 306 if (!topOverlap && !bottomOverlap && !hasVisibleSelection) {
306 this._headSelection = null; 307 this._headSelection = null;
307 this._anchorSelection = null; 308 this._anchorSelection = null;
308 return false; 309 return false;
309 } 310 }
310 311
311 if (!this._anchorSelection || !this._headSelection) { 312 if (!this._anchorSelection || !this._headSelection) {
312 this._anchorSelection = this._createSelectionModel(0, this.element, 0); 313 this._anchorSelection = this._createSelectionModel(0, this.element, 0);
313 this._headSelection = this._createSelectionModel(this._provider.item Count() - 1, this.element, this.element.children.length); 314 this._headSelection = this._createSelectionModel(this._itemCount - 1 , this.element, this.element.children.length);
314 this._selectionIsBackward = false; 315 this._selectionIsBackward = false;
315 } 316 }
316 317
317 var isBackward = this._isSelectionBackwards(selection); 318 var isBackward = this._isSelectionBackwards(selection);
318 var startSelection = this._selectionIsBackward ? this._headSelection : t his._anchorSelection; 319 var startSelection = this._selectionIsBackward ? this._headSelection : t his._anchorSelection;
319 var endSelection = this._selectionIsBackward ? this._anchorSelection : t his._headSelection; 320 var endSelection = this._selectionIsBackward ? this._anchorSelection : t his._headSelection;
320 if (topOverlap && bottomOverlap && hasVisibleSelection) { 321 if (topOverlap && bottomOverlap && hasVisibleSelection) {
321 firstSelected = firstSelected.item < startSelection.item ? firstSele cted : startSelection; 322 firstSelected = firstSelected.item < startSelection.item ? firstSele cted : startSelection;
322 lastSelected = lastSelected.item > endSelection.item ? lastSelected : endSelection; 323 lastSelected = lastSelected.item > endSelection.item ? lastSelected : endSelection;
323 } else if (!hasVisibleSelection) { 324 } else if (!hasVisibleSelection) {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 } 372 }
372 373
373 selection.setBaseAndExtent(anchorElement, anchorOffset, headElement, hea dOffset); 374 selection.setBaseAndExtent(anchorElement, anchorOffset, headElement, hea dOffset);
374 }, 375 },
375 376
376 refresh: function() 377 refresh: function()
377 { 378 {
378 if (!this._visibleHeight()) 379 if (!this._visibleHeight())
379 return; // Do nothing for invisible controls. 380 return; // Do nothing for invisible controls.
380 381
381 if (!this._provider.itemCount()) { 382 if (!this._itemCount) {
382 for (var i = 0; i < this._renderedItems.length; ++i) 383 for (var i = 0; i < this._renderedItems.length; ++i)
383 this._renderedItems[i].willHide(); 384 this._renderedItems[i].willHide();
384 this._renderedItems = []; 385 this._renderedItems = [];
385 this._contentElement.removeChildren(); 386 this._contentElement.removeChildren();
386 this._topGapElement.style.height = "0px"; 387 this._topGapElement.style.height = "0px";
387 this._bottomGapElement.style.height = "0px"; 388 this._bottomGapElement.style.height = "0px";
388 this._firstVisibleIndex = -1; 389 this._firstVisibleIndex = -1;
389 this._lastVisibleIndex = -1; 390 this._lastVisibleIndex = -1;
390 return; 391 return;
391 } 392 }
392 393
393 var selection = this.element.getComponentSelection(); 394 var selection = this.element.getComponentSelection();
394 var shouldRestoreSelection = this._updateSelectionModel(selection); 395 var shouldRestoreSelection = this._updateSelectionModel(selection);
395 396
396 var visibleFrom = this.element.scrollTop; 397 var visibleFrom = this.element.scrollTop;
397 var visibleHeight = this._visibleHeight(); 398 var visibleHeight = this._visibleHeight();
398 this._scrolledToBottom = this.element.isScrolledToBottom(); 399 this._scrolledToBottom = this.element.isScrolledToBottom();
399 var isInvalidating = !this._cumulativeHeights; 400 var isInvalidating = !this._cumulativeHeights;
400 401
401 for (var i = 0; i < this._renderedItems.length; ++i) { 402 for (var i = 0; i < this._renderedItems.length; ++i) {
402 // Tolerate 1-pixel error due to double-to-integer rounding errors. 403 // Tolerate 1-pixel error due to double-to-integer rounding errors.
403 if (this._cumulativeHeights && Math.abs(this._cachedItemHeight(this. _firstVisibleIndex + i) - this._renderedItems[i].element().offsetHeight) > 1) 404 if (this._cumulativeHeights && Math.abs(this._cachedItemHeight(this. _firstVisibleIndex + i) - this._renderedItems[i].element().offsetHeight) > 1)
404 delete this._cumulativeHeights; 405 delete this._cumulativeHeights;
405 } 406 }
406 this._rebuildCumulativeHeightsIfNeeded(); 407 this._rebuildCumulativeHeightsIfNeeded();
407 var itemCount = this._cumulativeHeights.length;
408 var oldFirstVisibleIndex = this._firstVisibleIndex; 408 var oldFirstVisibleIndex = this._firstVisibleIndex;
409 var oldLastVisibleIndex = this._lastVisibleIndex; 409 var oldLastVisibleIndex = this._lastVisibleIndex;
410 410
411 var shouldStickToBottom = isInvalidating && this._stickToBottom && this. _scrolledToBottom; 411 var shouldStickToBottom = isInvalidating && this._stickToBottom && this. _scrolledToBottom;
412 412
413 if (shouldStickToBottom) { 413 if (shouldStickToBottom) {
414 this._lastVisibleIndex = itemCount - 1; 414 this._lastVisibleIndex = this._itemCount - 1;
415 this._firstVisibleIndex = Math.max(itemCount - Math.ceil(visibleHeig ht / this._provider.minimumRowHeight()), 0); 415 this._firstVisibleIndex = Math.max(this._itemCount - Math.ceil(visib leHeight / this._provider.minimumRowHeight()), 0);
416 } else { 416 } else {
417 this._firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(t his._cumulativeHeights, visibleFrom + 1), 0); 417 this._firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(t his._cumulativeHeights, visibleFrom + 1), 0);
418 // Proactively render more rows in case some of them will be collaps ed without triggering refresh. @see crbug.com/390169 418 // Proactively render more rows in case some of them will be collaps ed without triggering refresh. @see crbug.com/390169
419 this._lastVisibleIndex = this._firstVisibleIndex + Math.ceil(visible Height / this._provider.minimumRowHeight()) - 1; 419 this._lastVisibleIndex = this._firstVisibleIndex + Math.ceil(visible Height / this._provider.minimumRowHeight()) - 1;
420 this._lastVisibleIndex = Math.min(this._lastVisibleIndex, itemCount - 1); 420 this._lastVisibleIndex = Math.min(this._lastVisibleIndex, this._item Count - 1);
421 } 421 }
422 var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0; 422 var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0;
423 var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.le ngth - 1] - this._cumulativeHeights[this._lastVisibleIndex]; 423 var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.le ngth - 1] - this._cumulativeHeights[this._lastVisibleIndex];
424 424
425 /** 425 /**
426 * @this {WebInspector.ViewportControl} 426 * @this {WebInspector.ViewportControl}
427 */ 427 */
428 function prepare() 428 function prepare()
429 { 429 {
430 this._topGapElement.style.height = topGapHeight + "px"; 430 this._topGapElement.style.height = topGapHeight + "px";
431 this._bottomGapElement.style.height = bottomGapHeight + "px"; 431 this._bottomGapElement.style.height = bottomGapHeight + "px";
432 this._topGapElement._active = !!topGapHeight; 432 this._topGapElement._active = !!topGapHeight;
433 this._bottomGapElement._active = !!bottomGapHeight; 433 this._bottomGapElement._active = !!bottomGapHeight;
434 this._contentElement.style.setProperty("height", "10000000px"); 434 this._contentElement.style.setProperty("height", "10000000px");
435 } 435 }
436 436
437 if (isInvalidating) 437 if (isInvalidating)
438 this._fullViewportUpdate(prepare.bind(this)); 438 this._fullViewportUpdate(prepare.bind(this));
439 else 439 else
440 this._partialViewportUpdate(oldFirstVisibleIndex, oldLastVisibleInde x, prepare.bind(this)); 440 this._partialViewportUpdate(oldFirstVisibleIndex, oldLastVisibleInde x, prepare.bind(this));
441 this._contentElement.style.removeProperty("height"); 441 this._contentElement.style.removeProperty("height");
442 // Should be the last call in the method as it might force layout. 442 // Should be the last call in the method as it might force layout.
443 if (shouldRestoreSelection) 443 if (shouldRestoreSelection)
444 this._restoreSelection(selection); 444 this._restoreSelection(selection);
445 if (shouldStickToBottom) 445 if (this._lastVisibleIndex === this._itemCount - 1 && this._scrolledToBo ttom && this._stickToBottom)
446 this.element.scrollTop = this.element.scrollHeight; 446 this.element.scrollTop = this.element.scrollHeight;
dgozman 2016/04/07 00:40:12 Let's replace scrollHeight with 10000000000.
lushnikov 2016/04/07 00:54:25 Done.
447 }, 447 },
448 448
449 /** 449 /**
450 * @param {function()} prepare 450 * @param {function()} prepare
451 */ 451 */
452 _fullViewportUpdate: function(prepare) 452 _fullViewportUpdate: function(prepare)
453 { 453 {
454 for (var i = 0; i < this._renderedItems.length; ++i) 454 for (var i = 0; i < this._renderedItems.length; ++i)
455 this._renderedItems[i].willHide(); 455 this._renderedItems[i].willHide();
456 prepare(); 456 prepare();
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
629 629
630 /** 630 /**
631 * @return {number} 631 * @return {number}
632 */ 632 */
633 _visibleHeight: function() 633 _visibleHeight: function()
634 { 634 {
635 // Use offsetHeight instead of clientHeight to avoid being affected by h orizontal scroll. 635 // Use offsetHeight instead of clientHeight to avoid being affected by h orizontal scroll.
636 return this.element.offsetHeight; 636 return this.element.offsetHeight;
637 } 637 }
638 } 638 }
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/inspector/console/console-viewport-stick-to-bottom-expected.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698