Factored most of SourceView out into SourceFrame so it can be shared
authortimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 May 2008 22:30:05 +0000 (22:30 +0000)
committertimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 May 2008 22:30:05 +0000 (22:30 +0000)
by the new ScriptView. Added the ScriptView class to be used for
scripts that arn't Resources (like eval code.) Added a simple Script
object that hold the data from the debugger parsedSource hooks. A
ScriptView holds a Script object, and uses it for source data.

Added breakpoint and execution line support to the SourceFrame
where they are visually represented in the source.

Reviewed by Kevin McCullough.

* page/inspector/inspector.js:
(WebInspector.performSearch): Change the caller of sourceFrameForResource
to use the SourceFrame result's element property.
* page/inspector/ResourcesPanel.js: Use the new SourceFrame.
* page/inspector/Script.js: Added.
* page/inspector/ScriptView.js: Added.
* page/inspector/SourceFrame.js: Added.
* page/inspector/SourceView.js: Use the new SourceFrame.
* WebCore.vcproj/WebCore.vcproj: Add new files.
* page/inspector/WebKit.qrc: Ditto.
* page/inspector/inspector.html: Ditto.

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

WebCore/ChangeLog
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/page/inspector/ResourcesPanel.js
WebCore/page/inspector/Script.js [new file with mode: 0644]
WebCore/page/inspector/ScriptView.js [new file with mode: 0644]
WebCore/page/inspector/SourceFrame.js [new file with mode: 0644]
WebCore/page/inspector/SourceView.js
WebCore/page/inspector/WebKit.qrc
WebCore/page/inspector/inspector.html
WebCore/page/inspector/inspector.js

index 9ef0a77..f41093e 100644 (file)
@@ -1,5 +1,30 @@
 2008-05-13  Timothy Hatcher  <timothy@apple.com>
 
+        Factored most of SourceView out into SourceFrame so it can be shared
+        by the new ScriptView. Added the ScriptView class to be used for
+        scripts that arn't Resources (like eval code.) Added a simple Script
+        object that hold the data from the debugger parsedSource hooks. A
+        ScriptView holds a Script object, and uses it for source data.
+
+        Added breakpoint and execution line support to the SourceFrame
+        where they are visually represented in the source.
+
+        Reviewed by Kevin McCullough.
+
+        * page/inspector/inspector.js:
+        (WebInspector.performSearch): Change the caller of sourceFrameForResource
+        to use the SourceFrame result's element property.
+        * page/inspector/ResourcesPanel.js: Use the new SourceFrame.
+        * page/inspector/Script.js: Added.
+        * page/inspector/ScriptView.js: Added.
+        * page/inspector/SourceFrame.js: Added.
+        * page/inspector/SourceView.js: Use the new SourceFrame.
+        * WebCore.vcproj/WebCore.vcproj: Add new files.
+        * page/inspector/WebKit.qrc: Ditto.
+        * page/inspector/inspector.html: Ditto.
+
+2008-05-13  Timothy Hatcher  <timothy@apple.com>
+
         Adds a Breakpoint object and basic add/remove functions on
         BreakpointsSidebarPane that call the InspectorController to
         add/remove the breakpoint.
index 6924742..b939bd7 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\page\inspector\Script.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath="..\page\inspector\ScriptView.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath="..\page\inspector\SourceFrame.js"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\page\inspector\StylesSidebarPane.js"\r
                                        >\r
                                </File>\r
index 3473891..81311fc 100644 (file)
@@ -314,7 +314,7 @@ WebInspector.ResourcesPanel.prototype = {
 
         resource._resourcesTreeElement.updateErrorsAndWarnings();
 
-        var view = this._resourceView(resource);
+        var view = this.resourceViewForResource(resource);
         if (view.addMessage)
             view.addMessage(msg);
     },
@@ -388,15 +388,15 @@ WebInspector.ResourcesPanel.prototype = {
 
         resource._resourcesTreeElement.updateErrorsAndWarnings();
 
+        var oldView = resource._resourcesView;
+
         resource._resourcesView.detach();
         delete resource._resourcesView;
 
         resource._resourcesView = newView;
 
-        if (resource !== this.visibleResource)
-            return;
-
-        newView.show();
+        if (oldView.visible && oldView.element.parentNode)
+            newView.show(oldView.element.parentNode);
     },
 
     showResource: function(resource, line)
@@ -409,11 +409,11 @@ WebInspector.ResourcesPanel.prototype = {
         if (this.visibleResource && this.visibleResource._resourcesView)
             this.visibleResource._resourcesView.hide();
 
-        var view = this._resourceView(resource);
-        view.show();
+        var view = this.resourceViewForResource(resource);
+        view.show(this.resourceViews);
 
-        if (line && view.showLine)
-            view.showLine(line);
+        if (line && view.revealLine)
+            view.revealLine(line);
 
         if (resource._resourcesTreeElement) {
             resource._resourcesTreeElement.reveal();
@@ -440,9 +440,18 @@ WebInspector.ResourcesPanel.prototype = {
         this._updateSidebarWidth();
     },
 
+    resourceViewForResource: function(resource)
+    {
+        if (!resource)
+            return null;
+        if (!resource._resourcesView)
+            resource._resourcesView = this._createResourceView(resource);
+        return resource._resourcesView;
+    },
+
     sourceFrameForResource: function(resource)
     {
-        var view = this._resourceView(resource);
+        var view = this.resourceViewForResource(resource);
         if (!view)
             return null;
 
@@ -454,7 +463,7 @@ WebInspector.ResourcesPanel.prototype = {
             this.attach();
 
         view.setupSourceFrameIfNeeded();
-        return view.frameElement;
+        return view.sourceFrame;
     },
 
     handleKeyEvent: function(event)
@@ -969,15 +978,6 @@ WebInspector.ResourcesPanel.prototype = {
         this.sortingFunction = selectedOption.sortingFunction;
     },
 
-    _resourceView: function(resource)
-    {
-        if (!resource)
-            return null;
-        if (!resource._resourcesView)
-            resource._resourcesView = this._createResourceView(resource);
-        return resource._resourcesView;
-    },
-
     _createResourceView: function(resource)
     {
         switch (resource.category) {
diff --git a/WebCore/page/inspector/Script.js b/WebCore/page/inspector/Script.js
new file mode 100644 (file)
index 0000000..46502a6
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.Script = function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage)
+{
+    this.sourceID = sourceID;
+    this.sourceURL = sourceURL;
+    this.source = source;
+    this.startingLine = startingLine;
+    this.errorLine = errorLine;
+    this.errorMessage = errorMessage;
+}
+
+WebInspector.Script.prototype = {
+}
diff --git a/WebCore/page/inspector/ScriptView.js b/WebCore/page/inspector/ScriptView.js
new file mode 100644 (file)
index 0000000..dbadf75
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ScriptView = function(script)
+{
+    WebInspector.View.call(this);
+
+    this.element.addStyleClass("script-view");
+
+    this.script = script;
+
+    this._frameNeedsSetup = true;
+
+    this.sourceFrame = new WebInspector.SourceFrame();
+
+    this.element.appendChild(this.sourceFrame.element);
+}
+
+WebInspector.ScriptView.prototype = {
+    show: function(parentElement)
+    {
+        WebInspector.View.prototype.show.call(this, parentElement);
+        this.setupSourceFrameIfNeeded();
+    },
+
+    setupSourceFrameIfNeeded: function()
+    {
+        if (!("_frameNeedsSetup" in this))
+            return;
+
+        delete this._frameNeedsSetup;
+
+        this.attach();
+
+        InspectorController.addSourceToFrame("text/javascript", this.script.source, this.sourceFrame.element);
+    },
+
+    revealLine: function(lineNumber)
+    {
+        this.setupSourceFrameIfNeeded();
+        this.sourceFrame.revealLine(lineNumber);
+    },
+
+    addMessage: function(msg)
+    {
+        this.sourceFrame.addMessage(msg);
+    },
+
+    clearMessages: function()
+    {
+        this.sourceFrame.clearMessages();
+    },
+
+    attach: function()
+    {
+        if (!this.element.parentNode)
+            document.getElementById("script-resource-views").appendChild(this.element);
+    }
+}
+
+WebInspector.ScriptView.prototype.__proto__ = WebInspector.View.prototype;
diff --git a/WebCore/page/inspector/SourceFrame.js b/WebCore/page/inspector/SourceFrame.js
new file mode 100644 (file)
index 0000000..b2f2278
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.SourceFrame = function(element, addBreakpointDelegate)
+{
+    this.messages = [];
+    this.breakpoints = [];
+
+    this.addBreakpointDelegate = addBreakpointDelegate;
+
+    this.element = element || document.createElement("iframe");
+    this.element.addStyleClass("source-view-frame");
+    this.element.setAttribute("viewsource", "true");
+
+    this.element.addEventListener("load", this._loaded.bind(this), false);
+}
+
+WebInspector.SourceFrame.prototype = {
+    get executionLine()
+    {
+        return this._executionLine;
+    },
+
+    set executionLine(x)
+    {
+        if (this._executionLine === x)
+            return;
+
+        var previousLine = this._executionLine;
+        this._executionLine = x;
+
+        this._updateExecutionLine(previousLine);
+    },
+
+    sourceRow: function(lineNumber)
+    {
+        if (!lineNumber || !this.element.contentDocument)
+            return;
+
+        var table = this.element.contentDocument.getElementsByTagName("table")[0];
+        if (!table)
+            return;
+
+        var rows = table.rows;
+
+        // Line numbers are a 1-based index, but the rows collection is 0-based.
+        --lineNumber;
+        if (lineNumber >= rows.length)
+            lineNumber = rows.length - 1;
+
+        return rows[lineNumber];
+    },
+
+    lineNumberForSourceRow: function(sourceRow)
+    {
+        // Line numbers are a 1-based index, but the rows collection is 0-based.
+        var lineNumber = 0;
+        while (sourceRow) {
+            ++lineNumber;
+            sourceRow = sourceRow.previousSibling;
+        }
+
+        return lineNumber;
+    },
+
+    revealLine: function(lineNumber)
+    {
+        var row = this.sourceRow(lineNumber);
+        if (row)
+            row.scrollIntoViewIfNeeded(true);
+    },
+
+    addBreakpoint: function(breakpoint)
+    {
+        this.breakpoints.push(breakpoint);
+        breakpoint.addEventListener("enabled", this._breakpointEnableChanged, this);
+        breakpoint.addEventListener("disabled", this._breakpointEnableChanged, this);
+        this._addBreakpointToSource(breakpoint);
+    },
+
+    removeBreakpoint: function(breakpoint)
+    {
+        this.breakpoints.remove(breakpoint);
+        breakpoint.removeEventListener("enabled", null, this);
+        breakpoint.removeEventListener("disabled", null, this);
+        this._removeBreakpointFromSource(breakpoint);
+    },
+
+    addMessage: function(msg)
+    {
+        this.messages.push(msg);
+        this._addMessageToSource(msg);
+    },
+
+    clearMessages: function()
+    {
+        this.messages = [];
+
+        if (!this.element.contentDocument)
+            return;
+
+        var bubbles = this.element.contentDocument.querySelectorAll(".webkit-html-message-bubble");
+        if (!bubbles)
+            return;
+
+        for (var i = 0; i < bubbles.length; ++i) {
+            var bubble = bubbles[i];
+            bubble.parentNode.removeChild(bubble);
+        }
+    },
+
+    _loaded: function()
+    {
+        WebInspector.addMainEventListeners(this.element.contentDocument);
+        this.element.contentDocument.addEventListener("mousedown", this._documentMouseDown.bind(this), true);
+
+        var headElement = this.element.contentDocument.getElementsByTagName("head")[0];
+        if (!headElement) {
+            headElement = this.element.contentDocument.createElement("head");
+            this.element.contentDocument.documentElement.insertBefore(headElement, this.element.contentDocument.documentElement.firstChild);
+        }
+
+        var styleElement = this.element.contentDocument.createElement("style");
+        headElement.appendChild(styleElement);
+
+        // Add these style rules here since they are specific to the Inspector. They also behave oddly and not
+        // all properties apply if added to view-source.css (becuase it is a user agent sheet.)
+        var styleText = ".webkit-line-number { background-repeat: no-repeat; background-position: right 1px; }\n";
+        styleText += ".webkit-breakpoint .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint); }\n";
+        styleText += ".webkit-breakpoint-disabled .webkit-line-number { color: white; background-image: -webkit-canvas(breakpoint-disabled); }\n";
+        styleText += ".webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(program-counter); }\n";
+        styleText += ".webkit-breakpoint.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-program-counter); }\n";
+        styleText += ".webkit-breakpoint-disabled.webkit-execution-line .webkit-line-number { color: transparent; background-image: -webkit-canvas(breakpoint-disabled-program-counter); }\n";
+        styleText += ".webkit-execution-line .webkit-line-content { background-color: rgb(171, 191, 254); outline: 1px solid rgb(64, 115, 244); }\n";
+
+        styleElement.textContent = styleText;
+
+        this._needsProgramCounterImage = true;
+        this._needsBreakpointImages = true;
+
+        this.element.contentWindow.Element.prototype.addStyleClass = Element.prototype.addStyleClass;
+        this.element.contentWindow.Element.prototype.removeStyleClass = Element.prototype.removeStyleClass;
+        this.element.contentWindow.Element.prototype.hasStyleClass = Element.prototype.hasStyleClass;
+        this.element.contentWindow.Node.prototype.enclosingNodeOrSelfWithNodeName = Node.prototype.enclosingNodeOrSelfWithNodeName;
+
+        this._addExistingMessagesToSource();
+        this._addExistingBreakpointsToSource();
+        this._updateExecutionLine();
+    },
+
+    _documentMouseDown: function(event)
+    {
+        if (!event.target.hasStyleClass("webkit-line-number"))
+            return;
+
+        var sourceRow = event.target.enclosingNodeOrSelfWithNodeName("tr");
+        if (sourceRow._breakpointObject)
+            sourceRow._breakpointObject.enabled = !sourceRow._breakpointObject.enabled;
+        else if (this.addBreakpointDelegate)
+            this.addBreakpointDelegate(this.lineNumberForSourceRow(sourceRow));
+    },
+
+    _breakpointEnableChanged: function(event)
+    {
+        var breakpoint = event.target;
+        var sourceRow = this.sourceRow(breakpoint.line);
+        if (!sourceRow)
+            return;
+
+        sourceRow.addStyleClass("webkit-breakpoint");
+
+        if (breakpoint.enabled)
+            sourceRow.removeStyleClass("webkit-breakpoint-disabled");
+        else
+            sourceRow.addStyleClass("webkit-breakpoint-disabled");
+    },
+
+    _updateExecutionLine: function(previousLine)
+    {
+        if (previousLine) {
+            var sourceRow = this.sourceRow(previousLine);
+            if (sourceRow)
+                sourceRow.removeStyleClass("webkit-execution-line");
+        }
+
+        if (!this._executionLine)
+            return;
+
+        this._drawProgramCounterImageIfNeeded();
+
+        var sourceRow = this.sourceRow(this._executionLine);
+        if (sourceRow)
+            sourceRow.addStyleClass("webkit-execution-line");
+    },
+
+    _addExistingBreakpointsToSource: function()
+    {
+        var length = this.breakpoints.length;
+        for (var i = 0; i < length; ++i)
+            this._addBreakpointToSource(this.breakpoints[i]);
+    },
+
+    _addBreakpointToSource: function(breakpoint)
+    {
+        var sourceRow = this.sourceRow(breakpoint.line);
+        if (!sourceRow)
+            return;
+
+        this._drawBreakpointImagesIfNeeded();
+
+        sourceRow._breakpointObject = breakpoint;
+
+        sourceRow.addStyleClass("webkit-breakpoint");
+        if (!breakpoint.enabled)
+            sourceRow.addStyleClass("webkit-breakpoint-disabled");
+    },
+
+    _removeBreakpointFromSource: function(breakpoint)
+    {
+        var sourceRow = this.sourceRow(breakpoint.line);
+        if (!sourceRow)
+            return;
+
+        delete sourceRow._breakpointObject;
+
+        sourceRow.removeStyleClass("webkit-breakpoint");
+        sourceRow.removeStyleClass("webkit-breakpoint-disabled");
+    },
+
+    _addExistingMessagesToSource: function()
+    {
+        var length = this.messages.length;
+        for (var i = 0; i < length; ++i)
+            this._addMessageToSource(this.messages[i]);
+    },
+
+    _addMessageToSource: function(msg)
+    {
+        var row = this.sourceRow(msg.line);
+        if (!row)
+            return;
+
+        var cell = row.getElementsByTagName("td")[1];
+        if (!cell)
+            return;
+
+        var errorDiv = cell.lastChild;
+        if (!errorDiv || errorDiv.nodeName.toLowerCase() !== "div" || !errorDiv.hasStyleClass("webkit-html-message-bubble")) {
+            errorDiv = this.element.contentDocument.createElement("div");
+            errorDiv.className = "webkit-html-message-bubble";
+            cell.appendChild(errorDiv);
+        }
+
+        var imageURL;
+        switch (msg.level) {
+            case WebInspector.ConsoleMessage.MessageLevel.Error:
+                errorDiv.addStyleClass("webkit-html-error-message");
+                imageURL = "Images/errorIcon.png";
+                break;
+            case WebInspector.ConsoleMessage.MessageLevel.Warning:
+                errorDiv.addStyleClass("webkit-html-warning-message");
+                imageURL = "Images/warningIcon.png";
+                break;
+        }
+
+        var lineDiv = this.element.contentDocument.createElement("div");
+        lineDiv.className = "webkit-html-message-line";
+        errorDiv.appendChild(lineDiv);
+
+        // Create the image element in the Inspector's document so we can use relative image URLs.
+        var image = document.createElement("img");
+        image.src = imageURL;
+        image.className = "webkit-html-message-icon";
+
+        // Adopt the image element since it wasn't created in element's contentDocument.
+        image = this.element.contentDocument.adoptNode(image);
+        lineDiv.appendChild(image);
+
+        lineDiv.appendChild(this.element.contentDocument.createTextNode(msg.message));
+    },
+
+    _drawProgramCounterInContext: function(ctx, glow)
+    {
+        if (glow)
+            ctx.save();
+
+        ctx.beginPath();
+        ctx.moveTo(17, 2);
+        ctx.lineTo(19, 2);
+        ctx.lineTo(19, 0);
+        ctx.lineTo(21, 0);
+        ctx.lineTo(26, 5.5);
+        ctx.lineTo(21, 11);
+        ctx.lineTo(19, 11);
+        ctx.lineTo(19, 9);
+        ctx.lineTo(17, 9);
+        ctx.closePath();
+        ctx.fillStyle = "rgb(142, 5, 4)";
+
+        if (glow) {
+            ctx.shadowBlur = 4;
+            ctx.shadowColor = "rgb(255, 255, 255)";
+            ctx.shadowOffsetX = -1;
+            ctx.shadowOffsetY = 0;
+        }
+
+        ctx.fill();
+        ctx.fill(); // Fill twice to get a good shadow and darker anti-aliased pixels.
+
+        if (glow)
+            ctx.restore();
+    },
+
+    _drawProgramCounterImageIfNeeded: function()
+    {
+        if (!this._needsProgramCounterImage || !this.element.contentDocument)
+            return;
+
+        var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "program-counter", 26, 11);
+        ctx.clearRect(0, 0, 26, 11);
+        this._drawProgramCounterInContext(ctx, true);
+
+        delete this._needsProgramCounterImage;
+    },
+
+    _drawBreakpointImagesIfNeeded: function()
+    {
+        if (!this._needsBreakpointImages || !this.element.contentDocument)
+            return;
+
+        function drawBreakpoint(ctx, disabled)
+        {
+            ctx.beginPath();
+            ctx.moveTo(0, 2);
+            ctx.lineTo(2, 0);
+            ctx.lineTo(21, 0);
+            ctx.lineTo(26, 5.5);
+            ctx.lineTo(21, 11);
+            ctx.lineTo(2, 11);
+            ctx.lineTo(0, 9);
+            ctx.closePath();
+            ctx.fillStyle = "rgb(1, 142, 217)";
+            ctx.strokeStyle = "rgb(0, 103, 205)";
+            ctx.lineWidth = 3;
+            ctx.fill();
+            ctx.save();
+            ctx.clip();
+            ctx.stroke();
+            ctx.restore();
+
+            if (!disabled)
+                return;
+
+            ctx.save();
+            ctx.globalCompositeOperation = "destination-out";
+            ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
+            ctx.fillRect(0, 0, 26, 11);
+            ctx.restore();
+        }
+
+        var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint", 26, 11);
+        ctx.clearRect(0, 0, 26, 11);
+        drawBreakpoint(ctx);
+
+        var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-program-counter", 26, 11);
+        ctx.clearRect(0, 0, 26, 11);
+        drawBreakpoint(ctx);
+        ctx.clearRect(20, 0, 6, 11);
+        this._drawProgramCounterInContext(ctx, true);
+
+        var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled", 26, 11);
+        ctx.clearRect(0, 0, 26, 11);
+        drawBreakpoint(ctx, true);
+
+        var ctx = this.element.contentDocument.getCSSCanvasContext("2d", "breakpoint-disabled-program-counter", 26, 11);
+        ctx.clearRect(0, 0, 26, 11);
+        drawBreakpoint(ctx, true);
+        ctx.clearRect(20, 0, 6, 11);
+        this._drawProgramCounterInContext(ctx, true);
+
+        delete this._needsBreakpointImages;
+    }
+}
index 1d28ea9..fa85148 100644 (file)
@@ -32,13 +32,11 @@ WebInspector.SourceView = function(resource)
 
     this.element.addStyleClass("source");
 
-    this.messages = [];
     this._frameNeedsSetup = true;
 
-    this.frameElement = document.createElement("iframe");
-    this.frameElement.className = "source-view-frame";
-    this.frameElement.setAttribute("viewsource", "true");
-    this.contentElement.appendChild(this.frameElement);
+    this.sourceFrame = new WebInspector.SourceFrame();
+
+    this.contentElement.appendChild(this.sourceFrame.element);
 }
 
 WebInspector.SourceView.prototype = {
@@ -55,107 +53,32 @@ WebInspector.SourceView.prototype = {
 
             this.attach();
 
-            InspectorController.addResourceSourceToFrame(this.resource.identifier, this.frameElement);
-            WebInspector.addMainEventListeners(this.frameElement.contentDocument);
-
-            var length = this.messages.length;
-            for (var i = 0; i < length; ++i)
-                this._addMessageToSource(this.messages[i]);
+            InspectorController.addResourceSourceToFrame(this.resource.identifier, this.sourceFrame.element);
         }
     },
 
-    sourceRow: function(lineNumber)
+    revealLine: function(lineNumber)
     {
-        if (!lineNumber)
-            return;
-
         this.setupSourceFrameIfNeeded();
-
-        var doc = this.frameElement.contentDocument;
-        var rows = doc.getElementsByTagName("table")[0].rows;
-
-        // Line numbers are a 1-based index, but the rows collection is 0-based.
-        --lineNumber;
-        if (lineNumber >= rows.length)
-            lineNumber = rows.length - 1;
-
-        return rows[lineNumber];
-    },
-
-    showLine: function(lineNumber)
-    {
-        var row = this.sourceRow(lineNumber);
-        if (!row)
-            return;
-        row.scrollIntoViewIfNeeded(true);
+        this.sourceFrame.revealLine(lineNumber);
     },
 
     addMessage: function(msg)
     {
-        this.messages.push(msg);
-        if (!this._frameNeedsSetup)
-            this._addMessageToSource(msg);
+        this.sourceFrame.addMessage(msg);
     },
 
     clearMessages: function()
     {
-        this.messages = [];
-
-        if (this._frameNeedsSetup)
-            return;
-
-        var bubbles = this.frameElement.contentDocument.querySelectorAll(".webkit-html-message-bubble");
-        if (!bubbles)
-            return;
-
-        for (var i = 0; i < bubbles.length; ++i) {
-            var bubble = bubbles[i];
-            bubble.parentNode.removeChild(bubble);
-        }
+        this.sourceFrame.clearMessages();
     },
 
-    _addMessageToSource: function(msg)
+    detach: function()
     {
-        var row = this.sourceRow(msg.line);
-        if (!row)
-            return;
-
-        var doc = this.frameElement.contentDocument;
-        var cell = row.getElementsByTagName("td")[1];
-
-        var errorDiv = cell.lastChild;
-        if (!errorDiv || errorDiv.nodeName.toLowerCase() !== "div" || !errorDiv.hasStyleClass("webkit-html-message-bubble")) {
-            errorDiv = doc.createElement("div");
-            errorDiv.className = "webkit-html-message-bubble";
-            cell.appendChild(errorDiv);
-        }
-
-        var imageURL;
-        switch (msg.level) {
-            case WebInspector.ConsoleMessage.MessageLevel.Error:
-                errorDiv.addStyleClass("webkit-html-error-message");
-                imageURL = "Images/errorIcon.png";
-                break;
-            case WebInspector.ConsoleMessage.MessageLevel.Warning:
-                errorDiv.addStyleClass("webkit-html-warning-message");
-                imageURL = "Images/warningIcon.png";
-                break;
-        }
-
-        var lineDiv = doc.createElement("div");
-        lineDiv.className = "webkit-html-message-line";
-        errorDiv.appendChild(lineDiv);
-
-        // Create the image element in the Inspector's document so we can use relative image URLs.
-        var image = document.createElement("img");
-        image.src = imageURL;
-        image.className = "webkit-html-message-icon";
-
-        // Adopt the image element since it wasn't created in doc.
-        image = doc.adoptNode(image);
-        lineDiv.appendChild(image);
-
-        lineDiv.appendChild(doc.createTextNode(msg.message));
+        // FIXME: We need to mark the frame for setup on detach because the frame DOM is cleared
+        // when it is removed from the document. Is this a bug?
+        WebInspector.ResourceView.prototype.detach.call(this);
+        this._frameNeedsSetup = true;
     }
 }
 
index a112468..25c1759 100644 (file)
@@ -28,6 +28,9 @@
     <file>SidebarPane.js</file>
     <file>SidebarTreeElement.js</file>
     <file>SourceView.js</file>
+    <file>Script.js</file>
+    <file>ScriptView.js</file>
+    <file>SourceFrame.js</file>
     <file>StylesSidebarPane.js</file>
     <file>TextPrompt.js</file>
     <file>treeoutline.js</file>
index ffd466f..2c008f7 100644 (file)
@@ -40,6 +40,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="Resource.js"></script>
     <script type="text/javascript" src="ResourceCategory.js"></script>
     <script type="text/javascript" src="Database.js"></script>
+    <script type="text/javascript" src="Script.js"></script>
     <script type="text/javascript" src="Breakpoint.js"></script>
     <script type="text/javascript" src="SidebarPane.js"></script>
     <script type="text/javascript" src="SidebarTreeElement.js"></script>
@@ -57,11 +58,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     <script type="text/javascript" src="DatabasesPanel.js"></script>
     <script type="text/javascript" src="ProfilesPanel.js"></script>
     <script type="text/javascript" src="ResourceView.js"></script>
+    <script type="text/javascript" src="SourceFrame.js"></script>
     <script type="text/javascript" src="SourceView.js"></script>
     <script type="text/javascript" src="FontView.js"></script>
     <script type="text/javascript" src="ImageView.js"></script>
     <script type="text/javascript" src="DatabaseTableView.js"></script>
     <script type="text/javascript" src="DatabaseQueryView.js"></script>
+    <script type="text/javascript" src="ScriptView.js"></script>
     <script type="text/javascript" src="ProfileView.js"></script>
 </head>
 <body class="detached">
index ec6eeba..5b81ab4 100644 (file)
@@ -766,7 +766,7 @@ WebInspector.performSearch = function(query)
         if (!isXPath) {
             var sourceFrame = this.panels.resources.sourceFrameForResource(resource);
             if (sourceFrame)
-                sourceResults = InspectorController.search(sourceFrame.contentDocument, query);
+                sourceResults = InspectorController.search(sourceFrame.element.contentDocument, query);
         }
 
         var domResults = [];