tizen beta release
[framework/web/webkit-efl.git] / debian / libwebkit-engine / usr / share / ewebkit-0 / webinspector / 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._rawSourceCodeForScriptId = {};
40     this._rawSourceCodeForURL = {};
41     this._rawSourceCodeForDocumentURL = {};
42     this._presentationCallFrames = [];
43
44     this._breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this._breakpointAdded.bind(this), this._breakpointRemoved.bind(this), WebInspector.debuggerModel);
45
46     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
47     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
48     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
49     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
50     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
51
52     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._consoleMessageAdded, this);
53     WebInspector.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._consoleCleared, this);
54
55     new WebInspector.DebuggerPresentationModelResourceBinding(this);
56 }
57
58 WebInspector.DebuggerPresentationModel.Events = {
59     UISourceCodeAdded: "source-file-added",
60     UISourceCodeReplaced: "source-file-replaced",
61     UISourceCodeRemoved: "source-file-removed",
62     ConsoleMessageAdded: "console-message-added",
63     ConsoleMessagesCleared: "console-messages-cleared",
64     BreakpointAdded: "breakpoint-added",
65     BreakpointRemoved: "breakpoint-removed",
66     DebuggerPaused: "debugger-paused",
67     DebuggerResumed: "debugger-resumed",
68     DebuggerReset: "debugger-reset",
69     CallFrameSelected: "call-frame-selected",
70     ConsoleCommandEvaluatedInSelectedCallFrame: "console-command-evaluated-in-selected-call-frame",
71     ExecutionLineChanged: "execution-line-changed"
72 }
73
74 WebInspector.DebuggerPresentationModel.prototype = {
75     /**
76      * @param {WebInspector.DebuggerPresentationModel.LinkifierFormatter=} formatter
77      */
78     createLinkifier: function(formatter)
79     {
80         return new WebInspector.DebuggerPresentationModel.Linkifier(this, formatter);
81     },
82
83     /**
84      * @param {WebInspector.PresentationCallFrame} callFrame
85      * @return {WebInspector.Placard}
86      */
87     createPlacard: function(callFrame)
88     {
89         return new WebInspector.DebuggerPresentationModel.CallFramePlacard(callFrame);
90     },
91
92     /*
93      * @param {DebuggerAgent.Location} rawLocation
94      * @return {?WebInspector.UILocation}
95      */
96     rawLocationToUILocation: function(rawLocation)
97     {
98         var rawSourceCode = this._rawSourceCodeForScriptId[rawLocation.scriptId];
99         if (!rawSourceCode.sourceMapping)
100             return null;
101         return rawSourceCode.sourceMapping.rawLocationToUILocation(rawLocation);
102     },
103
104     /**
105      * @param {WebInspector.Event} event
106      */
107     _parsedScriptSource: function(event)
108     {
109         var script = /** @type {WebInspector.Script} */ event.data;
110         this._addScript(script);
111     },
112
113     /**
114      * @param {WebInspector.Event} event
115      */
116     _failedToParseScriptSource: function(event)
117     {
118         var script = /** @type {WebInspector.Script} */ event.data;
119         this._addScript(script);
120     },
121
122     /**
123      * @param {WebInspector.Script} script
124      */
125     _addScript: function(script)
126     {
127         var resource;
128         var isInlineScript = false;
129         if (script.isInlineScript()) {
130             resource = WebInspector.networkManager.inflightResourceForURL(script.sourceURL) || WebInspector.resourceForURL(script.sourceURL);
131             if (resource && resource.type === WebInspector.Resource.Type.Document) {
132                 isInlineScript = true;
133                 var rawSourceCode = this._rawSourceCodeForDocumentURL[script.sourceURL];
134                 if (rawSourceCode) {
135                     rawSourceCode.addScript(script);
136                     this._bindScriptToRawSourceCode(script, rawSourceCode);
137                     return;
138                 }
139             }
140         }
141
142         rawSourceCode = new WebInspector.RawSourceCode(script.scriptId, script, resource, this._formatter, this._formatSource);
143         this._bindScriptToRawSourceCode(script, rawSourceCode);
144
145         if (isInlineScript)
146             this._rawSourceCodeForDocumentURL[script.sourceURL] = rawSourceCode;
147
148         if (rawSourceCode.sourceMapping)
149             this._updateSourceMapping(rawSourceCode, null);
150         rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._sourceMappingUpdated, this);
151     },
152
153     /**
154      * @param {WebInspector.Script} script
155      * @param {WebInspector.RawSourceCode} rawSourceCode
156      */
157     _bindScriptToRawSourceCode: function(script, rawSourceCode)
158     {
159         this._rawSourceCodeForScriptId[script.scriptId] = rawSourceCode;
160         this._rawSourceCodeForURL[script.sourceURL] = rawSourceCode;
161     },
162
163     /**
164      * @param {WebInspector.Event} event
165      */
166     _sourceMappingUpdated: function(event)
167     {
168         var rawSourceCode = /** @type {WebInspector.RawSourceCode} */ event.target;
169         var oldSourceMapping = /** @type {WebInspector.RawSourceCode.SourceMapping} */ event.data["oldSourceMapping"];
170         this._updateSourceMapping(rawSourceCode, oldSourceMapping);
171     },
172
173     /**
174      * @return {Array.<WebInspector.UISourceCode>}
175      */
176     uiSourceCodes: function()
177     {
178         var result = [];
179         for (var id in this._rawSourceCodeForScriptId) {
180             var uiSourceCodeList = this._rawSourceCodeForScriptId[id].sourceMapping.uiSourceCodeList();
181             for (var i = 0; i < uiSourceCodeList.length; ++i)
182                 result.push(uiSourceCodeList[i]);
183         }
184         return result;
185     },
186
187     /**
188      * @param {WebInspector.RawSourceCode} rawSourceCode
189      * @param {WebInspector.RawSourceCode.SourceMapping} oldSourceMapping
190      */
191     _updateSourceMapping: function(rawSourceCode, oldSourceMapping)
192     {
193         if (oldSourceMapping) {
194             var oldUISourceCodeList = oldSourceMapping.uiSourceCodeList();
195             for (var i = 0; i < oldUISourceCodeList.length; ++i) {
196                 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(oldUISourceCodeList[i]);
197                 for (var lineNumber in breakpoints) {
198                     var breakpoint = breakpoints[lineNumber];
199                     this._breakpointRemoved(breakpoint);
200                     delete breakpoint.uiSourceCode;
201                 }
202             }
203         }
204
205         this._restoreBreakpoints(rawSourceCode);
206         this._restoreConsoleMessages(rawSourceCode);
207
208         if (!oldSourceMapping) {
209             var uiSourceCodeList = rawSourceCode.sourceMapping.uiSourceCodeList();
210             for (var i = 0; i < uiSourceCodeList.length; ++i)
211                 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeAdded, uiSourceCodeList[i]);
212         } else {
213             var eventData = { uiSourceCodeList: rawSourceCode.sourceMapping.uiSourceCodeList(), oldUISourceCodeList: oldSourceMapping.uiSourceCodeList() };
214             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeReplaced, eventData);
215         }
216     },
217
218     /**
219      * @param {WebInspector.RawSourceCode} rawSourceCode
220      */
221     _restoreBreakpoints: function(rawSourceCode)
222     {
223         var uiSourceCodeList = rawSourceCode.sourceMapping.uiSourceCodeList();
224         for (var i = 0; i < uiSourceCodeList.length; ++i) {
225             var uiSourceCode = uiSourceCodeList[i];
226             this._breakpointManager.uiSourceCodeAdded(uiSourceCode);
227             var breakpoints = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
228             for (var lineNumber in breakpoints)
229                 this._breakpointAdded(breakpoints[lineNumber]);
230         }
231
232     },
233
234     /**
235      * @param {WebInspector.RawSourceCode} rawSourceCode
236      */
237     _restoreConsoleMessages: function(rawSourceCode)
238     {
239         var messages = rawSourceCode.messages;
240         for (var i = 0; i < messages.length; ++i)
241             messages[i]._presentationMessage = this._createPresentationMessage(messages[i], rawSourceCode.sourceMapping);
242     },
243
244     /**
245      * @param {WebInspector.UISourceCode} uiSourceCode
246      * @return {boolean}
247      */
248     canEditScriptSource: function(uiSourceCode)
249     {
250         if (!WebInspector.debuggerModel.canSetScriptSource() || this._formatSource)
251             return false;
252         var rawSourceCode = uiSourceCode.rawSourceCode;
253         var script = this._scriptForRawSourceCode(rawSourceCode);
254         return script && !script.lineOffset && !script.columnOffset;
255     },
256
257     /**
258      * @param {WebInspector.UISourceCode} uiSourceCode
259      * @param {string} newSource
260      * @param {function(?Protocol.Error)} callback
261      */
262     setScriptSource: function(uiSourceCode, newSource, callback)
263     {
264         var rawSourceCode = uiSourceCode.rawSourceCode;
265         var script = this._scriptForRawSourceCode(rawSourceCode);
266
267         /**
268          * @this {WebInspector.DebuggerPresentationModel}
269          * @param {?Protocol.Error} error
270          */
271         function didEditScriptSource(error)
272         {
273             callback(error);
274             if (error)
275                 return;
276
277             var resource = WebInspector.resourceForURL(rawSourceCode.url);
278             if (resource)
279                 resource.addRevision(newSource);
280
281             uiSourceCode.contentChanged(newSource);
282
283             if (WebInspector.debuggerModel.callFrames)
284                 this._debuggerPaused();
285         }
286         WebInspector.debuggerModel.setScriptSource(script.scriptId, newSource, didEditScriptSource.bind(this));
287     },
288
289     /**
290      * @param {WebInspector.UISourceCode} uiSourceCode
291      * @param {string} oldSource
292      * @param {string} newSource
293      */
294     _updateBreakpointsAfterLiveEdit: function(uiSourceCode, oldSource, newSource)
295     {
296         var breakpoints = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
297
298         // Clear and re-create breakpoints according to text diff.
299         var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
300         for (var lineNumber in breakpoints) {
301             var breakpoint = breakpoints[lineNumber];
302
303             this.removeBreakpoint(uiSourceCode, parseInt(lineNumber, 10));
304
305             var newLineNumber = diff.left[lineNumber].row;
306             if (newLineNumber === undefined) {
307                 for (var i = lineNumber - 1; i >= 0; --i) {
308                     if (diff.left[i].row === undefined)
309                         continue;
310                     var shiftedLineNumber = diff.left[i].row + lineNumber - i;
311                     if (shiftedLineNumber < diff.right.length) {
312                         var originalLineNumber = diff.right[shiftedLineNumber].row;
313                         if (originalLineNumber === lineNumber || originalLineNumber === undefined)
314                             newLineNumber = shiftedLineNumber;
315                     }
316                     break;
317                 }
318             }
319             if (newLineNumber !== undefined)
320                 this.setBreakpoint(uiSourceCode, newLineNumber, breakpoint.condition, breakpoint.enabled);
321         }
322     },
323
324     /**
325      * @param {boolean} formatSource
326      */
327     setFormatSource: function(formatSource)
328     {
329         if (this._formatSource === formatSource)
330             return;
331
332         this._formatSource = formatSource;
333         this._breakpointManager.reset();
334         for (var id in this._rawSourceCodeForScriptId)
335             this._rawSourceCodeForScriptId[id].setFormatted(this._formatSource);
336     },
337
338     /**
339      * @param {WebInspector.UISourceCode} uiSourceCode
340      * @param {string} sourceMappingURL
341      */
342     setCompilerSourceMapping: function(uiSourceCode, sourceMappingURL)
343     {
344         var sourceMapping = new WebInspector.ClosureCompilerSourceMapping(sourceMappingURL);
345         uiSourceCode.rawSourceCode.setCompilerSourceMapping(sourceMapping);
346     },
347
348     /**
349      * @param {WebInspector.Event} event
350      */
351     _consoleMessageAdded: function(event)
352     {
353         var message = /** @type {WebInspector.ConsoleMessage} */ event.data;
354         if (!message.url || !message.isErrorOrWarning() || !message.message)
355             return;
356
357         var rawSourceCode = this._rawSourceCodeForScriptWithURL(message.url);
358         if (!rawSourceCode)
359             return;
360
361         rawSourceCode.messages.push(message);
362         if (rawSourceCode.sourceMapping) {
363             message._presentationMessage = this._createPresentationMessage(message, rawSourceCode.sourceMapping);
364             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, message._presentationMessage);
365         }
366     },
367
368     /**
369      * @param {WebInspector.ConsoleMessage} message
370      * @param {WebInspector.RawSourceCode.SourceMapping} sourceMapping
371      * @return {WebInspector.PresentationConsoleMessage}
372      */
373     _createPresentationMessage: function(message, sourceMapping)
374     {
375         // FIXME(62725): stack trace line/column numbers are one-based.
376         var lineNumber = message.stackTrace ? message.stackTrace[0].lineNumber - 1 : message.line - 1;
377         var columnNumber = message.stackTrace ? message.stackTrace[0].columnNumber - 1 : 0;
378         var uiLocation = sourceMapping.rawLocationToUILocation(/** @type {DebuggerAgent.Location} */ { lineNumber: lineNumber, columnNumber: columnNumber });
379         var presentationMessage = new WebInspector.PresentationConsoleMessage(uiLocation.uiSourceCode, uiLocation.lineNumber, message);
380         return presentationMessage;
381     },
382
383     _consoleCleared: function()
384     {
385         for (var id in this._rawSourceCodeForScriptId)
386             this._rawSourceCodeForScriptId[id].messages = [];
387         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessagesCleared);
388     },
389
390     /**
391      * @param {WebInspector.UISourceCode} uiSourceCode
392      * @param {number} lineNumber
393      */
394     continueToLine: function(uiSourceCode, lineNumber)
395     {
396         // FIXME: use RawSourceCode.uiLocationToRawLocation.
397         var rawLocation = uiSourceCode.rawSourceCode.sourceMapping.uiLocationToRawLocation(uiSourceCode, lineNumber, 0);
398         WebInspector.debuggerModel.continueToLocation(rawLocation);
399     },
400
401     /**
402      * @param {WebInspector.UISourceCode} uiSourceCode
403      * @return {Array.<WebInspector.Breakpoint>}
404      */
405     breakpointsForUISourceCode: function(uiSourceCode)
406     {
407         var breakpointsMap = this._breakpointManager.breakpointsForUISourceCode(uiSourceCode);
408         var breakpointsList = [];
409         for (var lineNumber in breakpointsMap)
410             breakpointsList.push(breakpointsMap[lineNumber]);
411         return breakpointsList;
412     },
413
414     /**
415      * @param {WebInspector.UISourceCode} uiSourceCode
416      * @return {Array.<WebInspector.ConsoleMessage>}
417      */
418     messagesForUISourceCode: function(uiSourceCode)
419     {
420         var rawSourceCode = uiSourceCode.rawSourceCode;
421         var messages = [];
422         for (var i = 0; i < rawSourceCode.messages.length; ++i)
423             messages.push(rawSourceCode.messages[i]._presentationMessage);
424         return messages;
425     },
426
427     /**
428      * @param {WebInspector.UISourceCode} uiSourceCode
429      * @param {number} lineNumber
430      * @param {string} condition
431      * @param {boolean} enabled
432      */
433     setBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
434     {
435         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, condition, enabled);
436     },
437
438     /**
439      * @param {WebInspector.UISourceCode} uiSourceCode
440      * @param {number} lineNumber
441      * @param {boolean} enabled
442      */
443     setBreakpointEnabled: function(uiSourceCode, lineNumber, enabled)
444     {
445         var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber);
446         if (!breakpoint)
447             return;
448         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
449         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, breakpoint.condition, enabled);
450     },
451
452     /**
453      * @param {WebInspector.UISourceCode} uiSourceCode
454      * @param {number} lineNumber
455      * @param {string} condition
456      * @param {boolean} enabled
457      */
458     updateBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
459     {
460         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
461         this._breakpointManager.setBreakpoint(uiSourceCode, lineNumber, condition, enabled);
462     },
463
464     /**
465      * @param {WebInspector.UISourceCode} uiSourceCode
466      * @param {number} lineNumber
467      */
468     removeBreakpoint: function(uiSourceCode, lineNumber)
469     {
470         this._breakpointManager.removeBreakpoint(uiSourceCode, lineNumber);
471     },
472
473     /**
474      * @param {WebInspector.UISourceCode} uiSourceCode
475      * @param {number} lineNumber
476      * @return {WebInspector.Breakpoint|undefined}
477      */
478     findBreakpoint: function(uiSourceCode, lineNumber)
479     {
480         return this._breakpointManager.breakpointsForUISourceCode(uiSourceCode)[lineNumber];
481     },
482
483     /**
484      * @param {WebInspector.Breakpoint} breakpoint
485      */
486     _breakpointAdded: function(breakpoint)
487     {
488         if (breakpoint.uiSourceCode)
489             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
490     },
491
492     /**
493      * @param {WebInspector.Breakpoint} breakpoint
494      */
495     _breakpointRemoved: function(breakpoint)
496     {
497         if (breakpoint.uiSourceCode)
498             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
499     },
500
501     _debuggerPaused: function()
502     {
503         var callFrames = WebInspector.debuggerModel.callFrames;
504         this._presentationCallFrames = [];
505         for (var i = 0; i < callFrames.length; ++i) {
506             var callFrame = callFrames[i];
507             var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.scriptId);
508             if (!script)
509                 continue;
510             var rawSourceCode = this._rawSourceCodeForScript(script);
511             this._presentationCallFrames.push(new WebInspector.PresentationCallFrame(callFrame, i, this, rawSourceCode));
512         }
513         var details = WebInspector.debuggerModel.debuggerPausedDetails;
514         this.selectedCallFrame = this._presentationCallFrames[0];
515         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
516     },
517
518     _debuggerResumed: function()
519     {
520         this._presentationCallFrames = [];
521         this.selectedCallFrame = null;
522         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
523     },
524
525     get paused()
526     {
527         return !!WebInspector.debuggerModel.debuggerPausedDetails;
528     },
529
530     set selectedCallFrame(callFrame)
531     {
532         if (this._selectedCallFrame)
533             this._selectedCallFrame.rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._dispatchExecutionLineChanged, this);
534         this._selectedCallFrame = callFrame;
535         if (!this._selectedCallFrame)
536             return;
537
538         this._selectedCallFrame.rawSourceCode.forceUpdateSourceMapping();
539         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
540
541         this._selectedCallFrame.rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._dispatchExecutionLineChanged, this);
542     },
543
544     get selectedCallFrame()
545     {
546         return this._selectedCallFrame;
547     },
548
549     /**
550      * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback
551      */
552     evaluateInSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, returnByValue, callback)
553     {
554         /**
555          * @param {?RuntimeAgent.RemoteObject} result
556          * @param {boolean} wasThrown
557          */
558         function didEvaluate(result, wasThrown)
559         {
560             if (returnByValue)
561                 callback(null, wasThrown, wasThrown ? null : result);
562             else
563                 callback(WebInspector.RemoteObject.fromPayload(result), wasThrown);
564
565             if (objectGroup === "console")
566                 this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame);
567         }
568
569         this.selectedCallFrame.evaluate(code, objectGroup, includeCommandLineAPI, returnByValue, didEvaluate.bind(this));
570     },
571
572     /**
573      * @param {function(Object)} callback
574      */
575     getSelectedCallFrameVariables: function(callback)
576     {
577         var result = { this: true };
578
579         var selectedCallFrame = this.selectedCallFrame;
580         if (!selectedCallFrame)
581             callback(result);
582
583         var pendingRequests = 0;
584
585         function propertiesCollected(properties)
586         {
587             for (var i = 0; properties && i < properties.length; ++i)
588                 result[properties[i].name] = true;
589             if (--pendingRequests == 0)
590                 callback(result);
591         }
592
593         for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) {
594             var scope = selectedCallFrame.scopeChain[i];
595             var object = WebInspector.RemoteObject.fromPayload(scope.object);
596             pendingRequests++;
597             object.getAllProperties(propertiesCollected);
598         }
599     },
600
601     /**
602      * @param {WebInspector.Event} event
603      */
604     _dispatchExecutionLineChanged: function(event)
605     {
606         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ExecutionLineChanged, this.executionLineLocation);
607     },
608
609     /**
610      * @type {WebInspector.UILocation}
611      */
612     get executionLineLocation()
613     {
614         if (!this._selectedCallFrame.rawSourceCode.sourceMapping)
615             return;
616
617         var rawLocation = this._selectedCallFrame._callFrame.location;
618         var uiLocation = this._selectedCallFrame.rawSourceCode.sourceMapping.rawLocationToUILocation(rawLocation);
619         return uiLocation;
620     },
621
622     /**
623      * @param {string} sourceURL
624      */
625     _rawSourceCodeForScriptWithURL: function(sourceURL)
626     {
627         return this._rawSourceCodeForURL[sourceURL];
628     },
629
630     /**
631      * @param {WebInspector.Script} script
632      */
633     _rawSourceCodeForScript: function(script)
634     {
635         return this._rawSourceCodeForScriptId[script.scriptId];
636     },
637
638     /**
639      * @param {WebInspector.RawSourceCode} rawSourceCode
640      */
641     _scriptForRawSourceCode: function(rawSourceCode)
642     {
643         /**
644          * @this {WebInspector.DebuggerPresentationModel}
645          * @param {WebInspector.Script} script
646          * @return {boolean}
647          */
648         function filter(script)
649         {
650             return script.scriptId === rawSourceCode.id;
651         }
652         return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0];
653     },
654
655     _debuggerReset: function()
656     {
657         for (var id in this._rawSourceCodeForScriptId) {
658             var rawSourceCode = this._rawSourceCodeForScriptId[id];
659             if (rawSourceCode.sourceMapping) {
660                 var uiSourceCodeList = rawSourceCode.sourceMapping.uiSourceCodeList();
661                 for (var i = 0; i < uiSourceCodeList.length; ++i)
662                     this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.UISourceCodeRemoved, uiSourceCodeList[i]);
663             }
664             rawSourceCode.removeAllListeners();
665         }
666         this._rawSourceCodeForScriptId = {};
667         this._rawSourceCodeForURL = {};
668         this._rawSourceCodeForDocumentURL = {};
669         this._presentationCallFrames = [];
670         this._selectedCallFrame = null;
671         this._breakpointManager.debuggerReset();
672         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerReset);
673     }
674 }
675
676 WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
677
678 /**
679  * @constructor
680  * @param {WebInspector.UISourceCode} uiSourceCode
681  * @param {number} lineNumber
682  * @param {WebInspector.ConsoleMessage} originalMessage
683  */
684 WebInspector.PresentationConsoleMessage = function(uiSourceCode, lineNumber, originalMessage)
685 {
686     this.uiSourceCode = uiSourceCode;
687     this.lineNumber = lineNumber;
688     this.originalMessage = originalMessage;
689 }
690
691 /**
692  * @constructor
693  * @param {DebuggerAgent.CallFrame} callFrame
694  * @param {number} index
695  * @param {WebInspector.DebuggerPresentationModel} model
696  * @param {WebInspector.RawSourceCode} rawSourceCode
697  */
698 WebInspector.PresentationCallFrame = function(callFrame, index, model, rawSourceCode)
699 {
700     this._callFrame = callFrame;
701     this._index = index;
702     this._model = model;
703     this._rawSourceCode = rawSourceCode;
704 }
705
706 WebInspector.PresentationCallFrame.prototype = {
707     /**
708      * @return {string}
709      */
710     get type()
711     {
712         return this._callFrame.type;
713     },
714
715     /**
716      * @return {Array.<DebuggerAgent.Scope>}
717      */
718     get scopeChain()
719     {
720         return this._callFrame.scopeChain;
721     },
722
723     /**
724      * @return {RuntimeAgent.RemoteObject}
725      */
726     get this()
727     {
728         return this._callFrame.this;
729     },
730
731     /**
732      * @return {number}
733      */
734     get index()
735     {
736         return this._index;
737     },
738
739     /**
740      * @return {WebInspector.RawSourceCode}
741      */
742     get rawSourceCode()
743     {
744         return this._rawSourceCode;
745     },
746
747     /**
748      * @param {string} code
749      * @param {string} objectGroup
750      * @param {boolean} includeCommandLineAPI
751      * @param {boolean} returnByValue
752      * @param {function(?RuntimeAgent.RemoteObject, boolean)=} callback
753      */
754     evaluate: function(code, objectGroup, includeCommandLineAPI, returnByValue, callback)
755     {
756         /**
757          * @this {WebInspector.PresentationCallFrame}
758          * @param {?Protocol.Error} error
759          * @param {RuntimeAgent.RemoteObject} result
760          * @param {boolean} wasThrown
761          */
762         function didEvaluateOnCallFrame(error, result, wasThrown)
763         {
764             if (error) {
765                 console.error(error);
766                 callback(null, false);
767                 return;
768             }
769             callback(result, wasThrown);
770         }
771         DebuggerAgent.evaluateOnCallFrame(this._callFrame.callFrameId, code, objectGroup, includeCommandLineAPI, returnByValue, didEvaluateOnCallFrame.bind(this));
772     },
773
774     /**
775      * @param {function(WebInspector.UILocation)} callback
776      */
777     uiLocation: function(callback)
778     {
779         function sourceMappingReady()
780         {
781             this._rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
782             callback(this._rawSourceCode.sourceMapping.rawLocationToUILocation(this._callFrame.location));
783         }
784         if (this._rawSourceCode.sourceMapping)
785             sourceMappingReady.call(this);
786         else
787             this._rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, sourceMappingReady, this);
788     }
789 }
790
791 /**
792  * @constructor
793  * @extends {WebInspector.Placard}
794  * @param {WebInspector.PresentationCallFrame} callFrame
795  */
796 WebInspector.DebuggerPresentationModel.CallFramePlacard = function(callFrame)
797 {
798     WebInspector.Placard.call(this, callFrame._callFrame.functionName || WebInspector.UIString("(anonymous function)"), "");
799     this._callFrame = callFrame;
800     var rawSourceCode = callFrame._rawSourceCode;
801     if (rawSourceCode.sourceMapping)
802         this._update();
803     rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._update, this);
804 }
805
806 WebInspector.DebuggerPresentationModel.CallFramePlacard.prototype = {
807     discard: function()
808     {
809         this._callFrame._rawSourceCode.removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._update, this);
810     },
811
812     _update: function()
813     {
814         var rawSourceCode = this._callFrame._rawSourceCode;
815         var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(this._callFrame._callFrame.location);
816         this.subtitle = WebInspector.displayNameForURL(uiLocation.uiSourceCode.url) + ":" + (uiLocation.lineNumber + 1);
817     }
818 }
819
820 WebInspector.DebuggerPresentationModel.CallFramePlacard.prototype.__proto__ = WebInspector.Placard.prototype;
821
822 /**
823  * @constructor
824  * @implements {WebInspector.ResourceDomainModelBinding}
825  * @param {WebInspector.DebuggerPresentationModel} model
826  */
827 WebInspector.DebuggerPresentationModelResourceBinding = function(model)
828 {
829     this._presentationModel = model;
830     WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
831 }
832
833 WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
834     /**
835      * @param {WebInspector.Resource} resource
836      */
837     canSetContent: function(resource)
838     {
839         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url)
840         if (!rawSourceCode)
841             return false;
842         return this._presentationModel.canEditScriptSource(rawSourceCode.sourceMapping.uiSourceCodeList()[0]);
843     },
844
845     /**
846      * @param {WebInspector.Resource} resource
847      * @param {string} content
848      * @param {boolean} majorChange
849      * @param {function(?Protocol.Error)} userCallback
850      */
851     setContent: function(resource, content, majorChange, userCallback)
852     {
853         if (!majorChange)
854             return;
855
856         var rawSourceCode = this._presentationModel._rawSourceCodeForScriptWithURL(resource.url);
857         if (!rawSourceCode) {
858             userCallback("Resource is not editable");
859             return;
860         }
861
862         resource.requestContent(this._setContentWithInitialContent.bind(this, rawSourceCode.sourceMapping.uiSourceCodeList()[0], content, userCallback));
863     },
864
865     /**
866      * @param {WebInspector.UISourceCode} uiSourceCode
867      * @param {string} content
868      * @param {function(?Protocol.Error)} userCallback
869      * @param {string} oldContent
870      */
871     _setContentWithInitialContent: function(uiSourceCode, content, userCallback, oldContent)
872     {
873         /**
874          * @this {WebInspector.DebuggerPresentationModelResourceBinding}
875          * @param {?Protocol.Error} error
876          */
877         function callback(error)
878         {
879             if (userCallback)
880                 userCallback(error);
881             if (!error)
882                 this._presentationModel._updateBreakpointsAfterLiveEdit(uiSourceCode, oldContent, content);
883         }
884         this._presentationModel.setScriptSource(uiSourceCode, content, callback.bind(this));
885     }
886 }
887
888 /**
889  * @interface
890  */
891 WebInspector.DebuggerPresentationModel.LinkifierFormatter = function()
892 {
893 }
894
895 WebInspector.DebuggerPresentationModel.LinkifierFormatter.prototype = {
896     /**
897      * @param {WebInspector.RawSourceCode} rawSourceCode
898      * @param {Element} anchor
899      */
900     formatRawSourceCodeAnchor: function(rawSourceCode, anchor) { },
901 }
902
903 /**
904  * @constructor
905  * @implements {WebInspector.DebuggerPresentationModel.LinkifierFormatter}
906  * @param {number=} maxLength
907  */
908 WebInspector.DebuggerPresentationModel.DefaultLinkifierFormatter = function(maxLength)
909 {
910     this._maxLength = maxLength;
911 }
912
913 WebInspector.DebuggerPresentationModel.DefaultLinkifierFormatter.prototype = {
914     /**
915      * @param {WebInspector.RawSourceCode} rawSourceCode
916      * @param {Element} anchor
917      */
918     formatRawSourceCodeAnchor: function(rawSourceCode, anchor)
919     {
920         var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(anchor.rawLocation);
921
922         anchor.textContent = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
923
924         var text = WebInspector.formatLinkText(uiLocation.uiSourceCode.url, uiLocation.lineNumber);
925         if (this._maxLength)
926             text = text.trimMiddle(this._maxLength);
927         anchor.textContent = text;
928     }
929 }
930
931 WebInspector.DebuggerPresentationModel.DefaultLinkifierFormatter.prototype.__proto__ = WebInspector.DebuggerPresentationModel.LinkifierFormatter.prototype;
932
933 /**
934  * @constructor
935  * @param {WebInspector.DebuggerPresentationModel} model
936  * @param {WebInspector.DebuggerPresentationModel.LinkifierFormatter=} formatter
937  */
938 WebInspector.DebuggerPresentationModel.Linkifier = function(model, formatter)
939 {
940     this._model = model;
941     this._formatter = formatter || new WebInspector.DebuggerPresentationModel.DefaultLinkifierFormatter();
942     this._anchorsForRawSourceCode = {};
943 }
944
945 WebInspector.DebuggerPresentationModel.Linkifier.prototype = {
946     /**
947      * @param {string} sourceURL
948      * @param {number} lineNumber
949      * @param {number=} columnNumber
950      * @param {string=} classes
951      */
952     linkifyLocation: function(sourceURL, lineNumber, columnNumber, classes)
953     {
954         var rawSourceCode = this._model._rawSourceCodeForScriptWithURL(sourceURL);
955         if (!rawSourceCode)
956             return this.linkifyResource(sourceURL, lineNumber, classes);
957
958         return this.linkifyRawSourceCode(rawSourceCode, lineNumber, columnNumber, classes);
959     },
960
961     /**
962      * @param {string} sourceURL
963      * @param {number} lineNumber
964      * @param {string=} classes
965      */
966     linkifyResource: function(sourceURL, lineNumber, classes)
967     {
968         var linkText = WebInspector.formatLinkText(sourceURL, lineNumber);
969         var anchor = WebInspector.linkifyURLAsNode(sourceURL, linkText, classes, false);
970         anchor.setAttribute("preferred_panel", "resources");
971         anchor.setAttribute("line_number", lineNumber);
972         return anchor;
973     },
974
975     /**
976      * @param {WebInspector.RawSourceCode} rawSourceCode
977      * @param {number=} lineNumber
978      * @param {number=} columnNumber
979      * @param {string=} classes
980      */
981     linkifyRawSourceCode: function(rawSourceCode, lineNumber, columnNumber, classes)
982     {
983         var anchor = WebInspector.linkifyURLAsNode(rawSourceCode.url, "", classes, false);
984         anchor.rawLocation = { lineNumber: lineNumber, columnNumber: columnNumber };
985
986         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
987         if (!anchors) {
988             anchors = [];
989             this._anchorsForRawSourceCode[rawSourceCode.id] = anchors;
990             rawSourceCode.addEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
991         }
992
993         if (rawSourceCode.sourceMapping)
994             this._updateAnchor(rawSourceCode, anchor);
995         anchors.push(anchor);
996         return anchor;
997     },
998
999     reset: function()
1000     {
1001         for (var id in this._anchorsForRawSourceCode) {
1002             if (this._model._rawSourceCodeForScriptId[id]) // In case of navigation the list of rawSourceCodes is empty.
1003                 this._model._rawSourceCodeForScriptId[id].removeEventListener(WebInspector.RawSourceCode.Events.SourceMappingUpdated, this._updateSourceAnchors, this);
1004         }
1005         this._anchorsForRawSourceCode = {};
1006     },
1007
1008     /**
1009      * @param {WebInspector.Event} event
1010      */
1011     _updateSourceAnchors: function(event)
1012     {
1013         var rawSourceCode = /** @type {WebInspector.RawSourceCode} */ event.target;
1014         var anchors = this._anchorsForRawSourceCode[rawSourceCode.id];
1015         for (var i = 0; i < anchors.length; ++i)
1016             this._updateAnchor(rawSourceCode, anchors[i]);
1017     },
1018
1019     /**
1020      * @param {WebInspector.RawSourceCode} rawSourceCode
1021      * @param {Element} anchor
1022      */
1023     _updateAnchor: function(rawSourceCode, anchor)
1024     {
1025         var uiLocation = rawSourceCode.sourceMapping.rawLocationToUILocation(anchor.rawLocation);
1026         anchor.setAttribute("preferred_panel", "scripts");
1027         anchor.uiSourceCode = uiLocation.uiSourceCode;
1028         anchor.lineNumber = uiLocation.lineNumber;
1029
1030         this._formatter.formatRawSourceCodeAnchor(rawSourceCode, anchor);
1031     }
1032 }
1033
1034 WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
1035
1036 /**
1037  * @type {?WebInspector.DebuggerPresentationModel}
1038  */
1039 WebInspector.debuggerPresentationModel = null;