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

Unified Diff: chrome/browser/resources/hterm/js/scrollport.js

Issue 8680034: Initial landing of Screen, Terminal, and VT100 classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 9 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/hterm/js/scrollport.js
diff --git a/chrome/browser/resources/hterm/js/scrollport.js b/chrome/browser/resources/hterm/js/scrollport.js
index 73341a4570b128a79054a64cd18e72c4ccfb3cdc..cdf8852efd2dce7dc2c713d9671db475f744fb45 100644
--- a/chrome/browser/resources/hterm/js/scrollport.js
+++ b/chrome/browser/resources/hterm/js/scrollport.js
@@ -19,20 +19,24 @@
* of the selection is off screen. It would be difficult to fix this without
* adding significant overhead to pathologically large selection cases.
*
+ * The RowProvider should return rows rooted by the custom tag name 'x-row'.
+ * This ensures that we can quickly assign the correct display height
+ * to the rows with css.
+ *
* @param {RowProvider} rowProvider An object capable of providing rows as
* raw text or row nodes.
* @param {integer} fontSize The css font-size, in pixels.
* @param {integer} opt_lineHeight Optional css line-height in pixels.
* If omitted it will be computed based on the fontSize.
*/
-function ScrollPort(rowProvider, fontSize, opt_lineHeight) {
+hterm.ScrollPort = function(rowProvider, fontSize, opt_lineHeight) {
PubSub.addBehavior(this);
this.rowProvider_ = rowProvider;
this.fontSize_ = fontSize;
this.rowHeight_ = opt_lineHeight || fontSize + 2;
- this.selection_ = new ScrollPort.Selection(this);
+ this.selection_ = new hterm.ScrollPort.Selection(this);
// A map of rowIndex => rowNode for each row that is drawn as part of a
// pending redraw_() call. Null if there is no pending redraw_ call.
@@ -42,6 +46,9 @@ function ScrollPort(rowProvider, fontSize, opt_lineHeight) {
// previous redraw_() call.
this.previousRowNodeCache_ = {};
+ // The css rule that we use to control the height of a row.
+ this.xrowCssRule_ = null;
+
this.div_ = null;
this.document_ = null;
@@ -54,9 +61,9 @@ function ScrollPort(rowProvider, fontSize, opt_lineHeight) {
* Proxy for the native selection object which understands how to walk up the
* DOM to find the containing row node and sort out which comes first.
*
- * @param {ScrollPort} scrollPort The parent ScrollPort instance.
+ * @param {hterm.ScrollPort} scrollPort The parent hterm.ScrollPort instance.
*/
-ScrollPort.Selection = function(scrollPort) {
+hterm.ScrollPort.Selection = function(scrollPort) {
this.scrollPort_ = scrollPort;
this.selection_ = null;
@@ -101,7 +108,7 @@ ScrollPort.Selection = function(scrollPort) {
* This is a one-way synchronization, the DOM selection is copied to this
* object, not the other way around.
*/
-ScrollPort.Selection.prototype.sync = function() {
+hterm.ScrollPort.Selection.prototype.sync = function() {
var selection = this.scrollPort_.getDocument().getSelection();
this.startRow = null;
@@ -146,9 +153,9 @@ ScrollPort.Selection.prototype.sync = function() {
};
/**
- * Turn a div into this ScrollPort.
+ * Turn a div into this hterm.ScrollPort.
*/
-ScrollPort.prototype.decorate = function(div) {
+hterm.ScrollPort.prototype.decorate = function(div) {
this.div_ = div;
this.iframe_ = div.ownerDocument.createElement('iframe');
@@ -173,6 +180,14 @@ ScrollPort.prototype.decorate = function(div) {
'white-space: pre;' +
'-webkit-user-select: none;');
+ var style = doc.createElement('style');
+ style.textContent = 'x-row {}';
+ doc.head.appendChild(style);
+
+ this.xrowCssRule_ = doc.styleSheets[0].cssRules[0];
+ this.xrowCssRule_.style.display = 'block';
+ this.xrowCssRule_.style.height = this.rowHeight_ + 'px';
+
this.screen_ = doc.createElement('x-screen');
this.screen_.style.cssText = (
'display: block;' +
@@ -229,19 +244,35 @@ ScrollPort.prototype.decorate = function(div) {
this.setRowMetrics(this.fontSize_, this.rowHeight_);
};
-ScrollPort.prototype.getRowHeight = function() {
+hterm.ScrollPort.prototype.getForegroundColor = function() {
+ return this.document_.body.style.color;
+};
+
+hterm.ScrollPort.prototype.setForegroundColor = function(color) {
+ this.document_.body.style.color = color;
+};
+
+hterm.ScrollPort.prototype.getBackgroundColor = function() {
+ return this.document_.body.style.backgroundColor;
+};
+
+hterm.ScrollPort.prototype.setBackgroundColor = function(color) {
+ this.document_.body.style.backgroundColor = color;
+};
+
+hterm.ScrollPort.prototype.getRowHeight = function() {
return this.rowHeight_;
};
-ScrollPort.prototype.getScreenWidth = function() {
+hterm.ScrollPort.prototype.getScreenWidth = function() {
return this.screen_.clientWidth;
};
-ScrollPort.prototype.getScreenWidth = function() {
+hterm.ScrollPort.prototype.getScreenHeight = function() {
return this.screen_.clientHeight;
};
-ScrollPort.prototype.getCharacterWidth = function() {
+hterm.ScrollPort.prototype.getCharacterWidth = function() {
var span = this.document_.createElement('span');
span.textContent = '\xa0'; //  
this.rowNodes_.appendChild(span);
@@ -251,16 +282,16 @@ ScrollPort.prototype.getCharacterWidth = function() {
};
/**
- * Return the document that holds the visible rows of this ScrollPort.
+ * Return the document that holds the visible rows of this hterm.ScrollPort.
*/
-ScrollPort.prototype.getDocument = function() {
+hterm.ScrollPort.prototype.getDocument = function() {
return this.document_;
};
/**
* Clear out any cached rowNodes.
*/
-ScrollPort.prototype.resetCache = function() {
+hterm.ScrollPort.prototype.resetCache = function() {
this.currentRowNodeCache_ = null;
this.previousRowNodeCache_ = {};
};
@@ -271,27 +302,62 @@ ScrollPort.prototype.resetCache = function() {
* This will clear the row cache and cause a redraw.
*
* @param {Object} rowProvider An object capable of providing the rows
- * in this ScrollPort.
+ * in this hterm.ScrollPort.
*/
-ScrollPort.prototype.setRowProvider = function(rowProvider) {
- this.resetCache_();
+hterm.ScrollPort.prototype.setRowProvider = function(rowProvider) {
+ this.resetCache();
this.rowProvider_ = rowProvider;
this.redraw_();
};
/**
- * Set the fontSize and lineHeight of this ScrollPort.
+ * Inform the ScrollPort that a given range of rows is invalid.
+ *
+ * The RowProvider should call this method if the underlying x-row instance
+ * for a given rowIndex is no longer valid.
+ *
+ * Note that this is not necessary when only the *content* of the x-row has
+ * changed. It's only needed when getRowNode(N) would return a different
+ * x-row than it used to.
+ *
+ * If rows in the sepecified range are visible, they will be redrawn.
+ */
+hterm.ScrollPort.prototype.invalidateRowRange = function(start, end) {
+ this.resetCache();
+
+ var node = this.rowNodes_.firstChild;
+ while (node) {
+ if ('rowIndex' in node &&
+ node.rowIndex >= start && node.rowIndex <= end) {
+ var nextSibling = node.nextSibling;
+ this.rowNodes_.removeChild(node);
+
+ var newNode = this.rowProvider_.getRowNode(node.rowIndex);
+
+ this.rowNodes_.insertBefore(newNode, nextSibling);
+ this.previousRowNodeCache_[node.rowIndex] = newNode;
+
+ node = nextSibling;
+ } else {
+ node = node.nextSibling;
+ }
+ }
+};
+
+/**
+ * Set the fontSize and lineHeight of this hterm.ScrollPort.
*
* @param {integer} fontSize The css font-size, in pixels.
* @param {integer} opt_lineHeight Optional css line-height in pixels.
* If omitted it will be computed based on the fontSize.
*/
-ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) {
+hterm.ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) {
this.fontSize_ = fontSize;
this.rowHeight_ = opt_lineHeight || fontSize + 2;
this.screen_.style.fontSize = this.fontSize_ + 'px';
this.screen_.style.lineHeight = this.rowHeight_ + 'px';
+ this.xrowCssRule_.style.height = this.rowHeight_ + 'px';
this.topSelectBag_.style.height = this.rowHeight_ + 'px';
this.bottomSelectBag_.style.height = this.rowHeight_ + 'px';
@@ -310,7 +376,7 @@ ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) {
* Reset dimensions and visible row count to account for a change in the
* dimensions of the 'x-screen'.
*/
-ScrollPort.prototype.resize = function() {
+hterm.ScrollPort.prototype.resize = function() {
var screenWidth = this.screen_.clientWidth;
var screenHeight = this.screen_.clientHeight;
@@ -327,36 +393,43 @@ ScrollPort.prototype.resize = function() {
this.visibleRowTopMargin = screenHeight - visibleRowsHeight;
this.topFold_.style.marginBottom = this.visibleRowTopMargin + 'px';
- // Resize the scroll area to appear as though it contains every row.
- this.scrollArea_.style.height = (this.rowHeight_ *
- this.rowProvider_.getRowCount() +
- this.visibleRowTopMargin + 'px');
-
// Set the dimensions of the visible rows container.
this.rowNodes_.style.width = screenWidth + 'px';
this.rowNodes_.style.height = visibleRowsHeight + 'px';
this.rowNodes_.style.left = this.screen_.offsetLeft + 'px';
var self = this;
- this.publish('resize',
- { scrollPort: this },
- function() { self.redraw_() });
+ this.publish
+ ('resize', { scrollPort: this },
+ function() {
+ var index = self.bottomFold_.previousSibling.rowIndex;
+ self.scrollRowToBottom(index);
+ });
+};
+
+hterm.ScrollPort.prototype.syncScrollHeight = function() {
+ // Resize the scroll area to appear as though it contains every row.
+ this.scrollArea_.style.height = (this.rowHeight_ *
+ this.rowProvider_.getRowCount() +
+ this.visibleRowTopMargin + 'px');
};
/**
- * Redraw the current ScrollPort based on the current scrollbar position.
+ * Redraw the current hterm.ScrollPort based on the current scrollbar position.
*
* When redrawing, we are careful to make sure that the rows that start or end
* the current selection are not touched in any way. Doing so would disturb
* the selection, and cleaning up after that would cause flashes at best and
* incorrect selection at worst. Instead, we modify the DOM around these nodes.
* We even stash the selection start/end outside of the visible area if
- * they are not supposed to be visible in the ScrollPort.
+ * they are not supposed to be visible in the hterm.ScrollPort.
*/
-ScrollPort.prototype.redraw_ = function() {
+hterm.ScrollPort.prototype.redraw_ = function() {
this.resetSelectBags_();
this.selection_.sync();
+ this.syncScrollHeight();
+
this.currentRowNodeCache_ = {};
var topRowIndex = this.getTopRowIndex();
@@ -376,8 +449,8 @@ ScrollPort.prototype.redraw_ = function() {
* Ensure that the nodes above the top fold are as they should be.
*
* If the selection start and/or end nodes are above the visible range
- * of this ScrollPort then the dom will be adjusted so that they appear before
- * the top fold (the first x-fold element, aka this.topFold).
+ * of this hterm.ScrollPort then the dom will be adjusted so that they appear
+ * before the top fold (the first x-fold element, aka this.topFold).
*
* If not, the top fold will be the first element.
*
@@ -385,7 +458,7 @@ ScrollPort.prototype.redraw_ = function() {
* so would clear the current selection. Instead, the rest of the DOM is
* adjusted around them.
*/
-ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
+hterm.ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
if (!this.selection_.startRow ||
this.selection_.startRow.rowIndex >= topRowIndex) {
// Selection is entirely below the top fold, just make sure the fold is
@@ -401,12 +474,12 @@ ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
// Only the startRow is above the fold.
if (this.selection_.startRow.nextSibling != this.topFold_)
this.rowNodes_.insertBefore(this.topFold_,
- this.selection_.startRow.nextSibling);
+ this.selection_.startRow.nextSibling);
} else {
// Both rows are above the fold.
if (this.selection_.endRow.nextSibling != this.topFold_) {
this.rowNodes_.insertBefore(this.topFold_,
- this.selection_.endRow.nextSibling);
+ this.selection_.endRow.nextSibling);
}
// Trim any intermediate lines.
@@ -425,8 +498,8 @@ ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
* Ensure that the nodes below the bottom fold are as they should be.
*
* If the selection start and/or end nodes are below the visible range
- * of this ScrollPort then the dom will be adjusted so that they appear after
- * the bottom fold (the second x-fold element, aka this.bottomFold).
+ * of this hterm.ScrollPort then the dom will be adjusted so that they appear
+ * after the bottom fold (the second x-fold element, aka this.bottomFold).
*
* If not, the bottom fold will be the last element.
*
@@ -434,7 +507,7 @@ ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
* so would clear the current selection. Instead, the rest of the DOM is
* adjusted around them.
*/
-ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) {
+hterm.ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) {
if (!this.selection_.endRow ||
this.selection_.endRow.rowIndex <= bottomRowIndex) {
// Selection is entirely above the bottom fold, just make sure the fold is
@@ -484,7 +557,8 @@ ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) {
* so would clear the current selection. Instead, the rest of the DOM is
* adjusted around them.
*/
-ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) {
+hterm.ScrollPort.prototype.drawVisibleRows_ = function(
+ topRowIndex, bottomRowIndex) {
var self = this;
// Keep removing nodes, starting with currentNode, until we encounter
@@ -511,12 +585,20 @@ ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) {
// The node we're examining during the current iteration.
var node = this.topFold_.nextSibling;
- for (var drawCount = 0; drawCount < this.visibleRowCount; drawCount++) {
+ var targetDrawCount = Math.min(this.visibleRowCount,
+ this.rowProvider_.getRowCount());
+
+ for (var drawCount = 0; drawCount < targetDrawCount; drawCount++) {
var rowIndex = topRowIndex + drawCount;
if (node == bottomFold) {
// We've hit the bottom fold, we need to insert a new row.
var newNode = this.fetchRowNode_(rowIndex);
+ if (!newNode) {
+ console.log("Couldn't fetch row index: " + rowIndex);
+ break;
+ }
+
this.rowNodes_.insertBefore(newNode, node);
continue;
}
@@ -547,6 +629,11 @@ ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) {
// We encountered the start/end of the selection, but we don't want it
// yet. Insert a new row instead.
var newNode = this.fetchRowNode_(rowIndex);
+ if (!newNode) {
+ console.log("Couldn't fetch row index: " + rowIndex);
+ break;
+ }
+
this.rowNodes_.insertBefore(newNode, node);
continue;
}
@@ -554,7 +641,19 @@ ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) {
// There is nothing special about this node, but it's in our way. Replace
// it with the node that should be here.
var newNode = this.fetchRowNode_(rowIndex);
+ if (!newNode) {
+ console.log("Couldn't fetch row index: " + rowIndex);
+ break;
+ }
+
+ if (node == newNode) {
+ node = node.nextSibling;
+ continue;
+ }
+
this.rowNodes_.insertBefore(newNode, node);
+ if (!newNode.nextSibling)
+ debugger;
this.rowNodes_.removeChild(node);
node = newNode.nextSibling;
}
@@ -570,7 +669,7 @@ ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) {
* when that text is otherwise off screen. They are filled out in the
* onCopy_ event.
*/
-ScrollPort.prototype.resetSelectBags_ = function() {
+hterm.ScrollPort.prototype.resetSelectBags_ = function() {
if (this.topSelectBag_.parentNode) {
this.topSelectBag_.textContent = '';
this.topSelectBag_.parentNode.removeChild(this.topSelectBag_);
@@ -590,7 +689,7 @@ ScrollPort.prototype.resetSelectBags_ = function() {
* so that the first node *after* the top fold is always the first visible
* DOM node.
*/
-ScrollPort.prototype.syncRowNodesTop_ = function() {
+hterm.ScrollPort.prototype.syncRowNodesTop_ = function() {
var topMargin = 0;
var node = this.topFold_.previousSibling;
while (node) {
@@ -606,7 +705,7 @@ ScrollPort.prototype.syncRowNodesTop_ = function() {
*
* This method may only be used during a redraw_.
*/
-ScrollPort.prototype.cacheRowNode_ = function(rowNode) {
+hterm.ScrollPort.prototype.cacheRowNode_ = function(rowNode) {
this.currentRowNodeCache_[rowNode.rowIndex] = rowNode;
};
@@ -618,7 +717,7 @@ ScrollPort.prototype.cacheRowNode_ = function(rowNode) {
*
* If a redraw_ is in progress the row will be added to the current cache.
*/
-ScrollPort.prototype.fetchRowNode_ = function(rowIndex) {
+hterm.ScrollPort.prototype.fetchRowNode_ = function(rowIndex) {
var node;
if (this.previousRowNodeCache_ && rowIndex in this.previousRowNodeCache_) {
@@ -636,7 +735,7 @@ ScrollPort.prototype.fetchRowNode_ = function(rowIndex) {
/**
* Select all rows in the viewport.
*/
-ScrollPort.prototype.selectAll = function() {
+hterm.ScrollPort.prototype.selectAll = function() {
var firstRow;
if (this.topFold_.nextSibling.rowIndex != 0) {
@@ -675,17 +774,19 @@ ScrollPort.prototype.selectAll = function() {
/**
* Return the maximum scroll position in pixels.
*/
-ScrollPort.prototype.getScrollMax_ = function(e) {
+hterm.ScrollPort.prototype.getScrollMax_ = function(e) {
return (this.scrollArea_.clientHeight + this.visibleRowTopMargin -
this.screen_.clientHeight);
};
/**
- * Scroll the given rowIndex to the top of the ScrollPort.
+ * Scroll the given rowIndex to the top of the hterm.ScrollPort.
*
* @param {integer} rowIndex Index of the target row.
*/
-ScrollPort.prototype.scrollRowToTop = function(rowIndex) {
+hterm.ScrollPort.prototype.scrollRowToTop = function(rowIndex) {
+ this.syncScrollHeight();
+
var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
var scrollMax = this.getScrollMax_();
@@ -697,11 +798,13 @@ ScrollPort.prototype.scrollRowToTop = function(rowIndex) {
};
/**
- * Scroll the given rowIndex to the bottom of the ScrollPort.
+ * Scroll the given rowIndex to the bottom of the hterm.ScrollPort.
*
* @param {integer} rowIndex Index of the target row.
*/
-ScrollPort.prototype.scrollRowToBottom = function(rowIndex) {
+hterm.ScrollPort.prototype.scrollRowToBottom = function(rowIndex) {
+ this.syncScrollHeight();
+
var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
scrollTop -= (this.visibleRowCount - 1) * this.rowHeight_;
@@ -718,7 +821,7 @@ ScrollPort.prototype.scrollRowToBottom = function(rowIndex) {
* This is based on the scroll position. If a redraw_ is in progress this
* returns the row that *should* be at the top.
*/
-ScrollPort.prototype.getTopRowIndex = function() {
+hterm.ScrollPort.prototype.getTopRowIndex = function() {
return Math.floor(this.screen_.scrollTop / this.rowHeight_);
};
@@ -728,29 +831,30 @@ ScrollPort.prototype.getTopRowIndex = function() {
* This is based on the scroll position. If a redraw_ is in progress this
* returns the row that *should* be at the bottom.
*/
-ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) {
+hterm.ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) {
return topRowIndex + this.visibleRowCount - 1;
};
/**
* Handler for scroll events.
*
- * The onScroll event fires when the user moves the scrollbar associated with
- * this ScrollPort.
+ * The onScroll event fires when scrollArea's scrollTop property changes. This
+ * may be due to the user manually move the scrollbar, or a programmatic change.
*/
-ScrollPort.prototype.onScroll_ = function(e) {
+hterm.ScrollPort.prototype.onScroll_ = function(e) {
this.redraw_();
+ this.publish('scroll', { scrollPort: this });
};
/**
* Handler for scroll-wheel events.
*
* The onScrollWheel event fires when the user moves their scrollwheel over this
- * ScrollPort. Because the frontmost element in the ScrollPort is a fixed
- * position DIV, the scroll wheel does nothing by default. Instead, we have
- * to handle it manually.
+ * hterm.ScrollPort. Because the frontmost element in the hterm.ScrollPort is
+ * a fixed position DIV, the scroll wheel does nothing by default. Instead, we
+ * have to handle it manually.
*/
-ScrollPort.prototype.onScrollWheel_ = function(e) {
+hterm.ScrollPort.prototype.onScrollWheel_ = function(e) {
var top = this.screen_.scrollTop - e.wheelDeltaY;
if (top < 0)
top = 0;
@@ -769,10 +873,8 @@ ScrollPort.prototype.onScrollWheel_ = function(e) {
* The browser will resize us such that the top row stays at the top, but we
* prefer to the bottom row to stay at the bottom.
*/
-ScrollPort.prototype.onResize = function(e) {
- var index = this.bottomFold_.previousSibling.rowIndex;
+hterm.ScrollPort.prototype.onResize = function(e) {
this.resize();
- this.scrollRowToBottom(index);
};
/**
@@ -783,7 +885,7 @@ ScrollPort.prototype.onResize = function(e) {
* if we're missing some of the selected text, and if so populates one or both
* of the "select bags" with the missing text.
*/
-ScrollPort.prototype.onCopy_ = function(e) {
+hterm.ScrollPort.prototype.onCopy_ = function(e) {
this.resetSelectBags_();
this.selection_.sync();

Powered by Google App Engine
This is Rietveld 408576698