Web Inspector: debuggerPresentatioModel.linkifyLocation leaks updateAnchor closure...
authorloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 5 Oct 2011 11:41:07 +0000 (11:41 +0000)
committerloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 5 Oct 2011 11:41:07 +0000 (11:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=69146

In many places we use linkifyLocation function to produce a link node which updates automatically when the source file is changed on the fly.
Such changes happen when we use pretty print or another operation that changes the source code somehow.
linkifyLocation associates a new instance of updateAnchor closure with the each link node and add the closure to the SourceMappingUpdated event's list.
As the result the node<->closure pairs wouldn't be collected until reset the UI and DebuggerPresentationModel.

Reviewed by Pavel Feldman.

Source/WebCore:

Test: inspector/debugger/linkifier.html

* inspector/front-end/ConsoleMessage.js:
(WebInspector.ConsoleMessageImpl):
(WebInspector.ConsoleMessageImpl.prototype._linkifyLocation):
* inspector/front-end/ConsoleView.js:
(WebInspector.ConsoleView.prototype._consoleCleared):
(WebInspector.ConsoleCommandResult):
(WebInspector.ConsoleMessage.create):
(WebInspector.ConsoleMessage.createTextMessage):
* inspector/front-end/DebuggerPresentationModel.js:
(WebInspector.DebuggerPresentationModel.prototype.createLinkifier):
(WebInspector.DebuggerPresentationModel.Linkifier):
(WebInspector.DebuggerPresentationModel.Linkifier.prototype.linkifyLocation):
(WebInspector.DebuggerPresentationModel.Linkifier.prototype.reset):
(WebInspector.DebuggerPresentationModel.Linkifier.prototype._updateSourceAnchors):
(WebInspector.DebuggerPresentationModel.Linkifier.prototype._updateAnchor):
* inspector/front-end/EventListenersSidebarPane.js:
(WebInspector.EventListenersSidebarPane.prototype.update.callback):
(WebInspector.EventListenersSidebarPane.prototype.update):
():
* inspector/front-end/NetworkPanel.js:
(WebInspector.NetworkLogView):
(WebInspector.NetworkLogView.prototype._reset):
(WebInspector.NetworkDataGridNode.prototype._refreshInitiatorCell):
* inspector/front-end/ProfileDataGridTree.js:
(WebInspector.ProfileDataGridNode.prototype.createCell):
* inspector/front-end/ProfileView.js:
(WebInspector.CPUProfileView):
(WebInspector.CPUProfileView.prototype._resetClicked):
* inspector/front-end/TimelinePanel.js:
(WebInspector.TimelinePanel):
(WebInspector.TimelinePanel.prototype._linkifyLocation):
(WebInspector.TimelinePanel.prototype._linkifyCallFrame):
(WebInspector.TimelinePanel.prototype._clearPanel):
(WebInspector.TimelinePanel.FormattedRecord):
(WebInspector.TimelinePanel.FormattedRecord.prototype._generatePopupContent):
(WebInspector.TimelinePanel.FormattedRecord.prototype._getRecordDetails):
(WebInspector.TimelinePanel.PopupContentHelper):
(WebInspector.TimelinePanel.PopupContentHelper.prototype._appendLinkRow):
(WebInspector.TimelinePanel.PopupContentHelper.prototype._appendStackTrace):
* inspector/front-end/inspector.js:

LayoutTests:

* inspector/debugger/linkifier-expected.txt: Added.
* inspector/debugger/linkifier.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/debugger/linkifier-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/linkifier.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/ConsoleMessage.js
Source/WebCore/inspector/front-end/ConsoleView.js
Source/WebCore/inspector/front-end/DebuggerPresentationModel.js
Source/WebCore/inspector/front-end/EventListenersSidebarPane.js
Source/WebCore/inspector/front-end/NetworkPanel.js
Source/WebCore/inspector/front-end/ProfileDataGridTree.js
Source/WebCore/inspector/front-end/ProfileView.js
Source/WebCore/inspector/front-end/TimelinePanel.js
Source/WebCore/inspector/front-end/inspector.js

index 7bd1975385f518011bd011cea57789bef4530b17..e9c1a7d73736d04bdf9b7b8dd8963624d9b80b92 100644 (file)
@@ -1,3 +1,18 @@
+2011-10-04  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: debuggerPresentatioModel.linkifyLocation leaks updateAnchor closure instances.
+        https://bugs.webkit.org/show_bug.cgi?id=69146
+
+        In many places we use linkifyLocation function to produce a link node which updates automatically when the source file is changed on the fly.
+        Such changes happen when we use pretty print or another operation that changes the source code somehow.
+        linkifyLocation associates a new instance of updateAnchor closure with the each link node and add the closure to the SourceMappingUpdated event's list.
+        As the result the node<->closure pairs wouldn't be collected until reset the UI and DebuggerPresentationModel.
+
+        Reviewed by Pavel Feldman.
+
+        * inspector/debugger/linkifier-expected.txt: Added.
+        * inspector/debugger/linkifier.html: Added.
+
 2011-10-05  Hans Wennborg  <hans@chromium.org>
 
         Rebaseline fast/repaint/japanese-rl-selection-repaint-in-regions.html
diff --git a/LayoutTests/inspector/debugger/linkifier-expected.txt b/LayoutTests/inspector/debugger/linkifier-expected.txt
new file mode 100644 (file)
index 0000000..339d600
--- /dev/null
@@ -0,0 +1,28 @@
+Tests that Linkifier works correctly.
+
+Two addEventListener calls expected. The first for 'fakeUrl-1' the second for 'fakeUrl-2'.
+Two removeEventListener calls expected. The first for 'fakeUrl-1' the second for 'fakeUrl-2'.
+Debugger was enabled.
+--- linkify fakeURL-1 first time ---
+DummyPresentationModel._rawSourceCodeForScriptWithURL was called with arguments = ['fakeUrl-1']
+DummyRawSourceCode.addEventListener was called for source with URL = 'fakeUrl-1' with arguments = ['source-mapping-updated', function, object]
+
+--- linkify fakeURL-2 first time ---
+DummyPresentationModel._rawSourceCodeForScriptWithURL was called with arguments = ['fakeUrl-2']
+DummyRawSourceCode.addEventListener was called for source with URL = 'fakeUrl-2' with arguments = ['source-mapping-updated', function, object]
+
+--- linkify fakeURL-1 second time ---
+DummyPresentationModel._rawSourceCodeForScriptWithURL was called with arguments = ['fakeUrl-1']
+
+--- linkify fakeURL-2 second time ---
+DummyPresentationModel._rawSourceCodeForScriptWithURL was called with arguments = ['fakeUrl-2']
+
+--- reset ---
+DummyRawSourceCode.removeEventListener was called for source with id = 'fakeUrl-1' with arguments = ['source-mapping-updated', function, object]
+DummyRawSourceCode.removeEventListener was called for source with id = 'fakeUrl-2' with arguments = ['source-mapping-updated', function, object]
+
+--- location update ---
+original location: linkifier.html:93
+pretty printed location: linkifier.html:99
+reverted location: linkifier.html:93
+
diff --git a/LayoutTests/inspector/debugger/linkifier.html b/LayoutTests/inspector/debugger/linkifier.html
new file mode 100644 (file)
index 0000000..fb3541c
--- /dev/null
@@ -0,0 +1,118 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/debugger-test.js"></script>
+<script>
+
+function test()
+{
+    InspectorTest.startDebuggerTest(debuggerTest);
+
+    function debuggerTest()
+    {
+        if (!WebInspector.debuggerPresentationModel._rawSourceCode[WebInspector.mainResource._documentURL]) {
+            InspectorTest.addSniffer(WebInspector.debuggerPresentationModel, "_addScript", debuggerTest);
+            return;
+        }
+
+        var fakeURL1 = "fakeUrl-1";
+        var fakeURL2 = "fakeUrl-2";
+
+        DummyRawSourceCode = function(url)
+        {
+            this.url = url;
+            this.id = url;
+        }
+
+        DummyRawSourceCode.prototype = {
+            addEventListener: function(eventName, callback, object)
+            {
+                InspectorTest.addResult("DummyRawSourceCode.addEventListener was called for source with URL = '" + this.url + "' with arguments = ['" + eventName + "', " + (typeof callback) + ", " + (typeof object) + "]");
+            },
+
+            removeEventListener: function(eventName, callback, object)
+            {
+                InspectorTest.addResult("DummyRawSourceCode.removeEventListener was called for source with id = '" + this.url + "' with arguments = ['" + eventName + "', " + (typeof callback) + ", " + (typeof object) + "]");
+            }
+        }
+
+        DummyPresentationModel = function()
+        {
+            this._rawSourceCode = [];
+        }
+
+        DummyPresentationModel.prototype = {
+            _rawSourceCodeForScriptWithURL: function(sourceURL)
+            {
+                InspectorTest.addResult("DummyPresentationModel._rawSourceCodeForScriptWithURL was called with arguments = ['" + sourceURL + "']");
+                var rawSourceCode = this._rawSourceCode[sourceURL];
+                if (rawSourceCode)
+                    return rawSourceCode;
+                rawSourceCode = new DummyRawSourceCode(sourceURL);
+                this._rawSourceCode[sourceURL] = rawSourceCode;
+                return rawSourceCode;
+            }
+        }
+
+        var dummyPresentationModel = new DummyPresentationModel();
+
+        var linkifier = new WebInspector.DebuggerPresentationModel.Linkifier(dummyPresentationModel);
+
+        InspectorTest.addResult("--- linkify fakeURL-1 first time ---");
+        linkifier.linkifyLocation(fakeURL1, 42, 26, "fake-class-name");
+        InspectorTest.addResult("");
+
+        InspectorTest.addResult("--- linkify fakeURL-2 first time ---");
+        linkifier.linkifyLocation(fakeURL2, 42, 26, "fake-class-name");
+        InspectorTest.addResult("");
+
+        InspectorTest.addResult("--- linkify fakeURL-1 second time ---");
+        linkifier.linkifyLocation(fakeURL1, 97, 23, "fake-class-name");
+        InspectorTest.addResult("");
+
+        InspectorTest.addResult("--- linkify fakeURL-2 second time ---");
+        linkifier.linkifyLocation(fakeURL2, 4, 2, "fake-class-name");
+        InspectorTest.addResult("");
+
+        InspectorTest.addResult("--- reset ---");
+        linkifier.reset();
+        InspectorTest.addResult("");
+
+        InspectorTest.addResult("--- location update ---");
+
+        function dummyFunctionToBePrettyPrinted() { InspectorTest.addResult(""); console.error(""); }
+
+        linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
+        var link = linkifier.linkifyLocation(WebInspector.mainResource._documentURL, 92, 0, "dummy-class");
+        InspectorTest.addResult("original location: " + link.textContent);
+
+        InspectorTest.addSniffer(linkifier, "_updateAnchor", linkUpdated);
+        WebInspector.debuggerPresentationModel.setFormatSource(true);
+
+        function linkUpdated()
+        {
+            InspectorTest.addResult("pretty printed location: " + link.textContent);
+            InspectorTest.addSniffer(linkifier, "_updateAnchor", linkReverted);
+            WebInspector.debuggerPresentationModel.setFormatSource(false);
+        }
+
+        function linkReverted()
+        {
+            InspectorTest.addResult("reverted location: " + link.textContent);
+            InspectorTest.completeTest();
+        }
+    }
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that Linkifier works correctly.
+<p>
+Two addEventListener calls expected. The first for 'fakeUrl-1' the second for 'fakeUrl-2'.<br>
+Two removeEventListener calls expected. The first for 'fakeUrl-1' the second for 'fakeUrl-2'.<br>
+
+</body>
+</html>
index 2f71c7708cd3fb26a988b7ff41de35034f0c1612..c1ce3bf0bef0160700a3d54361b6eca97b7f1d2a 100644 (file)
@@ -1,3 +1,58 @@
+2011-10-04  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: debuggerPresentatioModel.linkifyLocation leaks updateAnchor closure instances.
+        https://bugs.webkit.org/show_bug.cgi?id=69146
+
+        In many places we use linkifyLocation function to produce a link node which updates automatically when the source file is changed on the fly.
+        Such changes happen when we use pretty print or another operation that changes the source code somehow.
+        linkifyLocation associates a new instance of updateAnchor closure with the each link node and add the closure to the SourceMappingUpdated event's list.
+        As the result the node<->closure pairs wouldn't be collected until reset the UI and DebuggerPresentationModel.
+
+        Reviewed by Pavel Feldman.
+
+        Test: inspector/debugger/linkifier.html
+
+        * inspector/front-end/ConsoleMessage.js:
+        (WebInspector.ConsoleMessageImpl):
+        (WebInspector.ConsoleMessageImpl.prototype._linkifyLocation):
+        * inspector/front-end/ConsoleView.js:
+        (WebInspector.ConsoleView.prototype._consoleCleared):
+        (WebInspector.ConsoleCommandResult):
+        (WebInspector.ConsoleMessage.create):
+        (WebInspector.ConsoleMessage.createTextMessage):
+        * inspector/front-end/DebuggerPresentationModel.js:
+        (WebInspector.DebuggerPresentationModel.prototype.createLinkifier):
+        (WebInspector.DebuggerPresentationModel.Linkifier):
+        (WebInspector.DebuggerPresentationModel.Linkifier.prototype.linkifyLocation):
+        (WebInspector.DebuggerPresentationModel.Linkifier.prototype.reset):
+        (WebInspector.DebuggerPresentationModel.Linkifier.prototype._updateSourceAnchors):
+        (WebInspector.DebuggerPresentationModel.Linkifier.prototype._updateAnchor):
+        * inspector/front-end/EventListenersSidebarPane.js:
+        (WebInspector.EventListenersSidebarPane.prototype.update.callback):
+        (WebInspector.EventListenersSidebarPane.prototype.update):
+        ():
+        * inspector/front-end/NetworkPanel.js:
+        (WebInspector.NetworkLogView):
+        (WebInspector.NetworkLogView.prototype._reset):
+        (WebInspector.NetworkDataGridNode.prototype._refreshInitiatorCell):
+        * inspector/front-end/ProfileDataGridTree.js:
+        (WebInspector.ProfileDataGridNode.prototype.createCell):
+        * inspector/front-end/ProfileView.js:
+        (WebInspector.CPUProfileView):
+        (WebInspector.CPUProfileView.prototype._resetClicked):
+        * inspector/front-end/TimelinePanel.js:
+        (WebInspector.TimelinePanel):
+        (WebInspector.TimelinePanel.prototype._linkifyLocation):
+        (WebInspector.TimelinePanel.prototype._linkifyCallFrame):
+        (WebInspector.TimelinePanel.prototype._clearPanel):
+        (WebInspector.TimelinePanel.FormattedRecord):
+        (WebInspector.TimelinePanel.FormattedRecord.prototype._generatePopupContent):
+        (WebInspector.TimelinePanel.FormattedRecord.prototype._getRecordDetails):
+        (WebInspector.TimelinePanel.PopupContentHelper):
+        (WebInspector.TimelinePanel.PopupContentHelper.prototype._appendLinkRow):
+        (WebInspector.TimelinePanel.PopupContentHelper.prototype._appendStackTrace):
+        * inspector/front-end/inspector.js:
+
 2011-10-03  Andreas Kling  <kling@webkit.org>
 
         REGRESSION(r82611) InlineBox has 33 bits of bitset, causing alignment issues and extra memory use.
index 3e7083f528eb51f61c30d8116a3dff64cbf162ce..d7a40c0d725bd45100f13f6527f06728447405eb 100644 (file)
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.ConsoleMessage.create = function(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request)
-{
-    return new WebInspector.ConsoleMessageImpl(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request);
-}
-
-WebInspector.ConsoleMessage.createTextMessage = function(text, level)
-{
-    level = level || WebInspector.ConsoleMessage.MessageLevel.Log;
-    return new WebInspector.ConsoleMessageImpl(WebInspector.ConsoleMessage.MessageSource.ConsoleAPI, WebInspector.ConsoleMessage.MessageType.Log, level, 0, null, 1, null, [text], null);
-}
-
 /**
  * @constructor
  * @extends {WebInspector.ConsoleMessage}
+ * @param {WebInspector.DebuggerPresentationModel.Linkifier} linkifier
  * @param {Array.<RuntimeAgent.RemoteObject>=} parameters
  * @param {ConsoleAgent.StackTrace=} stackTrace
  * @param {WebInspector.Resource=} request
  */
-WebInspector.ConsoleMessageImpl = function(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request)
+WebInspector.ConsoleMessageImpl = function(source, type, level, line, url, repeatCount, message, linkifier, parameters, stackTrace, request)
 {
     WebInspector.ConsoleMessage.call();
 
+    this._linkifier = linkifier;
     this.source = source;
     this.type = type;
     this.level = level;
@@ -194,7 +185,7 @@ WebInspector.ConsoleMessageImpl.prototype = {
         // FIXME(62725): stack trace line/column numbers are one-based.
         lineNumber = lineNumber ? lineNumber - 1 : undefined;
         columnNumber = columnNumber ? columnNumber - 1 : 0;
-        return WebInspector.debuggerPresentationModel.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
+        return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "console-message-url");
     },
 
     _linkifyCallFrame: function(callFrame)
index e412432cd9fdf39bdce74390f215a6cfe97ff6d4..3b11e735d5651a27464eb711763a39c90f07a7a2 100644 (file)
@@ -110,6 +110,8 @@ WebInspector.ConsoleView = function(hideContextSelector)
 
     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
+
+    this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
 }
 
 WebInspector.ConsoleView.Events = {
@@ -326,6 +328,8 @@ WebInspector.ConsoleView.prototype = {
         this.topGroup.messagesElement.removeChildren();
 
         this.dispatchEventToListeners(WebInspector.ConsoleView.Events.ConsoleCleared);
+
+        this._linkifier.reset();
     },
 
     completions: function(wordRange, bestMatchOnly, completionsReadyCallback)
@@ -617,7 +621,7 @@ WebInspector.ConsoleView.prototype = {
 
             WebInspector.settings.consoleHistory.set(this.prompt.history.slice(-30));
 
-            this._appendConsoleMessage(new WebInspector.ConsoleCommandResult(result, wasThrown, commandMessage));
+            this._appendConsoleMessage(new WebInspector.ConsoleCommandResult(result, wasThrown, commandMessage, this._linkifier));
         }
         this.evalInInspectedWindow(str, "console", true, undefined, undefined, printResult.bind(this));
 
@@ -694,11 +698,11 @@ WebInspector.ConsoleCommand.prototype = {
  * @extends {WebInspector.ConsoleMessageImpl}
  * @constructor
  */
-WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand)
+WebInspector.ConsoleCommandResult = function(result, wasThrown, originatingCommand, linkifier)
 {
     var level = (wasThrown ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log);
     this.originatingCommand = originatingCommand;
-    WebInspector.ConsoleMessageImpl.call(this, WebInspector.ConsoleMessage.MessageSource.JS, WebInspector.ConsoleMessage.MessageType.Result, level, -1, null, 1, null, [result]);
+    WebInspector.ConsoleMessageImpl.call(this, WebInspector.ConsoleMessage.MessageSource.JS, WebInspector.ConsoleMessage.MessageType.Result, level, -1, null, 1, null, linkifier, [result]);
 }
 
 WebInspector.ConsoleCommandResult.prototype = {
@@ -776,3 +780,14 @@ WebInspector.ConsoleGroup.prototype = {
  * @type {?WebInspector.ConsoleView}
  */
 WebInspector.consoleView = null;
+
+WebInspector.ConsoleMessage.create = function(source, type, level, line, url, repeatCount, message, parameters, stackTrace, request)
+{
+    return new WebInspector.ConsoleMessageImpl(source, type, level, line, url, repeatCount, message, WebInspector.consoleView._linkifier, parameters, stackTrace, request);
+}
+
+WebInspector.ConsoleMessage.createTextMessage = function(text, level)
+{
+    level = level || WebInspector.ConsoleMessage.MessageLevel.Log;
+    return new WebInspector.ConsoleMessageImpl(WebInspector.ConsoleMessage.MessageSource.ConsoleAPI, WebInspector.ConsoleMessage.MessageType.Log, level, 0, null, 1, null, WebInspector.consoleView._linkifier, [text], null);
+}
index dfe0101356be216fe69f18c5e5d2eadb8ba36624..25302d0a01bdcbc8e7146fc168ac2b14101552f9 100644 (file)
@@ -67,30 +67,9 @@ WebInspector.DebuggerPresentationModel.Events = {
 }
 
 WebInspector.DebuggerPresentationModel.prototype = {
-    linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
+    createLinkifier: function()
     {
-        var linkText = WebInspector.formatLinkText(sourceURL, lineNumber);
-        var anchor = WebInspector.linkifyURLAsNode(sourceURL, linkText, classes, false);
-
-        var rawSourceCode = this._rawSourceCodeForScriptWithURL(sourceURL);
-        if (!rawSourceCode) {
-            anchor.setAttribute("preferred_panel", "resources");
-            anchor.setAttribute("line_number", lineNumber);
-            return anchor;
-        }
-
-        function updateAnchor()
-        {
-            var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation({ lineNumber: lineNumber, columnNumber: columnNumber });
-            anchor.textContent = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
-            anchor.setAttribute("preferred_panel", "scripts");
-            anchor.uiSourceCode = uiLocation.uiSourceCode;
-            anchor.lineNumber = uiLocation.lineNumber;
-        }
-        if (rawSourceCode.sourceMapping)
-            updateAnchor.call(this);
-        rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, updateAnchor, this);
-        return anchor;
+        return new WebInspector.DebuggerPresentationModel.Linkifier(this);
     },
 
     createPlacard: function(callFrame)
@@ -577,6 +556,67 @@ WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
     }
 }
 
+/**
+ * @constructor
+ */
+WebInspector.DebuggerPresentationModel.Linkifier = function(model)
+{
+    this._model = model;
+    this._anchorsForRawSourceCode = {};
+}
+
+WebInspector.DebuggerPresentationModel.Linkifier.prototype = {
+    linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
+    {
+        var linkText = WebInspector.formatLinkText(sourceURL, lineNumber);
+        var anchor = WebInspector.linkifyURLAsNode(sourceURL, linkText, classes, false);
+        anchor.rawLocation = { lineNumber: lineNumber, columnNumber: columnNumber };
+
+        var rawSourceCode = this._model._rawSourceCodeForScriptWithURL(sourceURL);
+        if (!rawSourceCode) {
+            anchor.setAttribute("preferred_panel", "resources");
+            anchor.setAttribute("line_number", lineNumber);
+            return anchor;
+        }
+
+        var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
+        if (!anchors) {
+            anchors = [];
+            this._anchorsForRawSourceCode[rawSourceCode.id] = anchors;
+            rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
+        }
+
+        if (rawSourceCode.sourceMapping)
+            this._updateAnchor(rawSourceCode, anchor);
+        anchors.push(anchor);
+        return anchor;
+    },
+
+    reset: function()
+    {
+        for (var id in this._anchorsForRawSourceCode)
+            this._model._rawSourceCode[id].removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
+        this._anchorsForRawSourceCode = {};
+    },
+
+    _updateSourceAnchors: function(event)
+    {
+        var rawSourceCode = event.target;
+        var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
+        for (var i = 0; i < anchors.length; ++i)
+            this._updateAnchor(rawSourceCode, anchors[i]);
+    },
+
+    _updateAnchor: function(rawSourceCode, anchor)
+    {
+        var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(anchor.rawLocation);
+        anchor.textContent = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
+        anchor.setAttribute("preferred_panel", "scripts");
+        anchor.uiSourceCode = uiLocation.uiSourceCode;
+        anchor.lineNumber = uiLocation.lineNumber;
+    }
+}
+
 WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
 
 /**
index 2eb8649601bfc3048f05fe7adcb56cb5238fc3df..795e6b18d513c4f14ebbb2ca658515ec960e41dc 100644 (file)
@@ -56,6 +56,8 @@ WebInspector.EventListenersSidebarPane = function()
     this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
 
     this.titleElement.appendChild(this.settingsSelectElement);
+
+    this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
 }
 
 WebInspector.EventListenersSidebarPane._objectGroupName = "event-listeners-sidebar-pane";
@@ -64,6 +66,8 @@ WebInspector.EventListenersSidebarPane.prototype = {
     update: function(node)
     {
         RuntimeAgent.releaseObjectGroup(WebInspector.EventListenersSidebarPane._objectGroupName);
+        this._linkifier.reset();
+
         var body = this.bodyElement;
         body.removeChildren();
         this.sections = [];
@@ -84,7 +88,7 @@ WebInspector.EventListenersSidebarPane.prototype = {
                 var type = eventListener.type;
                 var section = sectionMap[type];
                 if (!section) {
-                    section = new WebInspector.EventListenersSection(type, node.id);
+                    section = new WebInspector.EventListenersSection(type, node.id, self._linkifier);
                     sectionMap[type] = section;
                     sectionNames.push(type);
                     self.sections.push(section);
@@ -124,10 +128,11 @@ WebInspector.EventListenersSidebarPane.prototype = {
 
 WebInspector.EventListenersSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
 
-WebInspector.EventListenersSection = function(title, nodeId)
+WebInspector.EventListenersSection = function(title, nodeId, linkifier)
 {
     this.eventListeners = [];
     this._nodeId = nodeId;
+    this._linkifier = linkifier;
     WebInspector.PropertiesSection.call(this, title);
 
     // Changed from a Properties List
@@ -158,7 +163,7 @@ WebInspector.EventListenersSection.prototype = {
         var length = filteredEventListeners.length;
         for (var i = 0; i < length; ++i) {
             var eventListener = filteredEventListeners[i];
-            var eventListenerBar = new WebInspector.EventListenerBar(eventListener, this._nodeId);
+            var eventListenerBar = new WebInspector.EventListenerBar(eventListener, this._nodeId, this._linkifier);
             this.eventBars.appendChild(eventListenerBar.element);
         }
     },
@@ -171,13 +176,13 @@ WebInspector.EventListenersSection.prototype = {
 
 WebInspector.EventListenersSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
 
-WebInspector.EventListenerBar = function(eventListener, nodeId)
+WebInspector.EventListenerBar = function(eventListener, nodeId, linkifier)
 {
     this.eventListener = eventListener;
     this._nodeId = nodeId;
     WebInspector.ObjectPropertiesSection.call(this);
     this._setNodeTitle();
-    this._setFunctionSubtitle();
+    this._setFunctionSubtitle(linkifier);
     this.editable = false;
     this.element.className = "event-bar"; /* Changed from "section" */
     this.headerElement.addStyleClass("source-code");
@@ -231,7 +236,7 @@ WebInspector.EventListenerBar.prototype = {
         this.titleElement.appendChild(WebInspector.panels.elements.linkifyNodeReference(this.eventListener.node));
     },
 
-    _setFunctionSubtitle: function()
+    _setFunctionSubtitle: function(linkifier)
     {
         // Requires that Function.toString() return at least the function's signature.
         if (this.eventListener.location) {
@@ -240,7 +245,7 @@ WebInspector.EventListenerBar.prototype = {
             var url = this.eventListener.location.scriptId;
             var lineNumber = this.eventListener.location.lineNumber - 1;
             var columnNumber = 0;
-            var urlElement = WebInspector.debuggerPresentationModel.linkifyLocation(url, lineNumber, columnNumber);
+            var urlElement = linkifier.linkifyLocation(url, lineNumber, columnNumber);
             this.subtitleElement.appendChild(urlElement);
         } else {
             var match = this.eventListener.handlerBody.match(/function ([^\(]+?)\(/);
index 3aa1f7648fee60a2ab25e54a1ff7fd5cd7b839de..240fac8049d363a4244e929c29bffb218e5e9606 100644 (file)
@@ -56,6 +56,7 @@ WebInspector.NetworkLogView = function(parentElement)
 
     this._createStatusbarButtons();
     this._createFilterStatusBarItems();
+    this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
 
     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceStarted, this._onResourceStarted, this);
     WebInspector.networkManager.addEventListener(WebInspector.NetworkManager.EventTypes.ResourceUpdated, this._onResourceUpdated, this);
@@ -735,7 +736,7 @@ WebInspector.NetworkLogView.prototype = {
 
         this._mainResourceLoadTime = -1;
         this._mainResourceDOMContentTime = -1;
-
+        this._linkifier.reset();
     },
 
     get resources()
@@ -2045,7 +2046,7 @@ WebInspector.NetworkDataGridNode.prototype = {
                     return;
                 }
                 this._initiatorCell.title = topFrame.url + ":" + topFrame.lineNumber;
-                var urlElement = WebInspector.debuggerPresentationModel.linkifyLocation(topFrame.url, topFrame.lineNumber - 1, 0);
+                var urlElement = this._parentView._linkifier.linkifyLocation(topFrame.url, topFrame.lineNumber - 1, 0);
                 this._initiatorCell.appendChild(urlElement);
                 this._appendSubtitle(this._initiatorCell, WebInspector.UIString("Script"));
             } else { // initiator.type === "parser"
index 585568c3aba06981cec8529f1142b90f2643b87e..cf40bc26e79093053fd792b3737528b349afb4f0 100644 (file)
@@ -98,7 +98,7 @@ WebInspector.ProfileDataGridNode.prototype = {
         if (this.profileNode.url) {
             // FIXME(62725): profileNode should reference a debugger location.
             var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
-            var urlElement = WebInspector.debuggerPresentationModel.linkifyLocation(this.profileNode.url, lineNumber, 0, "profile-node-file");
+            var urlElement = this.profileView._linkifier.linkifyLocation(this.profileNode.url, lineNumber, 0, "profile-node-file");
             urlElement.style.maxWidth = "75%";
             cell.insertBefore(urlElement, cell.firstChild);
         }
index 2a03f83051da541efe41409c11bdd2ae89431ce2..086efa4ecad5252009856ab0a600b89bc41b1e2d 100644 (file)
@@ -96,6 +96,8 @@ WebInspector.CPUProfileView = function(profile)
         self._updatePercentButton();
     }
 
+    this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
+
     ProfilerAgent.getProfile(this.profile.typeId, this.profile.uid, profileCallback);
 }
 
@@ -485,6 +487,7 @@ WebInspector.CPUProfileView.prototype = {
     {
         this.resetButton.visible = false;
         this.profileDataGridTree.restore();
+        this._linkifier.reset();
         this.refresh();
         this.refreshVisibleData();
     },
index 15517ce6397df4e55b665005760e230e3a0f3834..36981c28a509769deb9885f8fd1f9ebe761a24c3 100644 (file)
@@ -109,6 +109,7 @@ WebInspector.TimelinePanel = function()
 
     this._registerShortcuts();
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onTimelineEventRecorded, this);
+    this._linkifier = WebInspector.debuggerPresentationModel.createLinkifier();
 }
 
 // Define row height, should be in sync with styles for timeline graphs.
@@ -116,6 +117,19 @@ WebInspector.TimelinePanel.rowHeight = 18;
 WebInspector.TimelinePanel.shortRecordThreshold = 0.015;
 
 WebInspector.TimelinePanel.prototype = {
+    _linkifyLocation: function(url, lineNumber, columnNumber)
+    {
+        // FIXME(62725): stack trace line/column numbers are one-based.
+        lineNumber = lineNumber ? lineNumber - 1 : lineNumber;
+        columnNumber = columnNumber ? columnNumber - 1 : 0;
+        return this._linkifier.linkifyLocation(url, lineNumber, columnNumber, "timeline-details");
+    },
+
+    _linkifyCallFrame: function(callFrame)
+    {
+        return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
+    },
+
     _createTopPane: function() {
         var topPaneElement = document.createElement("div");
         topPaneElement.id = "timeline-overview-panel";
@@ -528,6 +542,7 @@ WebInspector.TimelinePanel.prototype = {
         this._refresh();
         this._closeRecordDetails();
         this._model._reset();
+        this._linkifier.reset();
     },
 
     elementsToRestoreScrollPositionsFor: function()
@@ -961,6 +976,7 @@ WebInspector.TimelineRecordGraphRow.prototype = {
 
 WebInspector.TimelinePanel.FormattedRecord = function(record, parentRecord, panel, scriptDetails)
 {
+    this._panel = panel;
     var recordTypes = WebInspector.TimelineAgent.RecordType;
     var style = panel._recordStyles[record.type];
     this.parent = parentRecord;
@@ -1051,7 +1067,7 @@ WebInspector.TimelinePanel.FormattedRecord.prototype = {
 
     _generatePopupContent: function(calculator, categories)
     {
-        var contentHelper = new WebInspector.TimelinePanel.PopupContentHelper(this.title);
+        var contentHelper = new WebInspector.TimelinePanel.PopupContentHelper(this.title, this._panel);
 
         if (this._children && this._children.length) {
             contentHelper._appendTextRow(WebInspector.UIString("Self Time"), Number.secondsToString(this._selfTime + 0.0001));
@@ -1136,26 +1152,26 @@ WebInspector.TimelinePanel.FormattedRecord.prototype = {
             case WebInspector.TimelineAgent.RecordType.GCEvent:
                 return WebInspector.UIString("%s collected", Number.bytesToString(this.data.usedHeapSizeDelta));
             case WebInspector.TimelineAgent.RecordType.TimerFire:
-                return this.scriptName ? this._linkifyLocation(this.scriptName, this.scriptLine, 0) : this.data.timerId;
+                return this.scriptName ? this._panel._linkifyLocation(this.scriptName, this.scriptLine, 0) : this.data.timerId;
             case WebInspector.TimelineAgent.RecordType.FunctionCall:
-                return this.scriptName ? this._linkifyLocation(this.scriptName, this.scriptLine, 0) : null;
+                return this.scriptName ? this._panel._linkifyLocation(this.scriptName, this.scriptLine, 0) : null;
             case WebInspector.TimelineAgent.RecordType.FireAnimationFrameEvent:
-                return this.scriptName ? this._linkifyLocation(this.scriptName, this.scriptLine, 0) : this.data.id;
+                return this.scriptName ? this._panel._linkifyLocation(this.scriptName, this.scriptLine, 0) : this.data.id;
             case WebInspector.TimelineAgent.RecordType.EventDispatch:
                 return this.data ? this.data.type : null;
             case WebInspector.TimelineAgent.RecordType.Paint:
                 return this.data.width + "\u2009\u00d7\u2009" + this.data.height;
             case WebInspector.TimelineAgent.RecordType.TimerInstall:
             case WebInspector.TimelineAgent.RecordType.TimerRemove:
-                return this.stackTrace ? this._linkifyCallFrame(this.stackTrace[0]) : this.data.timerId;
+                return this.stackTrace ? this._panel._linkifyCallFrame(this.stackTrace[0]) : this.data.timerId;
             case WebInspector.TimelineAgent.RecordType.RegisterAnimationFrameCallback:
             case WebInspector.TimelineAgent.RecordType.CancelAnimationFrameCallback:
-                return this.stackTrace ? this._linkifyCallFrame(this.stackTrace[0]) : this.data.id;
+                return this.stackTrace ? this._panel._linkifyCallFrame(this.stackTrace[0]) : this.data.id;
             case WebInspector.TimelineAgent.RecordType.ParseHTML:
             case WebInspector.TimelineAgent.RecordType.RecalculateStyles:
-                return this.stackTrace ? this._linkifyCallFrame(this.stackTrace[0]) : null;
+                return this.stackTrace ? this._panel._linkifyCallFrame(this.stackTrace[0]) : null;
             case WebInspector.TimelineAgent.RecordType.EvaluateScript:
-                return this.url ? this._linkifyLocation(this.url, this.data.lineNumber, 0) : null;
+                return this.url ? this._panel._linkifyLocation(this.url, this.data.lineNumber, 0) : null;
             case WebInspector.TimelineAgent.RecordType.XHRReadyStateChange:
             case WebInspector.TimelineAgent.RecordType.XHRLoad:
             case WebInspector.TimelineAgent.RecordType.ScheduleResourceRequest:
@@ -1171,19 +1187,6 @@ WebInspector.TimelinePanel.FormattedRecord.prototype = {
         }
     },
 
-    _linkifyLocation: function(url, lineNumber, columnNumber)
-    {
-        // FIXME(62725): stack trace line/column numbers are one-based.
-        lineNumber = lineNumber ? lineNumber - 1 : lineNumber;
-        columnNumber = columnNumber ? columnNumber - 1 : 0;
-        return WebInspector.debuggerPresentationModel.linkifyLocation(url, lineNumber, columnNumber, "timeline-details");
-    },
-
-    _linkifyCallFrame: function(callFrame)
-    {
-        return this._linkifyLocation(callFrame.url, callFrame.lineNumber, callFrame.columnNumber);
-    },
-
     _calculateAggregatedStats: function(categories)
     {
         this._aggregatedStats = {};
@@ -1204,8 +1207,9 @@ WebInspector.TimelinePanel.FormattedRecord.prototype = {
     }
 }
 
-WebInspector.TimelinePanel.PopupContentHelper = function(title)
+WebInspector.TimelinePanel.PopupContentHelper = function(title, panel)
 {
+    this._panel = panel;
     this._contentTable = document.createElement("table");;
     var titleCell = this._createCell(WebInspector.UIString("%s - Details", title), "timeline-details-title");
     titleCell.colSpan = 2;
@@ -1251,7 +1255,7 @@ WebInspector.TimelinePanel.PopupContentHelper.prototype = {
 
     _appendLinkRow: function(title, scriptName, scriptLine)
     {
-        var link = WebInspector.TimelinePanel.FormattedRecord.prototype._linkifyLocation(scriptName, scriptLine, 0, "timeline-details");
+        var link = this._panel._linkifyLocation(scriptName, scriptLine, 0, "timeline-details");
         this._appendElementRow(title, link);
     },
 
@@ -1266,7 +1270,7 @@ WebInspector.TimelinePanel.PopupContentHelper.prototype = {
             row.appendChild(this._createCell(stackFrame.functionName ? stackFrame.functionName : WebInspector.UIString("(anonymous function)"), "timeline-function-name"));
             row.appendChild(this._createCell(" @ "));
             var linkCell = document.createElement("td");
-            var urlElement = WebInspector.TimelinePanel.FormattedRecord.prototype._linkifyCallFrame(stackFrame);
+            var urlElement = this._panel._linkifyCallFrame(stackFrame);
             linkCell.appendChild(urlElement);
             row.appendChild(linkCell);
             framesTable.appendChild(row);
index 6ace71ecb530c2e1f09d5acb6fbc490e1e158206..682094026d45fba0b92feddfdec7f0ee0e8925de 100644 (file)
@@ -509,6 +509,9 @@ WebInspector.doLoadedDone = function()
     this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
     this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
 
+    this.debuggerModel = new WebInspector.DebuggerModel();
+    this.debuggerPresentationModel = new WebInspector.DebuggerPresentationModel();
+
     this.drawer = new WebInspector.Drawer();
     this.consoleView = new WebInspector.ConsoleView(WebInspector.WorkerManager.isWorkerFrontend());
 
@@ -521,8 +524,6 @@ WebInspector.doLoadedDone = function()
     InspectorBackend.registerInspectorDispatcher(this);
 
     this.cssModel = new WebInspector.CSSStyleModel();
-    this.debuggerModel = new WebInspector.DebuggerModel();
-    this.debuggerPresentationModel = new WebInspector.DebuggerPresentationModel();
 
     this.searchController = new WebInspector.SearchController();