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 return; | |
147 if (!this._itemCount) | |
148 return; | |
149 var firstActiveIndex = this._firstActiveIndex; | 146 var firstActiveIndex = this._firstActiveIndex; |
150 var lastActiveIndex = this._lastActiveIndex; | 147 var lastActiveIndex = this._lastActiveIndex; |
151 var height = 0; | 148 var height = 0; |
152 this._cumulativeHeights = new Int32Array(this._itemCount); | 149 this._cumulativeHeights = new Int32Array(this._itemCount); |
153 for (var i = 0; i < this._itemCount; ++i) { | 150 for (var i = 0; i < this._itemCount; ++i) { |
154 if (firstActiveIndex <= i && i <= lastActiveIndex) | 151 if (firstActiveIndex <= i && i <= lastActiveIndex) |
155 height += this._renderedItems[i - firstActiveIndex].element().offsetHeig
ht; | 152 height += this._renderedItems[i - firstActiveIndex].element().offsetHeig
ht; |
156 else | 153 else |
157 height += this._provider.fastHeight(i); | 154 height += this._provider.fastHeight(i); |
158 this._cumulativeHeights[i] = height; | 155 this._cumulativeHeights[i] = height; |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 } | 314 } |
318 | 315 |
319 var selection = this.element.getComponentSelection(); | 316 var selection = this.element.getComponentSelection(); |
320 var shouldRestoreSelection = this._updateSelectionModel(selection); | 317 var shouldRestoreSelection = this._updateSelectionModel(selection); |
321 | 318 |
322 var visibleFrom = this.element.scrollTop; | 319 var visibleFrom = this.element.scrollTop; |
323 var visibleHeight = this._visibleHeight(); | 320 var visibleHeight = this._visibleHeight(); |
324 | 321 |
325 for (var i = 0; i < this._renderedItems.length; ++i) { | 322 for (var i = 0; i < this._renderedItems.length; ++i) { |
326 // Tolerate 1-pixel error due to double-to-integer rounding errors. | 323 // Tolerate 1-pixel error due to double-to-integer rounding errors. |
327 if (this._cumulativeHeights && | 324 var cachedItemHeight = this._cachedItemHeight(this._firstActiveIndex + i); |
328 Math.abs(this._cachedItemHeight(this._firstActiveIndex + i) - this._re
nderedItems[i].element().offsetHeight) > | 325 if (Math.abs(cachedItemHeight - this._renderedItems[i].element().offsetHei
ght) > 1) { |
329 1) | 326 this._rebuildCumulativeHeights(); |
330 delete this._cumulativeHeights; | 327 break; |
| 328 } |
331 } | 329 } |
332 this._rebuildCumulativeHeightsIfNeeded(); | |
333 var activeHeight = visibleHeight * 2; | 330 var activeHeight = visibleHeight * 2; |
334 // When the viewport is scrolled to the bottom, using the cumulative heights
estimate is not | 331 // 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 | 332 // precise enough to determine next visible indices. This stickToBottom chec
k avoids extra |
336 // calls to refresh in those cases. | 333 // calls to refresh in those cases. |
337 if (this._stickToBottom) { | 334 if (this._stickToBottom) { |
338 this._firstActiveIndex = | 335 this._firstActiveIndex = |
339 Math.max(this._itemCount - Math.ceil(activeHeight / this._provider.min
imumRowHeight()), 0); | 336 Math.max(this._itemCount - Math.ceil(activeHeight / this._provider.min
imumRowHeight()), 0); |
340 this._lastActiveIndex = this._itemCount - 1; | 337 this._lastActiveIndex = this._itemCount - 1; |
341 } else { | 338 } else { |
342 this._firstActiveIndex = Math.max( | 339 this._firstActiveIndex = Math.max( |
343 Array.prototype.lowerBound.call( | 340 Int32Array.prototype.lowerBound.call( |
344 this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visible
Height) / 2), | 341 this._cumulativeHeights, visibleFrom + 1 - (activeHeight - visible
Height) / 2), |
345 0); | 342 0); |
346 // Proactively render more rows in case some of them will be collapsed wit
hout triggering refresh. @see crbug.com/390169 | 343 // 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; | 344 this._lastActiveIndex = this._firstActiveIndex + Math.ceil(activeHeight /
this._provider.minimumRowHeight()) - 1; |
348 this._lastActiveIndex = Math.min(this._lastActiveIndex, this._itemCount -
1); | 345 this._lastActiveIndex = Math.min(this._lastActiveIndex, this._itemCount -
1); |
349 } | 346 } |
350 | 347 |
351 var topGapHeight = this._cumulativeHeights[this._firstActiveIndex - 1] || 0; | 348 var topGapHeight = this._cumulativeHeights[this._firstActiveIndex - 1] || 0; |
352 var bottomGapHeight = | 349 var bottomGapHeight = |
353 this._cumulativeHeights[this._cumulativeHeights.length - 1] - this._cumu
lativeHeights[this._lastActiveIndex]; | 350 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 */ | 465 */ |
469 _onScroll(event) { | 466 _onScroll(event) { |
470 this.refresh(); | 467 this.refresh(); |
471 } | 468 } |
472 | 469 |
473 /** | 470 /** |
474 * @return {number} | 471 * @return {number} |
475 */ | 472 */ |
476 firstVisibleIndex() { | 473 firstVisibleIndex() { |
477 var firstVisibleIndex = | 474 var firstVisibleIndex = |
478 Math.max(Array.prototype.lowerBound.call(this._cumulativeHeights, this.e
lement.scrollTop + 1), 0); | 475 Math.max(Int32Array.prototype.lowerBound.call(this._cumulativeHeights, t
his.element.scrollTop + 1), 0); |
479 return Math.max(firstVisibleIndex, this._firstActiveIndex); | 476 return Math.max(firstVisibleIndex, this._firstActiveIndex); |
480 } | 477 } |
481 | 478 |
482 /** | 479 /** |
483 * @return {number} | 480 * @return {number} |
484 */ | 481 */ |
485 lastVisibleIndex() { | 482 lastVisibleIndex() { |
486 var lastVisibleIndex; | 483 var lastVisibleIndex; |
487 if (this._stickToBottom) { | 484 if (this._stickToBottom) { |
488 lastVisibleIndex = this._itemCount - 1; | 485 lastVisibleIndex = this._itemCount - 1; |
(...skipping 30 matching lines...) Expand all Loading... |
519 this.forceScrollItemToBeFirst(index); | 516 this.forceScrollItemToBeFirst(index); |
520 else if (index >= lastVisibleIndex) | 517 else if (index >= lastVisibleIndex) |
521 this.forceScrollItemToBeLast(index); | 518 this.forceScrollItemToBeLast(index); |
522 } | 519 } |
523 | 520 |
524 /** | 521 /** |
525 * @param {number} index | 522 * @param {number} index |
526 */ | 523 */ |
527 forceScrollItemToBeFirst(index) { | 524 forceScrollItemToBeFirst(index) { |
528 this.setStickToBottom(false); | 525 this.setStickToBottom(false); |
529 this._rebuildCumulativeHeightsIfNeeded(); | |
530 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; | 526 this.element.scrollTop = index > 0 ? this._cumulativeHeights[index - 1] : 0; |
531 if (this.element.isScrolledToBottom()) | 527 if (this.element.isScrolledToBottom()) |
532 this.setStickToBottom(true); | 528 this.setStickToBottom(true); |
533 this.refresh(); | 529 this.refresh(); |
534 } | 530 } |
535 | 531 |
536 /** | 532 /** |
537 * @param {number} index | 533 * @param {number} index |
538 */ | 534 */ |
539 forceScrollItemToBeLast(index) { | 535 forceScrollItemToBeLast(index) { |
540 this.setStickToBottom(false); | 536 this.setStickToBottom(false); |
541 this._rebuildCumulativeHeightsIfNeeded(); | |
542 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeigh
t(); | 537 this.element.scrollTop = this._cumulativeHeights[index] - this._visibleHeigh
t(); |
543 if (this.element.isScrolledToBottom()) | 538 if (this.element.isScrolledToBottom()) |
544 this.setStickToBottom(true); | 539 this.setStickToBottom(true); |
545 this.refresh(); | 540 this.refresh(); |
546 } | 541 } |
547 | 542 |
548 /** | 543 /** |
549 * @return {number} | 544 * @return {number} |
550 */ | 545 */ |
551 _visibleHeight() { | 546 _visibleHeight() { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
598 Console.ConsoleViewportElement.prototype = { | 593 Console.ConsoleViewportElement.prototype = { |
599 willHide() {}, | 594 willHide() {}, |
600 | 595 |
601 wasShown() {}, | 596 wasShown() {}, |
602 | 597 |
603 /** | 598 /** |
604 * @return {!Element} | 599 * @return {!Element} |
605 */ | 600 */ |
606 element() {}, | 601 element() {}, |
607 }; | 602 }; |
OLD | NEW |