Unreviewed test fix for r97050.
[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             if (this._rawSourceCode[id].sourceMapping && this._rawSourceCode[id].sourceMapping.uiSourceCode)
436                 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeRemoved, this._rawSourceCode[id].sourceMapping.uiSourceCode);
437             this._rawSourceCode[id].removeAllListeners();
438         }
439         this._rawSourceCode = {};
440         this._presentationCallFrames = [];
441         this._selectedCallFrame = null;
442         this._breakpointManager.debuggerReset();
443     }
444 }
445
446 WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
447
448 /**
449  * @constructor
450  */
451 WebInspector.PresentationCallFrame = function(callFrame, index, model, rawSourceCode)
452 {
453     this._callFrame = callFrame;
454     this._index = index;
455     this._model = model;
456     this._rawSourceCode = rawSourceCode;
457 }
458
459 WebInspector.PresentationCallFrame.prototype = {
460     get type()
461     {
462         return this._callFrame.type;
463     },
464
465     get scopeChain()
466     {
467         return this._callFrame.scopeChain;
468     },
469
470     get this()
471     {
472         return this._callFrame.this;
473     },
474
475     get index()
476     {
477         return this._index;
478     },
479
480     get rawSourceCode()
481     {
482         return this._rawSourceCode;
483     },
484
485     evaluate: function(code, objectGroup, includeCommandLineAPI, returnByValue, callback)
486     {
487         function didEvaluateOnCallFrame(error, result, wasThrown)
488         {
489             if (error) {
490                 console.error(error);
491                 callback(null);
492                 return;
493             }
494
495             if (returnByValue && !wasThrown)
496                 callback(result, wasThrown);
497             else
498                 callback(WebInspector.RemoteObject.fromPayload(result), wasThrown);
499         }
500         DebuggerAgent.evaluateOnCallFrame(this._callFrame.callFrameId, code, objectGroup, includeCommandLineAPI, returnByValue, didEvaluateOnCallFrame.bind(this));
501     },
502
503     uiLocation: function(callback)
504     {
505         function sourceMappingReady()
506         {
507             this._rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
508             callback(this._rawSourceCode.sourceMapping.rawLocationToUILocation(this._callFrame.location));
509         }
510         if (this._rawSourceCode.sourceMapping)
511             sourceMappingReady.call(this);
512         else
513             this._rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
514     }
515 }
516
517 /**
518  * @constructor
519  * @implements {WebInspector.ResourceDomainModelBinding}
520  */
521 WebInspector.DebuggerPresentationModelResourceBinding = function(model)
522 {
523     this._presentationModel = model;
524     WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
525 }
526
527 WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
528     canSetContent: function(resource)
529     {
530         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url)
531         if (!rawSourceCode)
532             return false;
533         return this._presentationModel.canEditScriptSource(rawSourceCode.sourceMapping.uiSourceCode);
534     },
535
536     setContent: function(resource, content, majorChange, userCallback)
537     {
538         if (!majorChange)
539             return;
540
541         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url);
542         if (!rawSourceCode) {
543             userCallback("Resource is not editable");
544             return;
545         }
546
547         resource.requestContent(this._setContentWithInitialContent.bind(this, rawSourceCode.sourceMapping.uiSourceCode, content, userCallback));
548     },
549
550     _setContentWithInitialContent: function(uiSourceCode, content, userCallback, oldContent)
551     {
552         function callback(error)
553         {
554             if (userCallback)
555                 userCallback(error);
556             if (!error)
557                 this._presentationModel._updateBreakpointsAfterLiveEdit(uiSourceCode, oldContent, content);
558         }
559         this._presentationModel.setScriptSource(uiSourceCode, content, callback.bind(this));
560     }
561 }
562
563 /**
564  * @constructor
565  */
566 WebInspector.DebuggerPresentationModel.Linkifier = function(model)
567 {
568     this._model = model;
569     this._anchorsForRawSourceCode = {};
570 }
571
572 WebInspector.DebuggerPresentationModel.Linkifier.prototype = {
573     linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
574     {
575         var linkText = WebInspector.formatLinkText(sourceURL, lineNumber);
576         var anchor = WebInspector.linkifyURLAsNode(sourceURL, linkText, classes, false);
577         anchor.rawLocation = { lineNumber: lineNumber, columnNumber: columnNumber };
578
579         var rawSourceCode = this._model._rawSourceCodeForScriptWithURL(sourceURL);
580         if (!rawSourceCode) {
581             anchor.setAttribute("preferred_panel", "resources");
582             anchor.setAttribute("line_number", lineNumber);
583             return anchor;
584         }
585
586         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
587         if (!anchors) {
588             anchors = [];
589             this._anchorsForRawSourceCode[rawSourceCode.id] = anchors;
590             rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
591         }
592
593         if (rawSourceCode.sourceMapping)
594             this._updateAnchor(rawSourceCode, anchor);
595         anchors.push(anchor);
596         return anchor;
597     },
598
599     reset: function()
600     {
601         for (var id in this._anchorsForRawSourceCode)
602             this._model._rawSourceCode[id].removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
603         this._anchorsForRawSourceCode = {};
604     },
605
606     _updateSourceAnchors: function(event)
607     {
608         var rawSourceCode = event.target;
609         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
610         for (var i = 0; i < anchors.length; ++i)
611             this._updateAnchor(rawSourceCode, anchors[i]);
612     },
613
614     _updateAnchor: function(rawSourceCode, anchor)
615     {
616         var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(anchor.rawLocation);
617         anchor.textContent = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
618         anchor.setAttribute("preferred_panel", "scripts");
619         anchor.uiSourceCode = uiLocation.uiSourceCode;
620         anchor.lineNumber = uiLocation.lineNumber;
621     }
622 }
623
624 WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
625
626 /**
627  * @type {?WebInspector.DebuggerPresentationModel}
628  */
629 WebInspector.debuggerPresentationModel = null;