2011-01-12 Pavel Podivilov <podivilov@chromium.org>
authorpodivilov@chromium.org <podivilov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Jan 2011 11:59:29 +0000 (11:59 +0000)
committerpodivilov@chromium.org <podivilov@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Jan 2011 11:59:29 +0000 (11:59 +0000)
        Reviewed by Pavel Feldman.

        Web Inspector: breakpoints are restored incorrectly when reverting live edit.
        https://bugs.webkit.org/show_bug.cgi?id=52300

        Fix breakpoints restoring when reverting to old revision by using text diff.
        Move live edit logic from ScriptsPanel to DebuggerModel.
        Eliminate unnecessary editLine delegate in TextViewer.

        * inspector/front-end/DebuggerModel.js:
        (WebInspector.DebuggerModel):
        (WebInspector.DebuggerModel.prototype.reset):
        (WebInspector.DebuggerModel.prototype.editScriptSource):
        (WebInspector.DebuggerModel.prototype._updateScriptSource):
        (WebInspector.DebuggerModel.prototype.get callFrames):
        (WebInspector.DebuggerModel.prototype.pausedScript):
        (WebInspector.DebuggerModel.prototype.resumedScript):
        * inspector/front-end/Script.js:
        (WebInspector.Script.prototype.get source):
        * inspector/front-end/ScriptView.js:
        (WebInspector.ScriptView):
        * inspector/front-end/ScriptsPanel.js:
        (WebInspector.ScriptsPanel):
        (WebInspector.ScriptsPanel.prototype._scriptSourceChanged):
        * inspector/front-end/SourceFrame.js:
        (WebInspector.SourceFrame):
        (WebInspector.SourceFrame.prototype._createViewerIfNeeded):
        (WebInspector.SourceFrame.prototype._doubleClick.didEditLine):
        (WebInspector.SourceFrame.prototype._doubleClick):
        * inspector/front-end/SourceView.js:
        (WebInspector.SourceView):
        * inspector/front-end/TextViewer.js:
        (WebInspector.TextViewer):
        (WebInspector.TextViewer.prototype._handleKeyDown):
        (WebInspector.TextViewer.prototype.editLine.finishEditing):
        (WebInspector.TextViewer.prototype.editLine):
        (WebInspector.TextChunk.prototype._createRow):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@76341 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/DebuggerModel.js
Source/WebCore/inspector/front-end/Script.js
Source/WebCore/inspector/front-end/ScriptView.js
Source/WebCore/inspector/front-end/ScriptsPanel.js
Source/WebCore/inspector/front-end/SourceFrame.js
Source/WebCore/inspector/front-end/SourceView.js
Source/WebCore/inspector/front-end/TextViewer.js

index 464673f..69356ab 100644 (file)
@@ -1,3 +1,43 @@
+2011-01-12  Pavel Podivilov  <podivilov@chromium.org>
+
+        Reviewed by Pavel Feldman.
+
+        Web Inspector: breakpoints are restored incorrectly when reverting live edit.
+        https://bugs.webkit.org/show_bug.cgi?id=52300
+
+        Fix breakpoints restoring when reverting to old revision by using text diff.
+        Move live edit logic from ScriptsPanel to DebuggerModel.
+        Eliminate unnecessary editLine delegate in TextViewer.
+
+        * inspector/front-end/DebuggerModel.js:
+        (WebInspector.DebuggerModel):
+        (WebInspector.DebuggerModel.prototype.reset):
+        (WebInspector.DebuggerModel.prototype.editScriptSource):
+        (WebInspector.DebuggerModel.prototype._updateScriptSource):
+        (WebInspector.DebuggerModel.prototype.get callFrames):
+        (WebInspector.DebuggerModel.prototype.pausedScript):
+        (WebInspector.DebuggerModel.prototype.resumedScript):
+        * inspector/front-end/Script.js:
+        (WebInspector.Script.prototype.get source):
+        * inspector/front-end/ScriptView.js:
+        (WebInspector.ScriptView):
+        * inspector/front-end/ScriptsPanel.js:
+        (WebInspector.ScriptsPanel):
+        (WebInspector.ScriptsPanel.prototype._scriptSourceChanged):
+        * inspector/front-end/SourceFrame.js:
+        (WebInspector.SourceFrame):
+        (WebInspector.SourceFrame.prototype._createViewerIfNeeded):
+        (WebInspector.SourceFrame.prototype._doubleClick.didEditLine):
+        (WebInspector.SourceFrame.prototype._doubleClick):
+        * inspector/front-end/SourceView.js:
+        (WebInspector.SourceView):
+        * inspector/front-end/TextViewer.js:
+        (WebInspector.TextViewer):
+        (WebInspector.TextViewer.prototype._handleKeyDown):
+        (WebInspector.TextViewer.prototype.editLine.finishEditing):
+        (WebInspector.TextViewer.prototype.editLine):
+        (WebInspector.TextChunk.prototype._createRow):
+
 2011-01-21  Adam Klein  <adamk@chromium.org>
 
         Reviewed by Eric Seidel.
index e01f8bd..717486c 100644 (file)
@@ -31,6 +31,7 @@
 WebInspector.DebuggerModel = function()
 {
     this._paused = false;
+    this._callFrames = [];
     this._breakpoints = {};
     this._sourceIDAndLineToBreakpointId = {};
     this._scripts = {};
@@ -43,6 +44,7 @@ WebInspector.DebuggerModel.Events = {
     DebuggerResumed: "debugger-resumed",
     ParsedScriptSource: "parsed-script-source",
     FailedToParseScriptSource: "failed-to-parse-script-source",
+    ScriptSourceChanged: "script-source-changed",
     BreakpointAdded: "breakpoint-added",
     BreakpointRemoved: "breakpoint-removed"
 }
@@ -135,6 +137,7 @@ WebInspector.DebuggerModel.prototype = {
     reset: function()
     {
         this._paused = false;
+        this._callFrames = [];
         this._breakpoints = {};
         delete this._oneTimeBreakpoint;
         this._sourceIDAndLineToBreakpointId = {};
@@ -162,9 +165,64 @@ WebInspector.DebuggerModel.prototype = {
         return scripts;
     },
 
+    editScriptSource: function(sourceID, scriptSource)
+    {
+        function didEditScriptSource(success, newBodyOrErrorMessage, callFrames)
+        {
+            if (success) {
+                if (callFrames && callFrames.length)
+                    this._callFrames = callFrames;
+                this._updateScriptSource(sourceID, newBodyOrErrorMessage);
+            } else
+                WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning);
+        }
+        InspectorBackend.editScriptSource(sourceID, scriptSource, didEditScriptSource.bind(this));
+    },
+
+    _updateScriptSource: function(sourceID, scriptSource)
+    {
+        var script = this._scripts[sourceID];
+        var oldSource = script.source;
+        script.source = scriptSource;
+
+        // Clear and re-create breakpoints according to text diff.
+        var diff = Array.diff(oldSource.split("\n"), script.source.split("\n"));
+        for (var id in this._breakpoints) {
+            var breakpoint = this._breakpoints[id];
+            if (breakpoint.sourceID !== sourceID)
+                continue;
+            breakpoint.remove();
+            var lineNumber = breakpoint.line - 1;
+            var newLineNumber = diff.left[lineNumber].row;
+            if (newLineNumber === undefined) {
+                for (var i = lineNumber - 1; i >= 0; --i) {
+                    if (diff.left[i].row === undefined)
+                        continue;
+                    var shiftedLineNumber = diff.left[i].row + lineNumber - i;
+                    if (shiftedLineNumber < diff.right.length) {
+                        var originalLineNumber = diff.right[shiftedLineNumber].row;
+                        if (originalLineNumber === lineNumber || originalLineNumber === undefined)
+                            newLineNumber = shiftedLineNumber;
+                    }
+                    break;
+                }
+            }
+            if (newLineNumber !== undefined)
+                this.setBreakpoint(sourceID, newLineNumber + 1, breakpoint.enabled, breakpoint.condition);
+        }
+
+        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ScriptSourceChanged, { sourceID: sourceID, oldSource: oldSource });
+    },
+
+    get callFrames()
+    {
+        return this._callFrames;
+    },
+
     _pausedScript: function(details)
     {
         this._paused = true;
+        this._callFrames = details.callFrames;
         if ("_continueToLineBreakpointId" in this) {
             InspectorBackend.removeBreakpoint(this._continueToLineBreakpointId);
             delete this._continueToLineBreakpointId;
@@ -175,6 +233,7 @@ WebInspector.DebuggerModel.prototype = {
     _resumedScript: function()
     {
         this._paused = false;
+        this._callFrames = [];
         this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
     },
 
index f0ddeac..6e3b18d 100644 (file)
@@ -97,12 +97,15 @@ WebInspector.Script.prototype = {
 
     get source()
     {
+        if (!this._source && this.resource)
+            this._source = this.resource.content;
         return this._source;
     },
 
     set source(source)
     {
         this._source = source;
+        delete this._lineEndings;
     },
 
     requestSource: function(callback)
index d6c1c59..f631fcc 100644 (file)
@@ -30,7 +30,7 @@ WebInspector.ScriptView = function(script)
     this.element.addStyleClass("script-view");
 
     var contentProvider = new WebInspector.SourceFrameContentProviderForScript(script);
-    this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, "", WebInspector.panels.scripts.canEditScripts());
+    this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, "", true);
 }
 
 WebInspector.ScriptView.prototype = {
index c3a7c4d..a74f80d 100644 (file)
@@ -186,6 +186,7 @@ WebInspector.ScriptsPanel = function()
 
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
+    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ScriptSourceChanged, this._scriptSourceChanged, this);
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
 }
@@ -251,6 +252,27 @@ WebInspector.ScriptsPanel.prototype = {
         this._addScript(event.data);
     },
 
+    _scriptSourceChanged: function(event)
+    {
+        var sourceID = event.data.sourceID;
+        var oldSource = event.data.oldSource;
+
+        var script = WebInspector.debuggerModel.scriptForSourceID(sourceID);
+        var oldView = script._scriptView;
+        if (oldView) {
+            script._scriptView = new WebInspector.ScriptView(script);
+            this.viewRecreated(oldView, script._scriptView);
+        }
+        if (script.resource) {
+            var revertHandle = WebInspector.debuggerModel.editScriptSource.bind(WebInspector.debuggerModel, sourceID, oldSource);
+            script.resource.setContent(script.source, revertHandle);
+        }
+
+        var callFrames = WebInspector.debuggerModel.callFrames;
+        if (callFrames.length)
+            this._debuggerPaused({ data: { callFrames: callFrames } });
+    },
+
     _addScript: function(script)
     {
         var resource = WebInspector.resourceForURL(script.sourceURL);
@@ -286,52 +308,6 @@ WebInspector.ScriptsPanel.prototype = {
         delete resource._scriptsPendingResourceLoad;
     },
 
-    canEditScripts: function()
-    {
-        return Preferences.canEditScriptSource;
-    },
-
-    editScriptSource: function(editData, revertEditingCallback, cancelEditingCallback)
-    {
-        if (!this.canEditScripts())
-            return;
-
-        // Need to clear breakpoints and re-create them later when editing source.
-        var breakpoints = WebInspector.debuggerModel.queryBreakpoints(function(b) { return b.sourceID === editData.sourceID });
-        for (var i = 0; i < breakpoints.length; ++i)
-            breakpoints[i].remove();
-
-        function mycallback(success, newBodyOrErrorMessage, callFrames)
-        {
-            if (success) {
-                var script = WebInspector.debuggerModel.scriptForSourceID(editData.sourceID);
-                script.source = newBodyOrErrorMessage;
-                var oldView = script._scriptView
-                if (oldView) {
-                    script._scriptView = new WebInspector.ScriptView(script);
-                    this.viewRecreated(oldView, script._scriptView);
-                }
-                if (script.resource)
-                    script.resource.setContent(newBodyOrErrorMessage, revertEditingCallback);
-
-                if (callFrames && callFrames.length)
-                    this._debuggerPaused({ data: { callFrames: callFrames } });
-            } else {
-                if (cancelEditingCallback)
-                    cancelEditingCallback();
-                WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning);
-            }
-            for (var i = 0; i < breakpoints.length; ++i) {
-                var breakpoint = breakpoints[i];
-                var newLine = breakpoint.line;
-                if (success && breakpoint.line >= editData.line)
-                    newLine += editData.linesCountToShift;
-                WebInspector.debuggerModel.setBreakpoint(editData.sourceID, newLine, breakpoint.enabled, breakpoint.condition);
-            }
-        };
-        InspectorBackend.editScriptSource(editData.sourceID, editData.content, mycallback.bind(this));
-    },
-
     selectedCallFrameId: function()
     {
         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
index 1c6c4cd..eb89f24 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.SourceFrame = function(parentElement, contentProvider, url, canEditScripts)
+WebInspector.SourceFrame = function(parentElement, contentProvider, url, isScript)
 {
     this._parentElement = parentElement;
     this._contentProvider = contentProvider;
     this._url = url;
-    this._canEditScripts = canEditScripts;
+    this._isScript = isScript;
+
 
     this._textModel = new WebInspector.TextEditorModel();
     this._textModel.replaceTabsWithSpaces = true;
@@ -163,6 +164,7 @@ WebInspector.SourceFrame.prototype = {
         element.addEventListener("mousedown", this._mouseDown.bind(this), true);
         element.addEventListener("mousemove", this._mouseMove.bind(this), true);
         element.addEventListener("scroll", this._scroll.bind(this), true);
+        element.addEventListener("dblclick", this._doubleClick.bind(this), true);
         this._parentElement.appendChild(element);
 
         this._textViewer.beginUpdates();
@@ -802,39 +804,34 @@ WebInspector.SourceFrame.prototype = {
         WebInspector.debuggerModel.continueToLine(sourceID, lineNumber + 1);
     },
 
-    _editLine: function(lineNumber, newContent, cancelEditingCallback)
+    _doubleClick: function(event)
     {
-        var lines = [];
-        var oldLines = this._content.split('\n');
-        for (var i = 0; i < oldLines.length; ++i) {
-            if (i === lineNumber)
-                lines.push(newContent);
-            else
-                lines.push(oldLines[i]);
-        }
+        if (!Preferences.canEditScriptSource || !this._isScript)
+            return;
 
-        var editData = {};
-        editData.sourceID = this._sourceIDForLine(lineNumber);
-        editData.content = lines.join("\n");
-        editData.line = lineNumber + 1;
-        editData.linesCountToShift = newContent.split("\n").length - 1;
-        this._doEditLine(editData, cancelEditingCallback);
-    },
+        var target = event.target.enclosingNodeOrSelfWithNodeName("TD");
+        if (!target || target.parentElement.firstChild === target)
+            return;  // Do not trigger editing from line numbers.
 
-    _revertEditLine: function(editData, contentToRevertTo)
-    {
-        var newEditData = {};
-        newEditData.sourceID = editData.sourceID;
-        newEditData.content = contentToRevertTo;
-        newEditData.line = editData.line;
-        newEditData.linesCountToShift = -editData.linesCountToShift;
-        this._doEditLine(newEditData);
-    },
+        var lineRow = target.parentElement;
+        var lineNumber = lineRow.lineNumber;
+        var sourceID = this._sourceIDForLine(lineNumber);
+        if (!sourceID)
+            return;
 
-    _doEditLine: function(editData, cancelEditingCallback)
-    {
-        var revertEditingCallback = this._revertEditLine.bind(this, editData);
-        WebInspector.panels.scripts.editScriptSource(editData, revertEditingCallback, cancelEditingCallback);
+        function didEditLine(newContent)
+        {
+            var lines = [];
+            var oldLines = this._content.split('\n');
+            for (var i = 0; i < oldLines.length; ++i) {
+                if (i === lineNumber)
+                    lines.push(newContent);
+                else
+                    lines.push(oldLines[i]);
+            }
+            WebInspector.debuggerModel.editScriptSource(sourceID, lines.join("\n"));
+        }
+        this._textViewer.editLine(lineRow, didEditLine.bind(this));
     },
 
     _setBreakpoint: function(lineNumber, enabled, condition)
index e78ff94..37caabb 100644 (file)
@@ -33,8 +33,8 @@ WebInspector.SourceView = function(resource)
     this.element.addStyleClass("source");
 
     var contentProvider = new WebInspector.SourceFrameContentProviderForResource(resource);
-    var canEditScripts = WebInspector.panels.scripts.canEditScripts() && resource.type === WebInspector.Resource.Type.Script;
-    this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, resource.url, canEditScripts);
+    var isScript = resource.type === WebInspector.Resource.Type.Script;
+    this.sourceFrame = new WebInspector.SourceFrame(this.element, contentProvider, resource.url, isScript);
 }
 
 WebInspector.SourceView.prototype = {
index f116dea..ea36513 100644 (file)
@@ -43,7 +43,6 @@ WebInspector.TextViewer = function(textModel, platform, url)
     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.element.addEventListener("dblclick", this._handleDoubleClick.bind(this), false);
 
     this._url = url;
 
@@ -80,11 +79,6 @@ WebInspector.TextViewer.prototype = {
         chunk.element.scrollIntoViewIfNeeded();
     },
 
-    set editCallback(editCallback)
-    {
-        this._editCallback = editCallback;
-    },
-
     addDecoration: function(lineNumber, decoration)
     {
         var chunk = this._makeLineAChunk(lineNumber);
@@ -231,20 +225,20 @@ WebInspector.TextViewer.prototype = {
             scrollValue = -1;
         else if (event.keyCode == WebInspector.KeyboardShortcut.Keys.Down.code)
             scrollValue = 1;
-        
+
         if (scrollValue) {
             event.preventDefault();
             event.stopPropagation();
             this.element.scrollByLines(scrollValue);
             return;
         }
-        
+
         scrollValue = 0;
         if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Left.code)
             scrollValue = -40;
         else if (event.keyCode == WebInspector.KeyboardShortcut.Keys.Right.code)
             scrollValue = 40;
-        
+
         if (scrollValue) {
             event.preventDefault();
             event.stopPropagation();
@@ -252,42 +246,25 @@ WebInspector.TextViewer.prototype = {
         }
     },
 
-    _handleDoubleClick: function(e)
+    editLine: function(lineRow, callback)
     {
-        if (!this._editCallback)
-            return;
-
-        var cell = e.target.enclosingNodeOrSelfWithNodeName("TD");
-        if (!cell)
-            return;
-
-        var lineRow = cell.parentElement;
-        if (lineRow.firstChild === cell)
-            return;  // Do not trigger editing from line numbers.
-
-        var oldContent = lineRow.lastChild.innerHTML;
-        var cancelEditingCallback = this._cancelEditingLine.bind(this, lineRow.lastChild, oldContent);
-        var commitEditingCallback = this._commitEditingLine.bind(this, lineRow.lineNumber, lineRow.lastChild, cancelEditingCallback);
-        this._editingLine = WebInspector.startEditing(lineRow.lastChild, {
+        var element = lineRow.lastChild;
+        var oldContent = element.innerHTML;
+        function finishEditing(committed, e, newContent)
+        {
+            if (committed)
+                callback(newContent);
+            element.innerHTML = oldContent;
+            delete this._editingLine;
+        }
+        this._editingLine = WebInspector.startEditing(element, {
             context: null,
-            commitHandler: commitEditingCallback,
-            cancelHandler: cancelEditingCallback,
+            commitHandler: finishEditing.bind(this, true),
+            cancelHandler: finishEditing.bind(this, false),
             multiline: true
         });
     },
 
-    _commitEditingLine: function(lineNumber, element, cancelEditingCallback)
-    {
-        this._editCallback(lineNumber, element.textContent, cancelEditingCallback);
-        delete this._editingLine;
-    },
-
-    _cancelEditingLine: function(element, oldContent, e)
-    {
-        element.innerHTML = oldContent;
-        delete this._editingLine;
-    },
-
     _beforeCopy: function(e)
     {
         e.preventDefault();
@@ -786,7 +763,7 @@ WebInspector.TextChunk.prototype = {
 
             var lineContentElement = document.createElement("td");
             lineContentElement.className = "webkit-line-content";
-            lineRow.appendChild(lineContentElement);        
+            lineRow.appendChild(lineContentElement);
         }
         lineRow.lineNumber = lineNumber;
         lineNumberElement.textContent = lineNumber + 1;