[SCM] WebKit Debian packaging branch, webkit-1.3, updated. upstream/1.3.7-4207-g178b198

podivilov at chromium.org podivilov at chromium.org
Mon Feb 21 00:37:29 UTC 2011


The following commit has been merged in the webkit-1.3 branch:
commit 8e39c9b23cb4a3464bd8e61a878d8040aea13905
Author: podivilov at chromium.org <podivilov at chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Wed Feb 2 14:08:21 2011 +0000

    2011-02-02  Andrey Adaikin  <aandrey at google.com>
    
            Reviewed by Pavel Feldman.
    
            Web Inspector: Use DIVs instead of TABLE in TextViewer
            https://bugs.webkit.org/show_bug.cgi?id=53299
    
            * inspector/front-end/SourceFrame.js:
            (WebInspector.SourceFrame.prototype._createTextViewer):
            (WebInspector.SourceFrame.prototype._mouseDown):
            * inspector/front-end/TextViewer.js:
            (WebInspector.TextViewer):
            (WebInspector.TextViewer.prototype.set mimeType):
            (WebInspector.TextViewer.prototype.revealLine):
            (WebInspector.TextViewer.prototype.addDecoration):
            (WebInspector.TextViewer.prototype.removeDecoration):
            (WebInspector.TextViewer.prototype.markAndRevealRange):
            (WebInspector.TextViewer.prototype.highlightLine):
            (WebInspector.TextViewer.prototype.clearLineHighlight):
            (WebInspector.TextViewer.prototype.freeCachedElements):
            (WebInspector.TextViewer.prototype._handleKeyDown):
            (WebInspector.TextViewer.prototype.editLine.finishEditing):
            (WebInspector.TextViewer.prototype.editLine):
            (WebInspector.TextViewer.prototype.beginUpdates):
            (WebInspector.TextViewer.prototype.endUpdates):
            (WebInspector.TextViewer.prototype.resize):
            (WebInspector.TextViewer.prototype._textChanged):
            (WebInspector.TextViewer.prototype._updatePanelOffsets):
            (WebInspector.TextViewer.prototype._syncScroll):
            (WebInspector.TextViewer.prototype._syncDecorationsForLine):
            (WebInspector.TextEditorChunkedPanel):
            (WebInspector.TextEditorChunkedPanel.prototype.set syncScrollListener):
            (WebInspector.TextEditorChunkedPanel.prototype.get textModel):
            (WebInspector.TextEditorChunkedPanel.prototype.addDecoration):
            (WebInspector.TextEditorChunkedPanel.prototype.removeDecoration):
            (WebInspector.TextEditorChunkedPanel.prototype.revealLine):
            (WebInspector.TextEditorChunkedPanel.prototype.makeLineAChunk):
            (WebInspector.TextEditorChunkedPanel.prototype.textChanged):
            (WebInspector.TextEditorChunkedPanel.prototype.beginUpdates):
            (WebInspector.TextEditorChunkedPanel.prototype.endUpdates):
            (WebInspector.TextEditorChunkedPanel.prototype.resize):
            (WebInspector.TextEditorChunkedPanel.prototype._scroll):
            (WebInspector.TextEditorChunkedPanel.prototype._scheduleRepaintAll):
            (WebInspector.TextEditorChunkedPanel.prototype._buildChunks):
            (WebInspector.TextEditorChunkedPanel.prototype._repaintAll):
            (WebInspector.TextEditorChunkedPanel.prototype._chunkNumberForLine):
            (WebInspector.TextEditorChunkedPanel.prototype._chunkForLine):
            (WebInspector.TextEditorGutterPanel):
            (WebInspector.TextEditorGutterPanel.prototype.freeCachedElements):
            (WebInspector.TextEditorGutterPanel.prototype._createNewChunk):
            (WebInspector.TextEditorGutterPanel.prototype._expandChunks):
            (WebInspector.TextEditorGutterChunk):
            (WebInspector.TextEditorGutterChunk.prototype.get expanded):
            (WebInspector.TextEditorGutterChunk.prototype.set expanded):
            (WebInspector.TextEditorGutterChunk.prototype.get height):
            (WebInspector.TextEditorGutterChunk.prototype._createRow):
            (WebInspector.TextEditorMainPanel):
            (WebInspector.TextEditorMainPanel.prototype.set syncDecorationsForLine):
            (WebInspector.TextEditorMainPanel.prototype.set mimeType):
            (WebInspector.TextEditorMainPanel.prototype.markAndRevealRange):
            (WebInspector.TextEditorMainPanel.prototype.highlightLine):
            (WebInspector.TextEditorMainPanel.prototype.clearLineHighlight):
            (WebInspector.TextEditorMainPanel.prototype.freeCachedElements):
            (WebInspector.TextEditorMainPanel.prototype._buildChunks):
            (WebInspector.TextEditorMainPanel.prototype._createNewChunk):
            (WebInspector.TextEditorMainPanel.prototype._expandChunks):
            (WebInspector.TextEditorMainPanel.prototype._highlightDataReady):
            (WebInspector.TextEditorMainPanel.prototype._paintLines):
            (WebInspector.TextEditorMainPanel.prototype._paintLine):
            (WebInspector.TextEditorMainPanel.prototype._releaseLinesHighlight):
            (WebInspector.TextEditorMainPanel.prototype._getSelection):
            (WebInspector.TextEditorMainPanel.prototype._restoreSelection):
            (WebInspector.TextEditorMainPanel.prototype._selectionToPosition):
            (WebInspector.TextEditorMainPanel.prototype._positionToSelection):
            (WebInspector.TextEditorMainPanel.prototype._appendTextNode):
            (WebInspector.TextEditorMainPanel.prototype._handleDomUpdates):
            (WebInspector.TextEditorMainChunk):
            (WebInspector.TextEditorMainChunk.prototype.addDecoration):
            (WebInspector.TextEditorMainChunk.prototype.set expanded):
            (WebInspector.TextEditorMainChunk.prototype.get height):
            (WebInspector.TextEditorMainChunk.prototype.getExpandedLineRow):
            (WebInspector.TextEditorMainChunk.prototype._createRow):
            (WebInspector):
            * inspector/front-end/textViewer.css:
            (.text-editor-lines):
            (.text-editor-contents):
            (.text-editor-editable):
            (.webkit-line-decorations):
            (.webkit-line-number):
            (.webkit-execution-line.webkit-line-content):
            (.diff-container .webkit-added-line.webkit-line-content):
            (.diff-container .webkit-removed-line.webkit-line-content):
            (.diff-container .webkit-changed-line.webkit-line-content):
            (.webkit-highlighted-line.webkit-line-content):
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@77375 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 03f44dc..48a0327 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,98 @@
+2011-02-02  Andrey Adaikin  <aandrey at google.com>
+
+        Reviewed by Pavel Feldman.
+
+        Web Inspector: Use DIVs instead of TABLE in TextViewer
+        https://bugs.webkit.org/show_bug.cgi?id=53299
+
+        * inspector/front-end/SourceFrame.js:
+        (WebInspector.SourceFrame.prototype._createTextViewer):
+        (WebInspector.SourceFrame.prototype._mouseDown):
+        * inspector/front-end/TextViewer.js:
+        (WebInspector.TextViewer):
+        (WebInspector.TextViewer.prototype.set mimeType):
+        (WebInspector.TextViewer.prototype.revealLine):
+        (WebInspector.TextViewer.prototype.addDecoration):
+        (WebInspector.TextViewer.prototype.removeDecoration):
+        (WebInspector.TextViewer.prototype.markAndRevealRange):
+        (WebInspector.TextViewer.prototype.highlightLine):
+        (WebInspector.TextViewer.prototype.clearLineHighlight):
+        (WebInspector.TextViewer.prototype.freeCachedElements):
+        (WebInspector.TextViewer.prototype._handleKeyDown):
+        (WebInspector.TextViewer.prototype.editLine.finishEditing):
+        (WebInspector.TextViewer.prototype.editLine):
+        (WebInspector.TextViewer.prototype.beginUpdates):
+        (WebInspector.TextViewer.prototype.endUpdates):
+        (WebInspector.TextViewer.prototype.resize):
+        (WebInspector.TextViewer.prototype._textChanged):
+        (WebInspector.TextViewer.prototype._updatePanelOffsets):
+        (WebInspector.TextViewer.prototype._syncScroll):
+        (WebInspector.TextViewer.prototype._syncDecorationsForLine):
+        (WebInspector.TextEditorChunkedPanel):
+        (WebInspector.TextEditorChunkedPanel.prototype.set syncScrollListener):
+        (WebInspector.TextEditorChunkedPanel.prototype.get textModel):
+        (WebInspector.TextEditorChunkedPanel.prototype.addDecoration):
+        (WebInspector.TextEditorChunkedPanel.prototype.removeDecoration):
+        (WebInspector.TextEditorChunkedPanel.prototype.revealLine):
+        (WebInspector.TextEditorChunkedPanel.prototype.makeLineAChunk):
+        (WebInspector.TextEditorChunkedPanel.prototype.textChanged):
+        (WebInspector.TextEditorChunkedPanel.prototype.beginUpdates):
+        (WebInspector.TextEditorChunkedPanel.prototype.endUpdates):
+        (WebInspector.TextEditorChunkedPanel.prototype.resize):
+        (WebInspector.TextEditorChunkedPanel.prototype._scroll):
+        (WebInspector.TextEditorChunkedPanel.prototype._scheduleRepaintAll):
+        (WebInspector.TextEditorChunkedPanel.prototype._buildChunks):
+        (WebInspector.TextEditorChunkedPanel.prototype._repaintAll):
+        (WebInspector.TextEditorChunkedPanel.prototype._chunkNumberForLine):
+        (WebInspector.TextEditorChunkedPanel.prototype._chunkForLine):
+        (WebInspector.TextEditorGutterPanel):
+        (WebInspector.TextEditorGutterPanel.prototype.freeCachedElements):
+        (WebInspector.TextEditorGutterPanel.prototype._createNewChunk):
+        (WebInspector.TextEditorGutterPanel.prototype._expandChunks):
+        (WebInspector.TextEditorGutterChunk):
+        (WebInspector.TextEditorGutterChunk.prototype.get expanded):
+        (WebInspector.TextEditorGutterChunk.prototype.set expanded):
+        (WebInspector.TextEditorGutterChunk.prototype.get height):
+        (WebInspector.TextEditorGutterChunk.prototype._createRow):
+        (WebInspector.TextEditorMainPanel):
+        (WebInspector.TextEditorMainPanel.prototype.set syncDecorationsForLine):
+        (WebInspector.TextEditorMainPanel.prototype.set mimeType):
+        (WebInspector.TextEditorMainPanel.prototype.markAndRevealRange):
+        (WebInspector.TextEditorMainPanel.prototype.highlightLine):
+        (WebInspector.TextEditorMainPanel.prototype.clearLineHighlight):
+        (WebInspector.TextEditorMainPanel.prototype.freeCachedElements):
+        (WebInspector.TextEditorMainPanel.prototype._buildChunks):
+        (WebInspector.TextEditorMainPanel.prototype._createNewChunk):
+        (WebInspector.TextEditorMainPanel.prototype._expandChunks):
+        (WebInspector.TextEditorMainPanel.prototype._highlightDataReady):
+        (WebInspector.TextEditorMainPanel.prototype._paintLines):
+        (WebInspector.TextEditorMainPanel.prototype._paintLine):
+        (WebInspector.TextEditorMainPanel.prototype._releaseLinesHighlight):
+        (WebInspector.TextEditorMainPanel.prototype._getSelection):
+        (WebInspector.TextEditorMainPanel.prototype._restoreSelection):
+        (WebInspector.TextEditorMainPanel.prototype._selectionToPosition):
+        (WebInspector.TextEditorMainPanel.prototype._positionToSelection):
+        (WebInspector.TextEditorMainPanel.prototype._appendTextNode):
+        (WebInspector.TextEditorMainPanel.prototype._handleDomUpdates):
+        (WebInspector.TextEditorMainChunk):
+        (WebInspector.TextEditorMainChunk.prototype.addDecoration):
+        (WebInspector.TextEditorMainChunk.prototype.set expanded):
+        (WebInspector.TextEditorMainChunk.prototype.get height):
+        (WebInspector.TextEditorMainChunk.prototype.getExpandedLineRow):
+        (WebInspector.TextEditorMainChunk.prototype._createRow):
+        (WebInspector):
+        * inspector/front-end/textViewer.css:
+        (.text-editor-lines):
+        (.text-editor-contents):
+        (.text-editor-editable):
+        (.webkit-line-decorations):
+        (.webkit-line-number):
+        (.webkit-execution-line.webkit-line-content):
+        (.diff-container .webkit-added-line.webkit-line-content):
+        (.diff-container .webkit-removed-line.webkit-line-content):
+        (.diff-container .webkit-changed-line.webkit-line-content):
+        (.webkit-highlighted-line.webkit-line-content):
+
 2011-02-02  Hans Wennborg  <hans at chromium.org>
 
         Reviewed by Jeremy Orlow.
diff --git a/Source/WebCore/inspector/front-end/SourceFrame.js b/Source/WebCore/inspector/front-end/SourceFrame.js
index 821074a..248de5a 100644
--- a/Source/WebCore/inspector/front-end/SourceFrame.js
+++ b/Source/WebCore/inspector/front-end/SourceFrame.js
@@ -208,9 +208,6 @@ WebInspector.SourceFrame.prototype = {
         this._textViewer.endUpdates();
 
         WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, this._breakpointAdded, this);
-
-        if (this._canEditScripts)
-            this._textViewer.editCallback = this._editLine.bind(this);
     },
 
     _setTextViewerDecorations: function()
@@ -526,7 +523,7 @@ WebInspector.SourceFrame.prototype = {
         var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
         if (!target)
             return;
-        var textViewerLineNumber = target.parentElement.lineNumber;
+        var textViewerLineNumber = target.lineNumber;
         var originalLineNumber = this._formatter.formattedLineNumberToOriginalLineNumber(textViewerLineNumber);
 
         var contextMenu = new WebInspector.ContextMenu();
@@ -592,7 +589,7 @@ WebInspector.SourceFrame.prototype = {
         var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
         if (!target)
             return;
-        var originalLineNumber = this._formatter.formattedLineNumberToOriginalLineNumber(target.parentElement.lineNumber);
+        var originalLineNumber = this._formatter.formattedLineNumberToOriginalLineNumber(target.lineNumber);
 
         var breakpoint = this._findBreakpoint(originalLineNumber);
         if (breakpoint) {
@@ -855,11 +852,10 @@ WebInspector.SourceFrame.prototype = {
         if (!Preferences.canEditScriptSource || !this._isScript)
             return;
 
-        var target = event.target.enclosingNodeOrSelfWithNodeName("TD");
-        if (!target || target.parentElement.firstChild === target)
+        var lineRow = event.target.enclosingNodeOrSelfWithClass("webkit-line-content");
+        if (!lineRow)
             return;  // Do not trigger editing from line numbers.
 
-        var lineRow = target.parentElement;
         var lineNumber = lineRow.lineNumber;
         var sourceID = this._sourceIDForLine(lineNumber);
         if (!sourceID)
diff --git a/Source/WebCore/inspector/front-end/TextViewer.js b/Source/WebCore/inspector/front-end/TextViewer.js
index ea36513..853294b 100644
--- a/Source/WebCore/inspector/front-end/TextViewer.js
+++ b/Source/WebCore/inspector/front-end/TextViewer.js
@@ -32,37 +32,21 @@
 WebInspector.TextViewer = function(textModel, platform, url)
 {
     this._textModel = textModel;
-    this._textModel.changeListener = this._buildChunks.bind(this);
-    this._highlighter = new WebInspector.TextEditorHighlighter(this._textModel, this._highlightDataReady.bind(this));
+    this._textModel.changeListener = this._textChanged.bind(this);
 
     this.element = document.createElement("div");
     this.element.className = "text-editor monospace";
-    this.element.tabIndex = 0;
-
-    this.element.addEventListener("scroll", this._scroll.bind(this), false);
-    this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false);
-    this.element.addEventListener("beforecopy", this._beforeCopy.bind(this), false);
-    this.element.addEventListener("copy", this._copy.bind(this), false);
 
-    this._url = url;
-
-    this._linesContainerElement = document.createElement("table");
-    this._linesContainerElement.className = "text-editor-lines";
-    this._linesContainerElement.setAttribute("cellspacing", 0);
-    this._linesContainerElement.setAttribute("cellpadding", 0);
-    this.element.appendChild(this._linesContainerElement);
-
-    this._defaultChunkSize = 50;
-    this._paintCoalescingLevel = 0;
-
-    this.freeCachedElements();
-    this._buildChunks();
+    this._mainPanel = new WebInspector.TextEditorMainPanel(this._textModel, url, this._syncScroll.bind(this), this._syncDecorationsForLine.bind(this));
+    this._gutterPanel = new WebInspector.TextEditorGutterPanel(this._textModel);
+    this.element.appendChild(this._mainPanel.element);
+    this.element.appendChild(this._gutterPanel.element);
 }
 
 WebInspector.TextViewer.prototype = {
     set mimeType(mimeType)
     {
-        this._highlighter.mimeType = mimeType;
+        this._mainPanel.mimeType = mimeType;
     },
 
     get textModel()
@@ -72,85 +56,166 @@ WebInspector.TextViewer.prototype = {
 
     revealLine: function(lineNumber)
     {
-        if (lineNumber >= this._textModel.linesCount)
-            return;
-
-        var chunk = this._makeLineAChunk(lineNumber);
-        chunk.element.scrollIntoViewIfNeeded();
+        this._mainPanel.revealLine(lineNumber);
     },
 
     addDecoration: function(lineNumber, decoration)
     {
-        var chunk = this._makeLineAChunk(lineNumber);
-        chunk.addDecoration(decoration);
+        this._mainPanel.addDecoration(lineNumber, decoration);
+        this._gutterPanel.addDecoration(lineNumber, decoration);
     },
 
     removeDecoration: function(lineNumber, decoration)
     {
-        var chunk = this._makeLineAChunk(lineNumber);
-        chunk.removeDecoration(decoration);
+        this._mainPanel.removeDecoration(lineNumber, decoration);
+        this._gutterPanel.removeDecoration(lineNumber, decoration);
     },
 
     markAndRevealRange: function(range)
     {
-        if (this._rangeToMark) {
-            var markedLine = this._rangeToMark.startLine;
-            this._rangeToMark = null;
-            this._paintLines(markedLine, markedLine + 1);
-        }
-
-        if (range) {
-            this._rangeToMark = range;
-            this.revealLine(range.startLine);
-            this._paintLines(range.startLine, range.startLine + 1);
-            if (this._markedRangeElement)
-                this._markedRangeElement.scrollIntoViewIfNeeded();
-        }
-        delete this._markedRangeElement;
+        this._mainPanel.markAndRevealRange(range);
     },
 
     highlightLine: function(lineNumber)
     {
-        this.clearLineHighlight();
-        this._highlightedLine = lineNumber;
-        this.revealLine(lineNumber);
-        var chunk = this._makeLineAChunk(lineNumber);
-        chunk.addDecoration("webkit-highlighted-line");
+        this._mainPanel.highlightLine(lineNumber);
     },
 
     clearLineHighlight: function()
     {
-        if (typeof this._highlightedLine === "number") {
-            var chunk = this._makeLineAChunk(this._highlightedLine);
-            chunk.removeDecoration("webkit-highlighted-line");
-            delete this._highlightedLine;
-        }
+        this._mainPanel.clearLineHighlight();
     },
 
     freeCachedElements: function()
     {
-        this._cachedSpans = [];
-        this._cachedTextNodes = [];
-        this._cachedRows = [];
+        this._mainPanel.freeCachedElements();
+        this._gutterPanel.freeCachedElements();
+    },
+
+    editLine: function(lineRow, callback)
+    {
+        this._mainPanel.editLine(lineRow, callback);
+    },
+
+    beginUpdates: function()
+    {
+        this._mainPanel.beginUpdates();
+        this._gutterPanel.beginUpdates();
+    },
+
+    endUpdates: function()
+    {
+        this._mainPanel.endUpdates();
+        this._gutterPanel.endUpdates();
+    },
+
+    resize: function()
+    {
+        this._mainPanel.resize();
+        this._gutterPanel.resize();
+        this._updatePanelOffsets();
+    },
+
+    // WebInspector.TextModel listener
+    _textChanged: function(oldRange, newRange, oldText, newText)
+    {
+        this._mainPanel.textChanged();
+        this._gutterPanel.textChanged();
+        this._updatePanelOffsets();
+    },
+
+    _updatePanelOffsets: function()
+    {
+        var lineNumbersWidth = this._gutterPanel.element.offsetWidth;
+        if (lineNumbersWidth)
+            this._mainPanel.element.style.left = (lineNumbersWidth + 2) + "px";
+        else
+            this._mainPanel.element.style.left = ""; // Use default value set in CSS.
+    },
+
+    _syncScroll: function()
+    {
+        // Async call due to performance reasons.
+        setTimeout(function() {
+            var mainElement = this._mainPanel.element;
+            var gutterElement = this._gutterPanel.element;
+
+            // Handle horizontal scroll bar at the bottom of the main panel.
+            if (gutterElement.offsetHeight > mainElement.clientHeight)
+                gutterElement.style.paddingBottom = (gutterElement.offsetHeight - mainElement.clientHeight) + "px";
+            else
+                gutterElement.style.paddingBottom = "";
+
+            gutterElement.scrollTop = mainElement.scrollTop;
+        }.bind(this), 0);
+    },
+
+    _syncDecorationsForLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var mainChunk = this._mainPanel.makeLineAChunk(lineNumber);
+        var gutterChunk = this._gutterPanel.makeLineAChunk(lineNumber);
+        gutterChunk.element.style.height = mainChunk.element.offsetHeight + "px";
+    }
+}
+
+WebInspector.TextEditorChunkedPanel = function(textModel)
+{
+    this._textModel = textModel;
+
+    this._defaultChunkSize = 50;
+    this._paintCoalescingLevel = 0;
+}
+
+WebInspector.TextEditorChunkedPanel.prototype = {
+    get textModel()
+    {
+        return this._textModel;
+    },
+
+    revealLine: function(lineNumber)
+    {
+        if (lineNumber >= this._textModel.linesCount)
+            return;
+
+        var chunk = this.makeLineAChunk(lineNumber);
+        chunk.element.scrollIntoViewIfNeeded();
+    },
+
+    addDecoration: function(lineNumber, decoration)
+    {
+        var chunk = this.makeLineAChunk(lineNumber);
+        chunk.addDecoration(decoration);
+    },
+
+    removeDecoration: function(lineNumber, decoration)
+    {
+        var chunk = this.makeLineAChunk(lineNumber);
+        chunk.removeDecoration(decoration);
+    },
+
+    textChanged: function(oldRange, newRange, oldText, newText)
+    {
+        this._buildChunks();
     },
 
     _buildChunks: function()
     {
-        this._linesContainerElement.removeChildren();
+        this.element.removeChildren();
 
         this._textChunks = [];
         for (var i = 0; i < this._textModel.linesCount; i += this._defaultChunkSize) {
-            var chunk = new WebInspector.TextChunk(this, i, i + this._defaultChunkSize);
+            var chunk = this._createNewChunk(i, i + this._defaultChunkSize);
             this._textChunks.push(chunk);
-            this._linesContainerElement.appendChild(chunk.element);
+            this.element.appendChild(chunk.element);
         }
 
-        this._indexChunks();
-        this._highlighter.reset();
         this._repaintAll();
     },
 
-    _makeLineAChunk: function(lineNumber)
+    makeLineAChunk: function(lineNumber)
     {
         if (!this._textChunks)
             this._buildChunks();
@@ -163,31 +228,30 @@ WebInspector.TextViewer.prototype = {
         var wasExpanded = oldChunk.expanded;
         oldChunk.expanded = false;
 
-        var insertIndex = oldChunk.chunkNumber + 1;
+        var insertIndex = chunkNumber + 1;
 
         // Prefix chunk.
         if (lineNumber > oldChunk.startLine) {
-            var prefixChunk = new WebInspector.TextChunk(this, oldChunk.startLine, lineNumber);
+            var prefixChunk = this._createNewChunk(oldChunk.startLine, lineNumber);
             this._textChunks.splice(insertIndex++, 0, prefixChunk);
-            this._linesContainerElement.insertBefore(prefixChunk.element, oldChunk.element);
+            this.element.insertBefore(prefixChunk.element, oldChunk.element);
         }
 
         // Line chunk.
-        var lineChunk = new WebInspector.TextChunk(this, lineNumber, lineNumber + 1);
+        var lineChunk = this._createNewChunk(lineNumber, lineNumber + 1);
         this._textChunks.splice(insertIndex++, 0, lineChunk);
-        this._linesContainerElement.insertBefore(lineChunk.element, oldChunk.element);
+        this.element.insertBefore(lineChunk.element, oldChunk.element);
 
         // Suffix chunk.
         if (oldChunk.startLine + oldChunk.linesCount > lineNumber + 1) {
-            var suffixChunk = new WebInspector.TextChunk(this, lineNumber + 1, oldChunk.startLine + oldChunk.linesCount);
+            var suffixChunk = this._createNewChunk(lineNumber + 1, oldChunk.startLine + oldChunk.linesCount);
             this._textChunks.splice(insertIndex, 0, suffixChunk);
-            this._linesContainerElement.insertBefore(suffixChunk.element, oldChunk.element);
+            this.element.insertBefore(suffixChunk.element, oldChunk.element);
         }
 
         // Remove enclosing chunk.
-        this._textChunks.splice(oldChunk.chunkNumber, 1);
-        this._linesContainerElement.removeChild(oldChunk.element);
-        this._indexChunks();
+        this._textChunks.splice(chunkNumber, 1);
+        this.element.removeChild(oldChunk.element);
 
         if (wasExpanded) {
             if (prefixChunk)
@@ -200,19 +264,306 @@ WebInspector.TextViewer.prototype = {
         return lineChunk;
     },
 
-    _indexChunks: function()
+    _scroll: function()
     {
-        for (var i = 0; i < this._textChunks.length; ++i)
-            this._textChunks[i].chunkNumber = i;
+        this._scheduleRepaintAll();
+        if (this._syncScrollListener)
+            this._syncScrollListener();
     },
 
-    _scroll: function()
+    _scheduleRepaintAll: function()
     {
-        var scrollTop = this.element.scrollTop;
-        setTimeout(function() {
-            if (scrollTop === this.element.scrollTop)
-                this._repaintAll();
-        }.bind(this), 50);
+        if (this._repaintAllTimer)
+            clearTimeout(this._repaintAllTimer);
+        this._repaintAllTimer = setTimeout(this._repaintAll.bind(this), 50);
+    },
+
+    beginUpdates: function()
+    {
+        this._paintCoalescingLevel++;
+    },
+
+    endUpdates: function()
+    {
+        this._paintCoalescingLevel--;
+        if (!this._paintCoalescingLevel)
+            this._repaintAll();
+    },
+
+    _chunkNumberForLine: function(lineNumber)
+    {
+        for (var i = 0; i < this._textChunks.length; ++i) {
+            var line = this._textChunks[i].startLine;
+            if (lineNumber >= line && lineNumber < line + this._textChunks[i].linesCount)
+                return i;
+        }
+        return this._textChunks.length - 1;
+    },
+
+    _chunkForLine: function(lineNumber)
+    {
+        return this._textChunks[this._chunkNumberForLine(lineNumber)];
+    },
+
+    _repaintAll: function()
+    {
+        delete this._repaintAllTimer;
+
+        if (this._paintCoalescingLevel)
+            return;
+
+        if (!this._textChunks)
+            this._buildChunks();
+
+        var visibleFrom = this.element.scrollTop;
+        var visibleTo = this.element.scrollTop + this.element.clientHeight;
+
+        var offset = 0;
+        var fromIndex = -1;
+        var toIndex = 0;
+        for (var i = 0; i < this._textChunks.length; ++i) {
+            var chunk = this._textChunks[i];
+            var chunkHeight = chunk.height;
+            if (offset + chunkHeight > visibleFrom && offset < visibleTo) {
+                if (fromIndex === -1)
+                    fromIndex = i;
+                toIndex = i + 1;
+            } else {
+                if (offset >= visibleTo)
+                    break;
+            }
+            offset += chunkHeight;
+        }
+
+        if (toIndex)
+            this._expandChunks(fromIndex, toIndex);
+    },
+    
+    resize: function()
+    {
+        this._scheduleRepaintAll();
+    }
+}
+
+WebInspector.TextEditorGutterPanel = function(textModel)
+{
+    WebInspector.TextEditorChunkedPanel.call(this, textModel);
+
+    this.element = document.createElement("div");
+    this.element.className = "text-editor-lines";
+
+    this.element.addEventListener("scroll", this._scroll.bind(this), false);
+
+    this.freeCachedElements();
+    this._buildChunks();
+}
+
+WebInspector.TextEditorGutterPanel.prototype = {
+    freeCachedElements: function()
+    {
+        this._cachedRows = [];
+    },
+
+    _createNewChunk: function(startLine, endLine)
+    {
+        return new WebInspector.TextEditorGutterChunk(this, startLine, endLine);
+    },
+
+    _expandChunks: function(fromIndex, toIndex)
+    {
+        for (var i = 0; i < this._textChunks.length; ++i) {
+            this._textChunks[i].expanded = (fromIndex <= i && i < toIndex);
+        }
+    }
+}
+
+WebInspector.TextEditorGutterPanel.prototype.__proto__ = WebInspector.TextEditorChunkedPanel.prototype;
+
+WebInspector.TextEditorGutterChunk = function(textViewer, startLine, endLine)
+{
+    this._textViewer = textViewer;
+    this._textModel = textViewer._textModel;
+
+    this.startLine = startLine;
+    endLine = Math.min(this._textModel.linesCount, endLine);
+    this.linesCount = endLine - startLine;
+
+    this._expanded = false;
+
+    this.element = document.createElement("div");
+    this.element.lineNumber = startLine;
+    this.element.className = "webkit-line-number";
+
+    if (this.linesCount === 1) {
+        // Single line chunks are typically created for decorations. Host line number in
+        // the sub-element in order to allow flexible border / margin management.
+        var innerSpan = document.createElement("span");
+        innerSpan.className = "webkit-line-number-inner";
+        innerSpan.textContent = startLine + 1;
+        var outerSpan = document.createElement("div");
+        outerSpan.className = "webkit-line-number-outer";
+        outerSpan.appendChild(innerSpan);
+        this.element.appendChild(outerSpan);
+    } else {
+        var lineNumbers = [];
+        for (var i = startLine; i < endLine; ++i) {
+            lineNumbers.push(i + 1);
+        }
+        this.element.textContent = lineNumbers.join("\n");
+    }
+}
+
+WebInspector.TextEditorGutterChunk.prototype = {
+    addDecoration: function(decoration)
+    {
+        if (typeof decoration === "string") {
+            this.element.addStyleClass(decoration);
+        }
+    },
+
+    removeDecoration: function(decoration)
+    {
+        if (typeof decoration === "string") {
+            this.element.removeStyleClass(decoration);
+        }
+    },
+
+    get expanded()
+    {
+        return this._expanded;
+    },
+
+    set expanded(expanded)
+    {
+        if (this._expanded === expanded)
+            return;
+
+        this._expanded = expanded;
+
+        if (this.linesCount === 1) {
+            return;
+        }
+
+        if (expanded) {
+            this._expandedLineRows = [];
+            var parentElement = this.element.parentElement;
+            for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
+                var lineRow = this._createRow(i);
+                parentElement.insertBefore(lineRow, this.element);
+                this._expandedLineRows.push(lineRow);
+            }
+            parentElement.removeChild(this.element);
+        } else {
+            var elementInserted = false;
+            for (var i = 0; i < this._expandedLineRows.length; ++i) {
+                var lineRow = this._expandedLineRows[i];
+                var parentElement = lineRow.parentElement;
+                if (parentElement) {
+                    if (!elementInserted) {
+                        elementInserted = true;
+                        parentElement.insertBefore(this.element, lineRow);
+                    }
+                    this._textViewer._cachedRows.push(lineRow);
+                    parentElement.removeChild(lineRow);
+                }
+            }
+            delete this._expandedLineRows;
+        }
+    },
+
+    get height()
+    {
+        if (!this._expandedLineRows)
+            return this.element.offsetHeight;
+        var result = 0;
+        for (var i = 0; i < this._expandedLineRows.length; ++i) {
+            var lineRow = this._expandedLineRows[i];
+            result += lineRow.offsetHeight;
+        }
+        return result;
+    },
+
+    _createRow: function(lineNumber)
+    {
+        var lineRow = this._textViewer._cachedRows.pop() || document.createElement("div");
+        lineRow.lineNumber = lineNumber;
+        lineRow.className = "webkit-line-number";
+        lineRow.textContent = lineNumber + 1;
+        return lineRow;
+    }
+}
+
+WebInspector.TextEditorMainPanel = function(textModel, url, syncScrollListener, syncDecorationsForLineListener)
+{
+    WebInspector.TextEditorChunkedPanel.call(this, textModel);
+
+    this._syncScrollListener = syncScrollListener;
+    this._syncDecorationsForLineListener = syncDecorationsForLineListener;
+
+    this._url = url;
+    this._highlighter = new WebInspector.TextEditorHighlighter(textModel, this._highlightDataReady.bind(this));
+
+    this.element = document.createElement("div");
+    this.element.className = "text-editor-contents";
+    this.element.tabIndex = 0;
+
+    this.element.addEventListener("scroll", this._scroll.bind(this), false);
+    this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false);
+
+    var handleDOMUpdates = this._handleDOMUpdates.bind(this);
+    this.element.addEventListener("DOMCharacterDataModified", handleDOMUpdates, false);
+    this.element.addEventListener("DOMNodeInserted", handleDOMUpdates, false);
+    this.element.addEventListener("DOMNodeRemoved", handleDOMUpdates, false);
+
+    this.freeCachedElements();
+    this._buildChunks();
+}
+
+WebInspector.TextEditorMainPanel.prototype = {
+    set mimeType(mimeType)
+    {
+        this._highlighter.mimeType = mimeType;
+    },
+
+    markAndRevealRange: function(range)
+    {
+        if (this._rangeToMark) {
+            var markedLine = this._rangeToMark.startLine;
+            this._rangeToMark = null;
+            this._paintLines(markedLine, markedLine + 1);
+        }
+
+        if (range) {
+            this._rangeToMark = range;
+            this.revealLine(range.startLine);
+            this._paintLines(range.startLine, range.startLine + 1);
+            if (this._markedRangeElement)
+                this._markedRangeElement.scrollIntoViewIfNeeded();
+        }
+        delete this._markedRangeElement;
+    },
+
+    highlightLine: function(lineNumber)
+    {
+        this.clearLineHighlight();
+        this._highlightedLine = lineNumber;
+        this.revealLine(lineNumber);
+        this.addDecoration(lineNumber, "webkit-highlighted-line");
+    },
+
+    clearLineHighlight: function()
+    {
+        if (typeof this._highlightedLine === "number") {
+            this.removeDecoration(this._highlightedLine, "webkit-highlighted-line");
+            delete this._highlightedLine;
+        }
+    },
+
+    freeCachedElements: function()
+    {
+        this._cachedSpans = [];
+        this._cachedTextNodes = [];
+        this._cachedRows = [];
     },
 
     _handleKeyDown: function()
@@ -248,16 +599,15 @@ WebInspector.TextViewer.prototype = {
 
     editLine: function(lineRow, callback)
     {
-        var element = lineRow.lastChild;
-        var oldContent = element.innerHTML;
+        var oldContent = lineRow.innerHTML;
         function finishEditing(committed, e, newContent)
         {
             if (committed)
                 callback(newContent);
-            element.innerHTML = oldContent;
+            lineRow.innerHTML = oldContent;
             delete this._editingLine;
         }
-        this._editingLine = WebInspector.startEditing(element, {
+        this._editingLine = WebInspector.startEditing(lineRow, {
             context: null,
             commitHandler: finishEditing.bind(this, true),
             cancelHandler: finishEditing.bind(this, false),
@@ -265,102 +615,21 @@ WebInspector.TextViewer.prototype = {
         });
     },
 
-    _beforeCopy: function(e)
-    {
-        e.preventDefault();
-    },
-
-    _copy: function(e)
-    {
-        var range = this._getSelection();
-        var text = this._textModel.copyRange(range);
-        InspectorFrontendHost.copyText(text);
-        e.preventDefault();
-    },
-
-    beginUpdates: function(enabled)
-    {
-        this._paintCoalescingLevel++;
-    },
-
-    endUpdates: function(enabled)
-    {
-        this._paintCoalescingLevel--;
-        if (!this._paintCoalescingLevel)
-            this._repaintAll();
-    },
-
-    _chunkForOffset: function(offset)
-    {
-        var currentOffset = 0;
-        var row = this._linesContainerElement.firstChild;
-        while (row) {
-            var rowHeight = row.offsetHeight;
-            if (offset >= currentOffset && offset < currentOffset + rowHeight)
-                return row.chunkNumber;
-            row = row.nextSibling;
-            currentOffset += rowHeight;
-        }
-        return this._textChunks.length - 1;
-    },
-
-    _chunkNumberForLine: function(lineNumber)
-    {
-        for (var i = 0; i < this._textChunks.length; ++i) {
-            var line = this._textChunks[i].startLine;
-            if (lineNumber >= this._textChunks[i].startLine && lineNumber < this._textChunks[i].startLine + this._textChunks[i].linesCount)
-                return i;
-        }
-        return this._textChunks.length - 1;
-    },
-
-    _chunkForLine: function(lineNumber)
+    _buildChunks: function()
     {
-        return this._textChunks[this._chunkNumberForLine(lineNumber)];
+        this._highlighter.reset();
+        WebInspector.TextEditorChunkedPanel.prototype._buildChunks.call(this);
     },
 
-    _chunkStartLine: function(chunkNumber)
+    _createNewChunk: function(startLine, endLine)
     {
-        var lineNumber = 0;
-        for (var i = 0; i < chunkNumber && i < this._textChunks.length; ++i)
-            lineNumber += this._textChunks[i].linesCount;
-        return lineNumber;
+        return new WebInspector.TextEditorMainChunk(this, startLine, endLine);
     },
 
-    _repaintAll: function()
+    _expandChunks: function(fromIndex, toIndex)
     {
-        if (this._paintCoalescingLevel)
-            return;
-
-        if (!this._textChunks)
-            this._buildChunks();
-
-        var visibleFrom = this.element.scrollTop;
-        var visibleTo = this.element.scrollTop + this.element.clientHeight;
-
-        var offset = 0;
-        var firstVisibleLine = -1;
-        var lastVisibleLine = 0;
-        var toExpand = [];
-        var toCollapse = [];
-        for (var i = 0; i < this._textChunks.length; ++i) {
-            var chunk = this._textChunks[i];
-            var chunkHeight = chunk.height;
-            if (offset + chunkHeight > visibleFrom && offset < visibleTo) {
-                toExpand.push(chunk);
-                if (firstVisibleLine === -1)
-                    firstVisibleLine = chunk.startLine;
-                lastVisibleLine = chunk.startLine + chunk.linesCount;
-            } else {
-                toCollapse.push(chunk);
-                if (offset >= visibleTo)
-                    break;
-            }
-            offset += chunkHeight;
-        }
-
-        for (var j = i; j < this._textChunks.length; ++j)
-            toCollapse.push(this._textChunks[i]);
+        var lastChunk = this._textChunks[toIndex - 1];
+        var lastVisibleLine = lastChunk.startLine + lastChunk.linesCount;
 
         var selection = this._getSelection();
 
@@ -368,10 +637,9 @@ WebInspector.TextViewer.prototype = {
         this._highlighter.highlight(lastVisibleLine);
         delete this._muteHighlightListener;
 
-        for (var i = 0; i < toCollapse.length; ++i)
-            toCollapse[i].expanded = false;
-        for (var i = 0; i < toExpand.length; ++i)
-            toExpand[i].expanded = true;
+        for (var i = 0; i < this._textChunks.length; ++i) {
+            this._textChunks[i].expanded = (fromIndex <= i && i < toIndex);
+        }
 
         this._restoreSelection(selection);
     },
@@ -380,40 +648,40 @@ WebInspector.TextViewer.prototype = {
     {
         if (this._muteHighlightListener)
             return;
+        this._paintLines(fromLine, toLine, true /*restoreSelection*/);
+    },
 
+    _paintLines: function(fromLine, toLine, restoreSelection)
+    {
         var selection;
+        var chunk = this._chunkForLine(fromLine);
         for (var i = fromLine; i < toLine; ++i) {
-            var lineRow = this._textModel.getAttribute(i, "line-row");
-            if (!lineRow || lineRow.highlighted)
+            if (i >= chunk.startLine + chunk.linesCount)
+                chunk = this._chunkForLine(i);
+            var lineRow = chunk.getExpandedLineRow(i);
+            if (!lineRow)
                 continue;
-            if (!selection)
+            if (restoreSelection && !selection)
                 selection = this._getSelection();
             this._paintLine(lineRow, i);
         }
-        this._restoreSelection(selection);
-    },
-
-    _paintLines: function(fromLine, toLine)
-    {
-        for (var i = fromLine; i < toLine; ++i) {
-            var lineRow = this._textModel.getAttribute(i, "line-row");
-            if (lineRow)
-                this._paintLine(lineRow, i);
-        }
+        if (restoreSelection)
+            this._restoreSelection(selection);
     },
 
     _paintLine: function(lineRow, lineNumber)
     {
-        var element = lineRow.lastChild;
         var highlight = this._textModel.getAttribute(lineNumber, "highlight");
         if (!highlight) {
             if (this._rangeToMark && this._rangeToMark.startLine === lineNumber)
-                this._markedRangeElement = highlightSearchResult(element, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn);
+                this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn);
             return;
         }
 
-        element.removeChildren();
+        lineRow.removeChildren();
         var line = this._textModel.line(lineNumber);
+        if (!line)
+            lineRow.appendChild(document.createElement("br"));
 
         var plainTextStart = -1;
         for (var j = 0; j < line.length;) {
@@ -430,98 +698,64 @@ WebInspector.TextViewer.prototype = {
                 j++;
             } else {
                 if (plainTextStart !== -1) {
-                    this._appendTextNode(element, line.substring(plainTextStart, j));
+                    this._appendTextNode(lineRow, line.substring(plainTextStart, j));
                     plainTextStart = -1;
                 }
-                this._appendSpan(element, line.substring(j, j + attribute.length), attribute.tokenType);
+                this._appendSpan(lineRow, line.substring(j, j + attribute.length), attribute.tokenType);
                 j += attribute.length;
             }
         }
         if (plainTextStart !== -1)
-            this._appendTextNode(element, line.substring(plainTextStart, line.length));
+            this._appendTextNode(lineRow, line.substring(plainTextStart, line.length));
         if (this._rangeToMark && this._rangeToMark.startLine === lineNumber)
-            this._markedRangeElement = highlightSearchResult(element, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn);
+            this._markedRangeElement = highlightSearchResult(lineRow, this._rangeToMark.startColumn, this._rangeToMark.endColumn - this._rangeToMark.startColumn);
         if (lineRow.decorationsElement)
-            element.appendChild(lineRow.decorationsElement);
+            lineRow.appendChild(lineRow.decorationsElement);
     },
 
-    _releaseLinesHighlight: function(fromLine, toLine)
+    _releaseLinesHighlight: function(lineRow)
     {
-        for (var i = fromLine; i < toLine; ++i) {
-            var lineRow = this._textModel.getAttribute(i, "line-row");
-            if (!lineRow)
-                continue;
-            var element = lineRow.lastChild;
-            if ("spans" in element) {
-                var spans = element.spans;
-                for (var j = 0; j < spans.length; ++j)
-                    this._cachedSpans.push(spans[j]);
-                delete element.spans;
-            }
-            if ("textNodes" in element) {
-                var textNodes = element.textNodes;
-                for (var j = 0; j < textNodes.length; ++j)
-                    this._cachedTextNodes.push(textNodes[j]);
-                delete element.textNodes;
-            }
+        if (!lineRow)
+            return;
+        if ("spans" in lineRow) {
+            var spans = lineRow.spans;
+            for (var j = 0; j < spans.length; ++j)
+                this._cachedSpans.push(spans[j]);
+            delete lineRow.spans;
+        }
+        if ("textNodes" in lineRow) {
+            var textNodes = lineRow.textNodes;
+            for (var j = 0; j < textNodes.length; ++j)
+                this._cachedTextNodes.push(textNodes[j]);
+            delete lineRow.textNodes;
         }
+        this._cachedRows.push(lineRow);
     },
 
     _getSelection: function()
     {
         var selection = window.getSelection();
-        if (selection.isCollapsed)
+        if (!selection.rangeCount)
             return null;
         var selectionRange = selection.getRangeAt(0);
         // Selection may be outside of the viewer.
         if (!this.element.isAncestor(selectionRange.startContainer) || !this.element.isAncestor(selectionRange.endContainer))
             return null;
         var start = this._selectionToPosition(selectionRange.startContainer, selectionRange.startOffset);
-        var end = this._selectionToPosition(selectionRange.endContainer, selectionRange.endOffset);
-        return new WebInspector.TextRange(start.line, start.column, end.line, end.column);
+        var end = selectionRange.collapsed ? start : this._selectionToPosition(selectionRange.endContainer, selectionRange.endOffset);
+        if (selection.anchorNode === selectionRange.startContainer && selection.anchorOffset === selectionRange.startOffset)
+            return new WebInspector.TextRange(start.line, start.column, end.line, end.column);
+        else
+            return new WebInspector.TextRange(end.line, end.column, start.line, start.column);
     },
 
     _restoreSelection: function(range)
     {
         if (!range)
             return;
-        var startRow = this._textModel.getAttribute(range.startLine, "line-row");
-        if (startRow)
-            var start = startRow.lastChild.rangeBoundaryForOffset(range.startColumn);
-        else {
-            var offset = range.startColumn;
-            var chunkNumber = this._chunkNumberForLine(range.startLine);
-            for (var i = this._chunkStartLine(chunkNumber); i < range.startLine; ++i)
-                offset += this._textModel.line(i).length + 1; // \n
-            var lineCell = this._textChunks[chunkNumber].element.lastChild;
-            if (lineCell.firstChild)
-                var start = { container: lineCell.firstChild, offset: offset };
-            else
-                var start = { container: lineCell, offset: 0 };
-        }
-
-        var endRow = this._textModel.getAttribute(range.endLine, "line-row");
-        if (endRow)
-            var end = endRow.lastChild.rangeBoundaryForOffset(range.endColumn);
-        else {
-            var offset = range.endColumn;
-            var chunkNumber = this._chunkNumberForLine(range.endLine);
-            for (var i = this._chunkStartLine(chunkNumber); i < range.endLine; ++i)
-                offset += this._textModel.line(i).length + 1; // \n
-            var lineCell = this._textChunks[chunkNumber].element.lastChild;
-            if (lineCell.firstChild)
-                var end = { container: lineCell.firstChild, offset: offset };
-            else
-                var end = { container: lineCell, offset: 0 };
-        }
-
-        var selectionRange = document.createRange();
-        selectionRange.setStart(start.container, start.offset);
-        selectionRange.setEnd(end.container, end.offset);
-
-        var selection = window.getSelection();
-        selection.removeAllRanges();
-        selection.addRange(selectionRange);
+        var start = this._positionToSelection(range.startLine, range.startColumn);
+        var end = range.isEmpty() ? start : this._positionToSelection(range.endLine, range.endColumn);
+        window.getSelection().setBaseAndExtent(start.container, start.offset, end.container, end.offset);
     },
 
     _selectionToPosition: function(container, offset)
@@ -531,21 +765,26 @@ WebInspector.TextViewer.prototype = {
         if (container === this.element && offset === 1)
             return { line: this._textModel.linesCount - 1, column: this._textModel.lineLength(this._textModel.linesCount - 1) };
 
-        var lineRow = container.enclosingNodeOrSelfWithNodeName("tr");
+        var lineRow = container.enclosingNodeOrSelfWithNodeName("DIV");
         var lineNumber = lineRow.lineNumber;
-        if (container.nodeName === "TD" && offset === 0)
+        if (container === lineRow && offset === 0)
             return { line: lineNumber, column: 0 };
-        if (container.nodeName === "TD" && offset === 1)
-            return { line: lineNumber, column: this._textModel.lineLength(lineNumber) };
 
+        // This may be chunk and chunks may contain \n.
         var column = 0;
-        var node = lineRow.lastChild.traverseNextTextNode(lineRow.lastChild);
+        var node = lineRow.traverseNextTextNode(lineRow);
         while (node && node !== container) {
-            column += node.textContent.length;
-            node = node.traverseNextTextNode(lineRow.lastChild);
+            var text = node.textContent;
+            for (var i = 0; i < text.length; ++i) {
+                if (text.charAt(i) === "\n") {
+                    lineNumber++;
+                    column = 0;
+                } else
+                    column++;
+            }
+            node = node.traverseNextTextNode(lineRow);
         }
 
-        // This may be chunk and chunks may contain \n.
         if (node === container && offset) {
             var text = node.textContent;
             for (var i = 0; i < offset; ++i) {
@@ -559,6 +798,25 @@ WebInspector.TextViewer.prototype = {
         return { line: lineNumber, column: column };
     },
 
+    _positionToSelection: function(line, column)
+    {
+        var chunk = this._chunkForLine(line);
+        var lineRow = chunk.getExpandedLineRow(line);
+        if (lineRow)
+            var rangeBoundary = lineRow.rangeBoundaryForOffset(column);
+        else {
+            var offset = column;
+            for (var i = chunk.startLine; i < line; ++i)
+                offset += this._textModel.lineLength(i) + 1; // \n
+            lineRow = chunk.element;
+            if (lineRow.firstChild)
+                var rangeBoundary = { container: lineRow.firstChild, offset: offset };
+            else
+                var rangeBoundary = { container: lineRow, offset: 0 };
+        }
+        return rangeBoundary;
+    },
+
     _appendSpan: function(element, content, className)
     {
         if (className === "html-resource-link" || className === "html-external-link") {
@@ -578,9 +836,9 @@ WebInspector.TextViewer.prototype = {
     _appendTextNode: function(element, text)
     {
         var textNode = this._cachedTextNodes.pop();
-        if (textNode) {
+        if (textNode)
             textNode.nodeValue = text;
-        } else
+        else
             textNode = document.createTextNode(text);
         element.appendChild(textNode);
         if (!("textNodes" in element))
@@ -614,58 +872,52 @@ WebInspector.TextViewer.prototype = {
         return WebInspector.completeURL(this._url, hrefValue);
     },
 
-    resize: function()
+    _handleDOMUpdates: function(e)
     {
-        this._repaintAll();
+        var target = e.target;
+        var lineRow = target.enclosingNodeOrSelfWithClass("webkit-line-content");
+        if (lineRow === target || !lineRow || !lineRow.decorationsElement || !lineRow.decorationsElement.isAncestor(target))
+            return;
+        if (this._syncDecorationsForLineListener) {
+            // Wait until this event is processed and only then sync the sizes. This is necessary in
+            // case of the DOMNodeRemoved event, because it is dispatched before the removal takes place.
+            setTimeout(function() {
+                this._syncDecorationsForLineListener(lineRow.lineNumber);
+            }.bind(this), 0);
+        }
     }
 }
 
-var cachedSpans = [];
+WebInspector.TextEditorMainPanel.prototype.__proto__ = WebInspector.TextEditorChunkedPanel.prototype;
 
-WebInspector.TextChunk = function(textViewer, startLine, endLine)
+WebInspector.TextEditorMainChunk = function(textViewer, startLine, endLine)
 {
     this._textViewer = textViewer;
-    this.element = document.createElement("tr");
     this._textModel = textViewer._textModel;
-    this.element.chunk = this;
+
+    this.element = document.createElement("div");
     this.element.lineNumber = startLine;
+    this.element.className = "webkit-line-content";
 
     this.startLine = startLine;
     endLine = Math.min(this._textModel.linesCount, endLine);
     this.linesCount = endLine - startLine;
 
-    this._lineNumberElement = document.createElement("td");
-    this._lineNumberElement.className = "webkit-line-number";
-    this.element.appendChild(this._lineNumberElement);
-
-    this._lineContentElement = document.createElement("td");
-    this._lineContentElement.className = "webkit-line-content";
-    this.element.appendChild(this._lineContentElement);
-
     this._expanded = false;
 
-    var lineNumbers = [];
     var lines = [];
     for (var i = startLine; i < endLine; ++i) {
-        lineNumbers.push(i + 1);
         lines.push(this._textModel.line(i));
     }
-    if (this.linesCount === 1) {
-        // Single line chunks are typically created for decorations. Host line number in
-        // the sub-element in order to allow flexible border / margin management.
-        var innerSpan = document.createElement("span");
-        innerSpan.className = "webkit-line-number-inner";
-        innerSpan.textContent = startLine + 1;
-        var outerSpan = document.createElement("div");
-        outerSpan.className = "webkit-line-number-outer";
-        outerSpan.appendChild(innerSpan);
-        this._lineNumberElement.appendChild(outerSpan);
-    } else
-        this._lineNumberElement.textContent = lineNumbers.join("\n");
-    this._lineContentElement.textContent = lines.join("\n");
+
+    this.element.textContent = lines.join("\n");
+
+    // The last empty line will get swallowed otherwise.
+    if (!lines[lines.length - 1])
+        this.element.appendChild(document.createElement("br"));
 }
 
-WebInspector.TextChunk.prototype = {
+WebInspector.TextEditorMainChunk.prototype = {
     addDecoration: function(decoration)
     {
         if (typeof decoration === "string") {
@@ -674,7 +926,8 @@ WebInspector.TextChunk.prototype = {
         }
         if (!this.element.decorationsElement) {
             this.element.decorationsElement = document.createElement("div");
-            this._lineContentElement.appendChild(this.element.decorationsElement);
+            this.element.decorationsElement.className = "webkit-line-decorations";
+            this.element.appendChild(this.element.decorationsElement);
         }
         this.element.decorationsElement.appendChild(decoration);
     },
@@ -703,44 +956,46 @@ WebInspector.TextChunk.prototype = {
         this._expanded = expanded;
 
         if (this.linesCount === 1) {
-            this._textModel.setAttribute(this.startLine, "line-row", this.element);
             if (expanded)
-                this._textViewer._paintLines(this.startLine, this.startLine + 1);
+                this._textViewer._paintLine(this.element, this.startLine);
             return;
         }
 
         if (expanded) {
+            this._expandedLineRows = [];
             var parentElement = this.element.parentElement;
             for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
                 var lineRow = this._createRow(i);
-                this._textModel.setAttribute(i, "line-row", lineRow);
                 parentElement.insertBefore(lineRow, this.element);
+                this._expandedLineRows.push(lineRow);
+                this._textViewer._paintLine(lineRow, i);
             }
             parentElement.removeChild(this.element);
-
-            this._textViewer._paintLines(this.startLine, this.startLine + this.linesCount);
         } else {
-            var firstLine = this._textModel.getAttribute(this.startLine, "line-row");
-            var parentElement = firstLine.parentElement;
-            this._textViewer._releaseLinesHighlight(this.startLine, this.startLine + this.linesCount);
-
-            parentElement.insertBefore(this.element, firstLine);
-            for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
-                var lineRow = this._textModel.getAttribute(i, "line-row");
-                this._textModel.removeAttribute(i, "line-row");
-                this._textViewer._cachedRows.push(lineRow);
-                parentElement.removeChild(lineRow);
+            var elementInserted = false;
+            for (var i = 0; i < this._expandedLineRows.length; ++i) {
+                var lineRow = this._expandedLineRows[i];
+                var parentElement = lineRow.parentElement;
+                if (parentElement) {
+                    if (!elementInserted) {
+                        elementInserted = true;
+                        parentElement.insertBefore(this.element, lineRow);
+                    }
+                    this._textViewer._releaseLinesHighlight(lineRow);
+                    parentElement.removeChild(lineRow);
+                }
             }
+            delete this._expandedLineRows;
         }
     },
 
     get height()
     {
-        if (!this._expanded)
+        if (!this._expandedLineRows)
             return this.element.offsetHeight;
         var result = 0;
-        for (var i = this.startLine; i < this.startLine + this.linesCount; ++i) {
-            var lineRow = this._textModel.getAttribute(i, "line-row");
+        for (var i = 0; i < this._expandedLineRows.length; ++i) {
+            var lineRow = this._expandedLineRows[i];
             result += lineRow.offsetHeight;
         }
         return result;
@@ -748,26 +1003,21 @@ WebInspector.TextChunk.prototype = {
 
     _createRow: function(lineNumber)
     {
-        var cachedRows = this._textViewer._cachedRows;
-        if (cachedRows.length) {
-            var lineRow = cachedRows[cachedRows.length - 1];
-            cachedRows.length--;
-            var lineNumberElement = lineRow.firstChild;
-            var lineContentElement = lineRow.lastChild;
-        } else {
-            var lineRow = document.createElement("tr");
-
-            var lineNumberElement = document.createElement("td");
-            lineNumberElement.className = "webkit-line-number";
-            lineRow.appendChild(lineNumberElement);
-
-            var lineContentElement = document.createElement("td");
-            lineContentElement.className = "webkit-line-content";
-            lineRow.appendChild(lineContentElement);
-        }
+        var lineRow = this._textViewer._cachedRows.pop() || document.createElement("div");
         lineRow.lineNumber = lineNumber;
-        lineNumberElement.textContent = lineNumber + 1;
-        lineContentElement.textContent = this._textModel.line(lineNumber);
+        lineRow.className = "webkit-line-content";
+        lineRow.textContent = this._textModel.line(lineNumber);
+        if (!lineRow.textContent)
+            lineRow.appendChild(document.createElement("br"));
         return lineRow;
+    },
+
+    getExpandedLineRow: function(lineNumber)
+    {
+        if (!this._expanded || lineNumber < this.startLine || lineNumber >= this.startLine + this.linesCount)
+            return null;
+        if (!this._expandedLineRows)
+            return this.element;
+        return this._expandedLineRows[lineNumber - this.startLine];
     }
 }
diff --git a/Source/WebCore/inspector/front-end/textViewer.css b/Source/WebCore/inspector/front-end/textViewer.css
index bee9fe5..54957b9 100644
--- a/Source/WebCore/inspector/front-end/textViewer.css
+++ b/Source/WebCore/inspector/front-end/textViewer.css
@@ -9,12 +9,34 @@
 }
 
 .text-editor-lines {
-    border: 0;
-    -webkit-border-horizontal-spacing: 0;
-    -webkit-border-vertical-spacing: 0;
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    overflow: hidden;
+    -webkit-user-select: none;
+}
+
+.text-editor-contents {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow: auto;
     -webkit-user-select: text;
 }
 
+.text-editor-editable {
+    -webkit-user-modify: read-write-plaintext-only;
+}
+
+.webkit-line-decorations {
+    pointer-events: none;
+    -webkit-user-select: none;
+    -webkit-user-modify: read-only;
+}
+
 .webkit-html-message-bubble {
     -webkit-box-shadow: black 0px 2px 5px;
     -webkit-border-radius: 9px;
@@ -63,7 +85,6 @@
     text-align: right;
     vertical-align: top;
     word-break: normal;
-    -webkit-user-select: none;
     padding-right: 4px;
     padding-left: 6px;
 }
@@ -85,12 +106,6 @@
     margin-right: -10px;
 }
 
-.webkit-line-content {
-    width: 100%;
-    padding-left: 2px;
-    vertical-align: top;
-}
-
 .webkit-breakpoint .webkit-line-number-outer {
     color: white;
     border-width: 0 14px 0px 2px;
@@ -136,21 +151,21 @@
     opacity: 0.3;
 }
 
-.webkit-execution-line .webkit-line-content {
+.webkit-execution-line.webkit-line-content {
     background-color: rgb(171, 191, 254);
     outline: 1px solid rgb(64, 115, 244);
 }
 
-.diff-container .webkit-added-line .webkit-line-content {
+.diff-container .webkit-added-line.webkit-line-content {
     background-color: rgb(220, 255, 220);
 }
 
-.diff-container .webkit-removed-line .webkit-line-content {
+.diff-container .webkit-removed-line.webkit-line-content {
     background-color: rgb(255, 220, 220);
     text-decoration: line-through;
 }
 
-.diff-container .webkit-changed-line .webkit-line-content {
+.diff-container .webkit-changed-line.webkit-line-content {
     background-color: rgb(220, 220, 255);
 }
 
@@ -164,7 +179,7 @@
     color: black;
 }
 
-.webkit-highlighted-line .webkit-line-content {
+.webkit-highlighted-line.webkit-line-content {
     -webkit-animation: "fadeout" 2s 0s;
 }
 

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list