Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 55 this.element.addEventListener('scroll', this._onScroll.bind(this), false); | 55 this.element.addEventListener('scroll', this._onScroll.bind(this), false); |
| 56 this.element.addEventListener('copy', this._onCopy.bind(this), false); | 56 this.element.addEventListener('copy', this._onCopy.bind(this), false); |
| 57 this.element.addEventListener('dragstart', this._onDragStart.bind(this), fal se); | 57 this.element.addEventListener('dragstart', this._onDragStart.bind(this), fal se); |
| 58 | 58 |
| 59 this._firstActiveIndex = 0; | 59 this._firstActiveIndex = 0; |
| 60 this._lastActiveIndex = -1; | 60 this._lastActiveIndex = -1; |
| 61 this._renderedItems = []; | 61 this._renderedItems = []; |
| 62 this._anchorSelection = null; | 62 this._anchorSelection = null; |
| 63 this._headSelection = null; | 63 this._headSelection = null; |
| 64 this._itemCount = 0; | 64 this._itemCount = 0; |
| 65 this._cumulativeHeights = new Int32Array(0); | |
| 65 | 66 |
| 66 // Listen for any changes to descendants and trigger a refresh. This ensures | 67 // Listen for any changes to descendants and trigger a refresh. This ensures |
| 67 // that items updated asynchronously will not break stick-to-bottom behavior | 68 // that items updated asynchronously will not break stick-to-bottom behavior |
| 68 // if they change the scroll height. | 69 // if they change the scroll height. |
| 69 this._observer = new MutationObserver(this.refresh.bind(this)); | 70 this._observer = new MutationObserver(this.refresh.bind(this)); |
| 70 this._observerConfig = {childList: true, subtree: true}; | 71 this._observerConfig = {childList: true, subtree: true}; |
| 71 } | 72 } |
| 72 | 73 |
| 73 /** | 74 /** |
| 74 * @return {boolean} | 75 * @return {boolean} |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 } | 114 } |
| 114 | 115 |
| 115 /** | 116 /** |
| 116 * @return {!Element} | 117 * @return {!Element} |
| 117 */ | 118 */ |
| 118 contentElement() { | 119 contentElement() { |
| 119 return this._contentElement; | 120 return this._contentElement; |
| 120 } | 121 } |
| 121 | 122 |
| 122 invalidate() { | 123 invalidate() { |
| 123 delete this._cumulativeHeights; | |
| 124 delete this._cachedProviderElements; | 124 delete this._cachedProviderElements; |
| 125 this._itemCount = this._provider.itemCount(); | 125 this._itemCount = this._provider.itemCount(); |
| 126 this._rebuildCumulativeHeights(); | |
| 126 this.refresh(); | 127 this.refresh(); |
| 127 } | 128 } |
| 128 | 129 |
| 129 /** | 130 /** |
| 130 * @param {number} index | 131 * @param {number} index |
| 131 * @return {?Console.ConsoleViewportElement} | 132 * @return {?Console.ConsoleViewportElement} |
| 132 */ | 133 */ |
| 133 _providerElement(index) { | 134 _providerElement(index) { |
| 134 if (!this._cachedProviderElements) | 135 if (!this._cachedProviderElements) |
| 135 this._cachedProviderElements = new Array(this._itemCount); | 136 this._cachedProviderElements = new Array(this._itemCount); |
| 136 var element = this._cachedProviderElements[index]; | 137 var element = this._cachedProviderElements[index]; |
| 137 if (!element) { | 138 if (!element) { |
| 138 element = this._provider.itemElement(index); | 139 element = this._provider.itemElement(index); |
| 139 this._cachedProviderElements[index] = element; | 140 this._cachedProviderElements[index] = element; |
| 140 } | 141 } |
| 141 return element; | 142 return element; |
| 142 } | 143 } |
| 143 | 144 |
| 144 _rebuildCumulativeHeightsIfNeeded() { | 145 _rebuildCumulativeHeights() { |
| 145 if (this._cumulativeHeights) | 146 if (!this._itemCount) { |
|
dgozman
2017/04/07 20:31:44
I think this special-casing is not needed anymore.
luoe
2017/04/07 22:09:54
Done.
| |
| 147 this._cumulativeHeights = new Int32Array(0); | |
| 146 return; | 148 return; |
| 147 if (!this._itemCount) | 149 } |
| 148 return; | |
| 149 var firstActiveIndex = this._firstActiveIndex; | 150 var firstActiveIndex = this._firstActiveIndex; |
| 150 var lastActiveIndex = this._lastActiveIndex; | 151 var lastActiveIndex = this._lastActiveIndex; |
| 151 var height = 0; | 152 var height = 0; |
| 152 this._cumulativeHeights = new Int32Array(this._itemCount); | 153 this._cumulativeHeights = new Int32Array(this._itemCount); |
| 153 for (var i = 0; i < this._itemCount; ++i) { | 154 for (var i = 0; i < this._itemCount; ++i) { |
| 154 if (firstActiveIndex <= i && i <= lastActiveIndex) | 155 if (firstActiveIndex <= i && i <= lastActiveIndex) |
| 155 height += this._renderedItems[i - firstActiveIndex].element().offsetHeig ht; | 156 height += this._renderedItems[i - firstActiveIndex].element().offsetHeig ht; |
| 156 else | 157 else |
| 157 height += this._provider.fastHeight(i); | 158 height += this._provider.fastHeight(i); |
| 158 this._cumulativeHeights[i] = height; | 159 this._cumulativeHeights[i] = height; |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 317 } | 318 } |
| 318 | 319 |
| 319 var selection = this.element.getComponentSelection(); | 320 var selection = this.element.getComponentSelection(); |
| 320 var shouldRestoreSelection = this._updateSelectionModel(selection); | 321 var shouldRestoreSelection = this._updateSelectionModel(selection); |
| 321 | 322 |
| 322 var visibleFrom = this.element.scrollTop; | 323 var visibleFrom = this.element.scrollTop; |
| 323 var visibleHeight = this._visibleHeight(); | 324 var visibleHeight = this._visibleHeight(); |
| 324 | 325 |
| 325 for (var i = 0; i < this._renderedItems.length; ++i) { | 326 for (var i = 0; i < this._renderedItems.length; ++i) { |
| 326 // Tolerate 1-pixel error due to double-to-integer rounding errors. | 327 // Tolerate 1-pixel error due to double-to-integer rounding errors. |
| 327 if (this._cumulativeHeights && | 328 var cachedItemHeight = this._cachedItemHeight(this._firstActiveIndex + i); |
| 328 Math.abs(this._cachedItemHeight(this._firstActiveIndex + i) - this._re nderedItems[i].element().offsetHeight) > | 329 if (Math.abs(cachedItemHeight - this._renderedItems[i].element().offsetHei ght) > 1) { |
| 329 1) | 330 this._rebuildCumulativeHeights(); |
| 330 delete this._cumulativeHeights; | 331 break; |
| 332 } | |
| 331 } | 333 } |
| 332 this._rebuildCumulativeHeightsIfNeeded(); | |
| 333 var activeHeight = visibleHeight * 2; | 334 var activeHeight = visibleHeight * 2; |
| 334 // When the viewport is scrolled to the bottom, using the cumulative heights estimate is not | 335 // When the viewport is scrolled to the bottom, using the cumulative heights estimate is not |
| 335 // precise enough to determine next visible indices. This stickToBottom chec k avoids extra | 336 // precise enough to determine next visible indices. This stickToBottom chec k avoids extra |
| 336 // calls to refresh in those cases. | 337 // calls to refresh in those cases. |
| 337 if (this._stickToBottom) { | 338 if (this._stickToBottom) { |
| 338 this._firstActiveIndex = | 339 this._firstActiveIndex = |
| 339 Math.max(this._itemCount - Math.ceil(activeHeight / this._provider.min imumRowHeight()), 0); | 340 Math.max(this._itemCount - Math.ceil(activeHeight / this._provider.min imumRowHeight()), 0); |
| 340 this._lastActiveIndex = this._itemCount - 1; | 341 this._lastActiveIndex = this._itemCount - 1; |
| 341 } else { | 342 } else { |
| 342 this._firstActiveIndex = Math.max( | 343 this._firstActiveIndex = Math.max( |
| 343 Array.prototype.lowerBound.call( | 344 Int32Array.prototype.lowerBound.call( |
| 344 this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visible Height) / 2), | 345 this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visible Height) / 2), |
| 345 0); | 346 0); |
| 346 // Proactively render more rows in case some of them will be collapsed wit hout triggering refresh. @see crbug.com/390169 | 347 // Proactively render more rows in case some of them will be collapsed wit hout triggering refresh. @see crbug.com/390169 |
| 347 this._lastActiveIndex = this._firstActiveIndex + Math.ceil(activeHeight / this._provider.minimumRowHeight()) - 1; | 348 this._lastActiveIndex = this._firstActiveIndex + Math.ceil(activeHeight / this._provider.minimumRowHeight()) - 1; |
| 348 this._lastActiveIndex = Math.min(this._lastActiveIndex, this._itemCount - 1); | 349 this._lastActiveIndex = Math.min(this._lastActiveIndex, this._itemCount - 1); |
| 349 } | 350 } |
| 350 | 351 |
| 351 var topGapHeight = this._cumulativeHeights[this._firstActiveIndex - 1] || 0; | 352 var topGapHeight = this._cumulativeHeights[this._firstActiveIndex - 1] || 0; |
| 352 var bottomGapHeight = | 353 var bottomGapHeight = |
| 353 this._cumulativeHeights[this._cumulativeHeights.length - 1] - this._cumu lativeHeights[this._lastActiveIndex]; | 354 this._cumulativeHeights[this._cumulativeHeights.length - 1] - this._cumu lativeHeights[this._lastActiveIndex]; |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 468 */ | 469 */ |
| 469 _onScroll(event) { | 470 _onScroll(event) { |
| 470 this.refresh(); | 471 this.refresh(); |
| 471 } | 472 } |
| 472 | 473 |
| 473 /** | 474 /** |
| 474 * @return {number} | 475 * @return {number} |
| 475 */ | 476 */ |
| 476 firstVisibleIndex() { | 477 firstVisibleIndex() { |
| 477 var firstVisibleIndex = | 478 var firstVisibleIndex = |
| 478 Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, this.e lement.scrollTop + 1), 0); | 479 Math.max(Int32Array.prototype.lowerBound.call(this._cumulativeHeights, t his.element.scrollTop + 1), 0); |
| 479 return Math.max(firstVisibleIndex, this._firstActiveIndex); | 480 return Math.max(firstVisibleIndex, this._firstActiveIndex); |
| 480 } | 481 } |
| 481 | 482 |
| 482 /** | 483 /** |
| 483 * @return {number} | 484 * @return {number} |
| 484 */ | 485 */ |
| 485 lastVisibleIndex() { | 486 lastVisibleIndex() { |
| 486 var lastVisibleIndex; | 487 var lastVisibleIndex; |
| 487 if (this._stickToBottom) { | 488 if (this._stickToBottom) { |
| 488 lastVisibleIndex = this._itemCount - 1; | 489 lastVisibleIndex = this._itemCount - 1; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 519 this.forceScrollItemToBeFirst(index); | 520 this.forceScrollItemToBeFirst(index); |
| 520 else if (index >= lastVisibleIndex) | 521 else if (index >= lastVisibleIndex) |
| 521 this.forceScrollItemToBeLast(index); | 522 this.forceScrollItemToBeLast(index); |
| 522 } | 523 } |
| 523 | 524 |
| 524 /** | 525 /** |
| 525 * @param {number} index | 526 * @param {number} index |
| 526 */ | 527 */ |
| 527 forceScrollItemToBeFirst(index) { | 528 forceScrollItemToBeFirst(index) { |
| 528 this.setStickToBottom(false); | 529 this.setStickToBottom(false); |
| 529 this._rebuildCumulativeHeightsIfNeeded(); | |
| 530 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; | 530 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; |
| 531 if (this.element.isScrolledToBottom()) | 531 if (this.element.isScrolledToBottom()) |
| 532 this.setStickToBottom(true); | 532 this.setStickToBottom(true); |
| 533 this.refresh(); | 533 this.refresh(); |
| 534 } | 534 } |
| 535 | 535 |
| 536 /** | 536 /** |
| 537 * @param {number} index | 537 * @param {number} index |
| 538 */ | 538 */ |
| 539 forceScrollItemToBeLast(index) { | 539 forceScrollItemToBeLast(index) { |
| 540 this.setStickToBottom(false); | 540 this.setStickToBottom(false); |
| 541 this._rebuildCumulativeHeightsIfNeeded(); | |
|
luoe
2017/04/07 01:19:38
By default, CH is an empty typed array if there ar
| |
| 542 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeigh t(); | 541 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeigh t(); |
| 543 if (this.element.isScrolledToBottom()) | 542 if (this.element.isScrolledToBottom()) |
| 544 this.setStickToBottom(true); | 543 this.setStickToBottom(true); |
| 545 this.refresh(); | 544 this.refresh(); |
| 546 } | 545 } |
| 547 | 546 |
| 548 /** | 547 /** |
| 549 * @return {number} | 548 * @return {number} |
| 550 */ | 549 */ |
| 551 _visibleHeight() { | 550 _visibleHeight() { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 598 Console.ConsoleViewportElement.prototype = { | 597 Console.ConsoleViewportElement.prototype = { |
| 599 willHide() {}, | 598 willHide() {}, |
| 600 | 599 |
| 601 wasShown() {}, | 600 wasShown() {}, |
| 602 | 601 |
| 603 /** | 602 /** |
| 604 * @return {!Element} | 603 * @return {!Element} |
| 605 */ | 604 */ |
| 606 element() {}, | 605 element() {}, |
| 607 }; | 606 }; |
| OLD | NEW |