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

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: fix test 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;
198 this.refresh(); 199 this._itemCount = this._provider.itemCount();
200 this._innerRefresh(false);
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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 else if (this._headSelection.item > this._lastVisibleIndex) 369 else if (this._headSelection.item > this._lastVisibleIndex)
369 headElement = this._bottomGapElement; 370 headElement = this._bottomGapElement;
370 headOffset = this._selectionIsBackward ? 0 : 1; 371 headOffset = this._selectionIsBackward ? 0 : 1;
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 {
379 this._innerRefresh(false);
380 },
381
382 /**
383 * @param {boolean} isUserGesture
384 */
385 _innerRefresh: function(isUserGesture)
386 {
378 if (!this._visibleHeight()) 387 if (!this._visibleHeight())
379 return; // Do nothing for invisible controls. 388 return; // Do nothing for invisible controls.
380 389
381 if (!this._provider.itemCount()) { 390 if (!this._itemCount) {
382 for (var i = 0; i < this._renderedItems.length; ++i) 391 for (var i = 0; i < this._renderedItems.length; ++i)
383 this._renderedItems[i].willHide(); 392 this._renderedItems[i].willHide();
384 this._renderedItems = []; 393 this._renderedItems = [];
385 this._contentElement.removeChildren(); 394 this._contentElement.removeChildren();
386 this._topGapElement.style.height = "0px"; 395 this._topGapElement.style.height = "0px";
387 this._bottomGapElement.style.height = "0px"; 396 this._bottomGapElement.style.height = "0px";
388 this._firstVisibleIndex = -1; 397 this._firstVisibleIndex = -1;
389 this._lastVisibleIndex = -1; 398 this._lastVisibleIndex = -1;
390 return; 399 return;
391 } 400 }
392 401
393 var selection = this.element.getComponentSelection(); 402 var selection = this.element.getComponentSelection();
394 var shouldRestoreSelection = this._updateSelectionModel(selection); 403 var shouldRestoreSelection = this._updateSelectionModel(selection);
395 404
396 var visibleFrom = this.element.scrollTop; 405 var visibleFrom = this.element.scrollTop;
397 var visibleHeight = this._visibleHeight(); 406 var visibleHeight = this._visibleHeight();
398 this._scrolledToBottom = this.element.isScrolledToBottom(); 407 this._scrolledToBottom = this.element.isScrolledToBottom();
399 var isInvalidating = !this._cumulativeHeights; 408 var isInvalidating = !this._cumulativeHeights;
400 409
401 for (var i = 0; i < this._renderedItems.length; ++i) { 410 for (var i = 0; i < this._renderedItems.length; ++i) {
402 // Tolerate 1-pixel error due to double-to-integer rounding errors. 411 // 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) 412 if (this._cumulativeHeights && Math.abs(this._cachedItemHeight(this. _firstVisibleIndex + i) - this._renderedItems[i].element().offsetHeight) > 1)
404 delete this._cumulativeHeights; 413 delete this._cumulativeHeights;
405 } 414 }
406 this._rebuildCumulativeHeightsIfNeeded(); 415 this._rebuildCumulativeHeightsIfNeeded();
407 var itemCount = this._cumulativeHeights.length;
408 var oldFirstVisibleIndex = this._firstVisibleIndex; 416 var oldFirstVisibleIndex = this._firstVisibleIndex;
409 var oldLastVisibleIndex = this._lastVisibleIndex; 417 var oldLastVisibleIndex = this._lastVisibleIndex;
410 418
411 var shouldStickToBottom = isInvalidating && this._stickToBottom && this. _scrolledToBottom; 419 var shouldStickToBottom = !isUserGesture && this._stickToBottom && this. _scrolledToBottom;
412 420
413 if (shouldStickToBottom) { 421 if (shouldStickToBottom) {
414 this._lastVisibleIndex = itemCount - 1; 422 this._lastVisibleIndex = this._itemCount - 1;
415 this._firstVisibleIndex = Math.max(itemCount - Math.ceil(visibleHeig ht / this._provider.minimumRowHeight()), 0); 423 this._firstVisibleIndex = Math.max(this._itemCount - Math.ceil(visib leHeight / this._provider.minimumRowHeight()), 0);
416 } else { 424 } else {
417 this._firstVisibleIndex = Math.max(Array.prototype.lowerBound.call(t his._cumulativeHeights, visibleFrom + 1), 0); 425 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 426 // 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; 427 this._lastVisibleIndex = this._firstVisibleIndex + Math.ceil(visible Height / this._provider.minimumRowHeight()) - 1;
420 this._lastVisibleIndex = Math.min(this._lastVisibleIndex, itemCount - 1); 428 this._lastVisibleIndex = Math.min(this._lastVisibleIndex, this._item Count - 1);
421 } 429 }
422 var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0; 430 var topGapHeight = this._cumulativeHeights[this._firstVisibleIndex - 1] || 0;
423 var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.le ngth - 1] - this._cumulativeHeights[this._lastVisibleIndex]; 431 var bottomGapHeight = this._cumulativeHeights[this._cumulativeHeights.le ngth - 1] - this._cumulativeHeights[this._lastVisibleIndex];
424 432
425 /** 433 /**
426 * @this {WebInspector.ViewportControl} 434 * @this {WebInspector.ViewportControl}
427 */ 435 */
428 function prepare() 436 function prepare()
429 { 437 {
430 this._topGapElement.style.height = topGapHeight + "px"; 438 this._topGapElement.style.height = topGapHeight + "px";
431 this._bottomGapElement.style.height = bottomGapHeight + "px"; 439 this._bottomGapElement.style.height = bottomGapHeight + "px";
432 this._topGapElement._active = !!topGapHeight; 440 this._topGapElement._active = !!topGapHeight;
433 this._bottomGapElement._active = !!bottomGapHeight; 441 this._bottomGapElement._active = !!bottomGapHeight;
434 this._contentElement.style.setProperty("height", "10000000px"); 442 this._contentElement.style.setProperty("height", "10000000px");
435 } 443 }
436 444
437 if (isInvalidating) 445 if (isInvalidating)
438 this._fullViewportUpdate(prepare.bind(this)); 446 this._fullViewportUpdate(prepare.bind(this));
439 else 447 else
440 this._partialViewportUpdate(oldFirstVisibleIndex, oldLastVisibleInde x, prepare.bind(this)); 448 this._partialViewportUpdate(oldFirstVisibleIndex, oldLastVisibleInde x, prepare.bind(this));
441 this._contentElement.style.removeProperty("height"); 449 this._contentElement.style.removeProperty("height");
442 // Should be the last call in the method as it might force layout. 450 // Should be the last call in the method as it might force layout.
443 if (shouldRestoreSelection) 451 if (shouldRestoreSelection)
444 this._restoreSelection(selection); 452 this._restoreSelection(selection);
445 if (shouldStickToBottom) 453 if (shouldStickToBottom)
446 this.element.scrollTop = this.element.scrollHeight; 454 this.element.scrollTop = 10000000;
447 }, 455 },
448 456
449 /** 457 /**
450 * @param {function()} prepare 458 * @param {function()} prepare
451 */ 459 */
452 _fullViewportUpdate: function(prepare) 460 _fullViewportUpdate: function(prepare)
453 { 461 {
454 for (var i = 0; i < this._renderedItems.length; ++i) 462 for (var i = 0; i < this._renderedItems.length; ++i)
455 this._renderedItems[i].willHide(); 463 this._renderedItems[i].willHide();
456 prepare(); 464 prepare();
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
553 while ((node = node.traverseNextTextNode()) && node !== container) 561 while ((node = node.traverseNextTextNode()) && node !== container)
554 chars += node.textContent.length; 562 chars += node.textContent.length;
555 return chars + offset; 563 return chars + offset;
556 }, 564 },
557 565
558 /** 566 /**
559 * @param {!Event} event 567 * @param {!Event} event
560 */ 568 */
561 _onScroll: function(event) 569 _onScroll: function(event)
562 { 570 {
563 this.refresh(); 571 this._innerRefresh(event.isTrusted);
564 }, 572 },
565 573
566 /** 574 /**
567 * @return {number} 575 * @return {number}
568 */ 576 */
569 firstVisibleIndex: function() 577 firstVisibleIndex: function()
570 { 578 {
571 return this._firstVisibleIndex; 579 return this._firstVisibleIndex;
572 }, 580 },
573 581
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 this.forceScrollItemToBeLast(index); 615 this.forceScrollItemToBeLast(index);
608 }, 616 },
609 617
610 /** 618 /**
611 * @param {number} index 619 * @param {number} index
612 */ 620 */
613 forceScrollItemToBeFirst: function(index) 621 forceScrollItemToBeFirst: function(index)
614 { 622 {
615 this._rebuildCumulativeHeightsIfNeeded(); 623 this._rebuildCumulativeHeightsIfNeeded();
616 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; 624 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0;
617 this.refresh(); 625 this._innerRefresh(false);
618 }, 626 },
619 627
620 /** 628 /**
621 * @param {number} index 629 * @param {number} index
622 */ 630 */
623 forceScrollItemToBeLast: function(index) 631 forceScrollItemToBeLast: function(index)
624 { 632 {
625 this._rebuildCumulativeHeightsIfNeeded(); 633 this._rebuildCumulativeHeightsIfNeeded();
626 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleH eight(); 634 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleH eight();
627 this.refresh(); 635 this._innerRefresh(false);
628 }, 636 },
629 637
630 /** 638 /**
631 * @return {number} 639 * @return {number}
632 */ 640 */
633 _visibleHeight: function() 641 _visibleHeight: function()
634 { 642 {
635 // Use offsetHeight instead of clientHeight to avoid being affected by h orizontal scroll. 643 // Use offsetHeight instead of clientHeight to avoid being affected by h orizontal scroll.
636 return this.element.offsetHeight; 644 return this.element.offsetHeight;
637 } 645 }
638 } 646 }
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