Web Inspector: small memory leak in scripts panel.
[WebKit-https.git] / Source / WebCore / inspector / front-end / DebuggerPresentationModel.js
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.Object}
34  */
35 WebInspector.DebuggerPresentationModel = function()
36 {
37     // FIXME: apply formatter from outside as a generic mapping.
38     this._formatter = new WebInspector.ScriptFormatter();
39     this._rawSourceCode = {};
40     this._presentationCallFrames = [];
41
42     this._breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this._breakpointAdded.bind(this), this._breakpointRemoved.bind(this), WebInspector.debuggerModel);
43
44     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
45     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
46     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
47     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
48     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._debuggerReset, this);
49
50     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
51     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
52
53     new WebInspector.DebuggerPresentationModelResourceBinding(this);
54 }
55
56 WebInspector.DebuggerPresentationModel.Events = {
57     UISourceCodeAdded: "source-file-added",
58     UISourceCodeReplaced: "source-file-replaced",
59     UISourceCodeRemoved: "source-file-removed",
60     ConsoleMessageAdded: "console-message-added",
61     ConsoleMessagesCleared: "console-messages-cleared",
62     BreakpointAdded: "breakpoint-added",
63     BreakpointRemoved: "breakpoint-removed",
64     DebuggerPaused: "debugger-paused",
65     DebuggerResumed: "debugger-resumed",
66     CallFrameSelected: "call-frame-selected",
67     ExecutionLineChanged: "execution-line-changed"
68 }
69
70 WebInspector.DebuggerPresentationModel.prototype = {
71     createLinkifier: function()
72     {
73         return new WebInspector.DebuggerPresentationModel.Linkifier(this);
74     },
75
76     createPlacard: function(callFrame)
77     {
78         var title = callFrame._callFrame.functionName || WebInspector.UIString("(anonymous function)");
79         var placard = new WebInspector.Placard(title, "");
80
81         var rawSourceCode = callFrame._rawSourceCode;
82         function updatePlacard()
83         {
84             var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(callFrame._callFrame.location);
85             placard.subtitle = WebInspector.displayNameForURL(uiLocation.uiSourceCode.url) + ":" + (uiLocation.lineNumber + 1);
86             placard._text = WebInspector.UIString("%s() at %s", placard.title, placard.subtitle);
87         }
88         if (rawSourceCode.sourceMapping)
89             updatePlacard.call(this);
90         rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, updatePlacard, this);
91         return placard;
92     },
93
94     _parsedScriptSource: function(event)
95     {
96         this._addScript(event.data);
97     },
98
99     _failedToParseScriptSource: function(event)
100     {
101         this._addScript(event.data);
102     },
103
104     _addScript: function(script)
105     {
106         var rawSourceCodeId = this._createRawSourceCodeId(script);
107         var rawSourceCode = this._rawSourceCode[rawSourceCodeId];
108         if (rawSourceCode) {
109             rawSourceCode.addScript(script);
110             return;
111         }
112
113         var resource;
114         if (script.sourceURL)
115             resource = WebInspector.networkManager.inflightResourceForURL(script.sourceURL) || WebInspector.resourceForURL(script.sourceURL);
116         rawSourceCode = new WebInspector.RawSourceCode(rawSourceCodeId, script, resource, this._formatter, this._formatSource);
117         this._rawSourceCode[rawSourceCodeId] = rawSourceCode;
118         if (rawSourceCode.sourceMapping)
119             this._updateSourceMapping(rawSourceCode, null);
120         rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._sourceMappingUpdated, this);
121     },
122
123     _sourceMappingUpdated: function(event)
124     {
125         var rawSourceCode = event.target;
126         var oldUISourceCode = event.data.oldUISourceCode;
127         this._updateSourceMapping(rawSourceCode, oldUISourceCode);
128     },
129
130     _updateSourceMapping: function(rawSourceCode, oldUISourceCode)
131     {
132         if (oldUISourceCode) {
133             var breakpoints = this._breakpointManager.breakpointsForUISourceCode(oldUISourceCode);
134             for (var lineNumber in breakpoints) {
135                 var breakpoint = breakpoints[lineNumber];
136                 this._breakpointRemoved(breakpoint);
137                 delete breakpoint.uiSourceCode;
138             }
139         }
140
141         this._restoreBreakpoints(rawSourceCode);
142         this._restoreConsoleMessages(rawSourceCode);
143
144         var uiSourceCode = rawSourceCode.sourceMapping.uiSourceCode;
145         if (!oldUISourceCode)
146             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeAdded, uiSourceCode);
147         else {
148             var eventData = { uiSourceCode: uiSourceCode, oldUISourceCode: oldUISourceCode };
149             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeReplaced, eventData);
150         }
151     },
152
153     _restoreBreakpoints: function(rawSourceCode)
154     {
155         var uiSourceCode = rawSourceCode.sourceMapping.uiSourceCode;
156         this._breakpointManager.uiSourceCodeAdded(uiSourceCode);
157         var breakpoints = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
158         for (var lineNumber in breakpoints)
159             this._breakpointAdded(breakpoints[lineNumber]);
160     },
161
162     _restoreConsoleMessages: function(rawSourceCode)
163     {
164         var messages = rawSourceCode.messages;
165         for (var i = 0; i < messages.length; ++i)
166             messages[i]._presentationMessage = this._createPresentationMessage(messages[i], rawSourceCode.sourceMapping);
167     },
168
169     canEditScriptSource: function(uiSourceCode)
170     {
171         if (!Preferences.canEditScriptSource || this._formatSource)
172             return false;
173         var rawSourceCode = uiSourceCode.rawSourceCode;
174         var script = this._scriptForRawSourceCode(rawSourceCode);
175         return script && !script.lineOffset && !script.columnOffset;
176     },
177
178     setScriptSource: function(uiSourceCode, newSource, callback)
179     {
180         var rawSourceCode = uiSourceCode.rawSourceCode;
181         var script = this._scriptForRawSourceCode(rawSourceCode);
182
183         function didEditScriptSource(error)
184         {
185             callback(error);
186             if (error)
187                 return;
188
189             var resource = WebInspector.resourceForURL(rawSourceCode.url);
190             if (resource)
191                 resource.addRevision(newSource);
192
193             rawSourceCode.contentEdited();
194
195             if (WebInspector.debuggerModel.callFrames)
196                 this._debuggerPaused();
197         }
198         WebInspector.debuggerModel.setScriptSource(script.scriptId, newSource, didEditScriptSource.bind(this));
199     },
200
201     _updateBreakpointsAfterLiveEdit: function(uiSourceCode, oldSource, newSource)
202     {
203         var breakpoints = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
204
205         // Clear and re-create breakpoints according to text diff.
206         var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
207         for (var lineNumber in breakpoints) {
208             var breakpoint = breakpoints[lineNumber];
209
210             this.removeBreakpoint(uiSourceCode, lineNumber);
211
212             var newLineNumber = diff.left[lineNumber].row;
213             if (newLineNumber === undefined) {
214                 for (var i = lineNumber - 1; i >= 0; --i) {
215                     if (diff.left[i].row === undefined)
216                         continue;
217                     var shiftedLineNumber = diff.left[i].row + lineNumber - i;
218                     if (shiftedLineNumber < diff.right.length) {
219                         var originalLineNumber = diff.right[shiftedLineNumber].row;
220                         if (originalLineNumber === lineNumber || originalLineNumber === undefined)
221                             newLineNumber = shiftedLineNumber;
222                     }
223                     break;
224                 }
225             }
226             if (newLineNumber !== undefined)
227                 this.setBreakpoint(uiSourceCode, newLineNumber, breakpoint.condition, breakpoint.enabled);
228         }
229     },
230
231     setFormatSource: function(formatSource)
232     {
233         if (this._formatSource === formatSource)
234             return;
235
236         this._formatSource = formatSource;
237         this._breakpointManager.reset();
238         for (var id in this._rawSourceCode)
239             this._rawSourceCode[id].setFormatted(this._formatSource);
240
241         if (WebInspector.debuggerModel.callFrames)
242             this._debuggerPaused();
243     },
244
245     _consoleMessageAdded: function(event)
246     {
247         var message = event.data;
248         if (!message.url || !message.isErrorOrWarning() || !message.message)
249             return;
250
251         var rawSourceCode = this._rawSourceCodeForScriptWithURL(message.url);
252         if (!rawSourceCode)
253             return;
254
255         rawSourceCode.messages.push(message);
256         if (rawSourceCode.sourceMapping) {
257             message._presentationMessage = this._createPresentationMessage(message, rawSourceCode.sourceMapping);
258             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, message._presentationMessage);
259         }
260     },
261
262     _createPresentationMessage: function(message, sourceMapping)
263     {
264         // FIXME(62725): stack trace line/column numbers are one-based.
265         var lineNumber = message.stackTrace ? message.stackTrace[0].lineNumber - 1 : message.line - 1;
266         var columnNumber = message.stackTrace ? message.stackTrace[0].columnNumber - 1 : 0;
267         var uiLocation = sourceMapping.rawLocationToUILocation({ lineNumber: lineNumber, columnNumber: columnNumber });
268         var presentationMessage = {};
269         presentationMessage.uiSourceCode = uiLocation.uiSourceCode;
270         presentationMessage.lineNumber = uiLocation.lineNumber;
271         presentationMessage.originalMessage = message;
272         return presentationMessage;
273     },
274
275     _consoleCleared: function()
276     {
277         for (var id in this._rawSourceCode)
278             this._rawSourceCode[id].messages = [];
279         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessagesCleared);
280     },
281
282     continueToLine: function(uiSourceCode, lineNumber)
283     {
284         // FIXME: use RawSourceCode.uiLocationToRawLocation.
285         var rawLocation = uiSourceCode.rawSourceCode.sourceMapping.uiLocationToRawLocation(uiSourceCode, lineNumber);
286         WebInspector.debuggerModel.continueToLocation(rawLocation);
287     },
288
289     breakpointsForUISourceCode: function(uiSourceCode)
290     {
291         var breakpointsMap = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
292         var breakpointsList = [];
293         for (var lineNumber in breakpointsMap)
294             breakpointsList.push(breakpointsMap[lineNumber]);
295         return breakpointsList;
296     },
297
298     messagesForUISourceCode: function(uiSourceCode)
299     {
300         var rawSourceCode = uiSourceCode.rawSourceCode;
301         var messages = [];
302         for (var i = 0; i < rawSourceCode.messages.length; ++i)
303             messages.push(rawSourceCode.messages[i]._presentationMessage);
304         return messages;
305     },
306
307     setBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
308     {
309         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, condition, enabled);
310     },
311
312     setBreakpointEnabled: function(uiSourceCode, lineNumber, enabled)
313     {
314         var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber);
315         if (!breakpoint)
316             return;
317         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
318         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, breakpoint.condition, enabled);
319     },
320
321     updateBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
322     {
323         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
324         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, condition, enabled);
325     },
326
327     removeBreakpoint: function(uiSourceCode, lineNumber)
328     {
329         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
330     },
331
332     findBreakpoint: function(uiSourceCode, lineNumber)
333     {
334         return this._breakpointManager.breakpointsForUISourceCode(uiSourceCode)[lineNumber];
335     },
336
337     _breakpointAdded: function(breakpoint)
338     {
339         if (breakpoint.uiSourceCode)
340             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
341     },
342
343     _breakpointRemoved: function(breakpoint)
344     {
345         if (breakpoint.uiSourceCode)
346             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
347     },
348
349     _debuggerPaused: function()
350     {
351         var callFrames = WebInspector.debuggerModel.callFrames;
352         this._presentationCallFrames = [];
353         for (var i = 0; i < callFrames.length; ++i) {
354             var callFrame = callFrames[i];
355             var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.scriptId);
356             if (!script)
357                 continue;
358             var rawSourceCode = this._rawSourceCodeForScript(script);
359             this._presentationCallFrames.push(new WebInspector.PresentationCallFrame(callFrame, i, this, rawSourceCode));
360         }
361         var details = WebInspector.debuggerModel.debuggerPausedDetails;
362         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
363
364         this.selectedCallFrame = this._presentationCallFrames[0];
365     },
366
367     _debuggerResumed: function()
368     {
369         this._presentationCallFrames = [];
370         this.selectedCallFrame = null;
371         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
372     },
373
374     set selectedCallFrame(callFrame)
375     {
376         if (this._selectedCallFrame)
377             this._selectedCallFrame.rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._dispatchExecutionLineChanged, this);
378         this._selectedCallFrame = callFrame;
379         if (!this._selectedCallFrame)
380             return;
381
382         this._selectedCallFrame.rawSourceCode.forceUpdateSourceMapping();
383         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
384
385         if (this._selectedCallFrame.rawSourceCode.sourceMapping)
386             this._dispatchExecutionLineChanged(null);
387         this._selectedCallFrame.rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._dispatchExecutionLineChanged, this);
388     },
389
390     get selectedCallFrame()
391     {
392         return this._selectedCallFrame;
393     },
394
395     _dispatchExecutionLineChanged: function(event)
396     {
397         var rawLocation = this._selectedCallFrame._callFrame.location;
398         var uiLocation = this._selectedCallFrame.rawSourceCode.sourceMapping.rawLocationToUILocation(rawLocation);
399         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ExecutionLineChanged, uiLocation);
400     },
401
402     /**
403      * @param {string} sourceURL
404      */
405     _rawSourceCodeForScriptWithURL: function(sourceURL)
406     {
407         return this._rawSourceCode[sourceURL];
408     },
409
410     /**
411      * @param {WebInspector.Script} script
412      */
413     _rawSourceCodeForScript: function(script)
414     {
415         return this._rawSourceCode[this._createRawSourceCodeId(script)];
416     },
417
418     _scriptForRawSourceCode: function(rawSourceCode)
419     {
420         function filter(script)
421         {
422             return this._createRawSourceCodeId(script) === rawSourceCode.id;
423         }
424         return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0];
425     },
426
427     _createRawSourceCodeId: function(script)
428     {
429         return script.sourceURL || script.scriptId;
430     },
431
432     _debuggerReset: function()
433     {
434         for (var id in this._rawSourceCode) {
435             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeRemoved, this._rawSourceCode[id].sourceMapping.uiSourceCode);
436             this._rawSourceCode[id].removeAllListeners();
437         }
438         this._rawSourceCode = {};
439         this._presentationCallFrames = [];
440         this._selectedCallFrame = null;
441         this._breakpointManager.debuggerReset();
442     }
443 }
444
445 WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
446
447 /**
448  * @constructor
449  */
450 WebInspector.PresentationCallFrame = function(callFrame, index, model, rawSourceCode)
451 {
452     this._callFrame = callFrame;
453     this._index = index;
454     this._model = model;
455     this._rawSourceCode = rawSourceCode;
456 }
457
458 WebInspector.PresentationCallFrame.prototype = {
459     get type()
460     {
461         return this._callFrame.type;
462     },
463
464     get scopeChain()
465     {
466         return this._callFrame.scopeChain;
467     },
468
469     get this()
470     {
471         return this._callFrame.this;
472     },
473
474     get index()
475     {
476         return this._index;
477     },
478
479     get rawSourceCode()
480     {
481         return this._rawSourceCode;
482     },
483
484     evaluate: function(code, objectGroup, includeCommandLineAPI, returnByValue, callback)
485     {
486         function didEvaluateOnCallFrame(error, result, wasThrown)
487         {
488             if (error) {
489                 console.error(error);
490                 callback(null);
491                 return;
492             }
493
494             if (returnByValue && !wasThrown)
495                 callback(result, wasThrown);
496             else
497                 callback(WebInspector.RemoteObject.fromPayload(result), wasThrown);
498         }
499         DebuggerAgent.evaluateOnCallFrame(this._callFrame.callFrameId, code, objectGroup, includeCommandLineAPI, returnByValue, didEvaluateOnCallFrame.bind(this));
500     },
501
502     uiLocation: function(callback)
503     {
504         function sourceMappingReady()
505         {
506             this._rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
507             callback(this._rawSourceCode.sourceMapping.rawLocationToUILocation(this._callFrame.location));
508         }
509         if (this._rawSourceCode.sourceMapping)
510             sourceMappingReady.call(this);
511         else
512             this._rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
513     }
514 }
515
516 /**
517  * @constructor
518  * @implements {WebInspector.ResourceDomainModelBinding}
519  */
520 WebInspector.DebuggerPresentationModelResourceBinding = function(model)
521 {
522     this._presentationModel = model;
523     WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
524 }
525
526 WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
527     canSetContent: function(resource)
528     {
529         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url)
530         if (!rawSourceCode)
531             return false;
532         return this._presentationModel.canEditScriptSource(rawSourceCode.sourceMapping.uiSourceCode);
533     },
534
535     setContent: function(resource, content, majorChange, userCallback)
536     {
537         if (!majorChange)
538             return;
539
540         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url);
541         if (!rawSourceCode) {
542             userCallback("Resource is not editable");
543             return;
544         }
545
546         resource.requestContent(this._setContentWithInitialContent.bind(this, rawSourceCode.sourceMapping.uiSourceCode, content, userCallback));
547     },
548
549     _setContentWithInitialContent: function(uiSourceCode, content, userCallback, oldContent)
550     {
551         function callback(error)
552         {
553             if (userCallback)
554                 userCallback(error);
555             if (!error)
556                 this._presentationModel._updateBreakpointsAfterLiveEdit(uiSourceCode, oldContent, content);
557         }
558         this._presentationModel.setScriptSource(uiSourceCode, content, callback.bind(this));
559     }
560 }
561
562 /**
563  * @constructor
564  */
565 WebInspector.DebuggerPresentationModel.Linkifier = function(model)
566 {
567     this._model = model;
568     this._anchorsForRawSourceCode = {};
569 }
570
571 WebInspector.DebuggerPresentationModel.Linkifier.prototype = {
572     linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
573     {
574         var linkText = WebInspector.formatLinkText(sourceURL, lineNumber);
575         var anchor = WebInspector.linkifyURLAsNode(sourceURL, linkText, classes, false);
576         anchor.rawLocation = { lineNumber: lineNumber, columnNumber: columnNumber };
577
578         var rawSourceCode = this._model._rawSourceCodeForScriptWithURL(sourceURL);
579         if (!rawSourceCode) {
580             anchor.setAttribute("preferred_panel", "resources");
581             anchor.setAttribute("line_number", lineNumber);
582             return anchor;
583         }
584
585         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
586         if (!anchors) {
587             anchors = [];
588             this._anchorsForRawSourceCode[rawSourceCode.id] = anchors;
589             rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
590         }
591
592         if (rawSourceCode.sourceMapping)
593             this._updateAnchor(rawSourceCode, anchor);
594         anchors.push(anchor);
595         return anchor;
596     },
597
598     reset: function()
599     {
600         for (var id in this._anchorsForRawSourceCode)
601             this._model._rawSourceCode[id].removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
602         this._anchorsForRawSourceCode = {};
603     },
604
605     _updateSourceAnchors: function(event)
606     {
607         var rawSourceCode = event.target;
608         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
609         for (var i = 0; i < anchors.length; ++i)
610             this._updateAnchor(rawSourceCode, anchors[i]);
611     },
612
613     _updateAnchor: function(rawSourceCode, anchor)
614     {
615         var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(anchor.rawLocation);
616         anchor.textContent = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
617         anchor.setAttribute("preferred_panel", "scripts");
618         anchor.uiSourceCode = uiLocation.uiSourceCode;
619         anchor.lineNumber = uiLocation.lineNumber;
620     }
621 }
622
623 WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
624
625 /**
626  * @type {?WebInspector.DebuggerPresentationModel}
627  */
628 WebInspector.debuggerPresentationModel = null;