• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
31WebInspector.DebuggerPresentationModel = function()
32{
33    this._sourceFiles = {};
34    this._messages = [];
35    this._breakpointsByDebuggerId = {};
36    this._breakpointsWithoutSourceFile = {};
37
38    this._presentationCallFrames = [];
39    this._selectedCallFrameIndex = 0;
40
41    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
42    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
43    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
44    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
45    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
46    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
47    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._debuggerReset, this);
48
49    new WebInspector.DebuggerPresentationModelResourceBinding(this);
50}
51
52WebInspector.DebuggerPresentationModel.Events = {
53    SourceFileAdded: "source-file-added",
54    SourceFileChanged: "source-file-changed",
55    ConsoleMessageAdded: "console-message-added",
56    BreakpointAdded: "breakpoint-added",
57    BreakpointRemoved: "breakpoint-removed",
58    DebuggerPaused: "debugger-paused",
59    DebuggerResumed: "debugger-resumed",
60    CallFrameSelected: "call-frame-selected"
61}
62
63WebInspector.DebuggerPresentationModel.prototype = {
64    _debuggerWasEnabled: function()
65    {
66        if (this._breakpointsRestored)
67            return;
68        this._restoreBreakpointsFromSettings();
69        this._breakpointsRestored = true;
70    },
71
72    sourceFile: function(sourceFileId)
73    {
74        return this._sourceFiles[sourceFileId];
75    },
76
77    sourceFileForScriptURL: function(scriptURL)
78    {
79        return this._sourceFiles[scriptURL];
80    },
81
82    requestSourceFileContent: function(sourceFileId, callback)
83    {
84        this._sourceFiles[sourceFileId].requestContent(callback);
85    },
86
87    _parsedScriptSource: function(event)
88    {
89        this._addScript(event.data);
90    },
91
92    _failedToParseScriptSource: function(event)
93    {
94        this._addScript(event.data);
95    },
96
97    _addScript: function(script)
98    {
99        var sourceFileId = this._createSourceFileId(script.sourceURL, script.sourceID);
100        var sourceFile = this._sourceFiles[sourceFileId];
101        if (sourceFile) {
102            sourceFile.addScript(script);
103            return;
104        }
105
106        function contentChanged(sourceFile)
107        {
108            this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFiles[sourceFileId]);
109        }
110        if (!this._formatSourceFiles)
111            sourceFile = new WebInspector.SourceFile(sourceFileId, script, contentChanged.bind(this));
112        else
113            sourceFile = new WebInspector.FormattedSourceFile(sourceFileId, script, contentChanged.bind(this), this._formatter());
114        this._sourceFiles[sourceFileId] = sourceFile;
115
116        this._restoreBreakpoints(sourceFile);
117
118        this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, sourceFile);
119    },
120
121    _restoreBreakpoints: function(sourceFile)
122    {
123        var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
124        for (var i = 0; pendingBreakpoints && i < pendingBreakpoints.length; ++i) {
125            var breakpointData = pendingBreakpoints[i];
126            if ("debuggerId" in breakpointData) {
127                var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
128                this._bindDebuggerId(breakpoint, breakpointData.debuggerId);
129                this._breakpointAdded(breakpoint);
130            } else
131                this.setBreakpoint(sourceFile.id, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled, true);
132        }
133        delete this._breakpointsWithoutSourceFile[sourceFile.id];
134    },
135
136    canEditScriptSource: function(sourceFileId)
137    {
138        if (!Preferences.canEditScriptSource || this._formatSourceFiles)
139            return false;
140        var script = this._scriptForSourceFileId(sourceFileId);
141        return !script.lineOffset && !script.columnOffset;
142    },
143
144    editScriptSource: function(sourceFileId, newSource, callback)
145    {
146        var script = this._scriptForSourceFileId(sourceFileId);
147        var sourceFile = this._sourceFiles[sourceFileId];
148
149        function didEditScriptSource(oldSource, error)
150        {
151            if (!error) {
152                sourceFile.content = newSource;
153
154                var resource = WebInspector.resourceForURL(sourceFile.url);
155                if (resource)
156                    resource.addRevision(newSource);
157            }
158
159            callback(error);
160
161            if (!error && WebInspector.debuggerModel.callFrames)
162                this._debuggerPaused();
163        }
164
165        var oldSource = sourceFile.requestContent(didReceiveSource.bind(this));
166        function didReceiveSource(oldSource)
167        {
168            WebInspector.debuggerModel.editScriptSource(script.sourceID, newSource, didEditScriptSource.bind(this, oldSource));
169        }
170    },
171
172    _updateBreakpointsAfterLiveEdit: function(sourceFileId, oldSource, newSource)
173    {
174        var sourceFile = this._sourceFiles[sourceFileId];
175
176        // Clear and re-create breakpoints according to text diff.
177        var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
178        for (var lineNumber in sourceFile.breakpoints) {
179            var breakpoint = sourceFile.breakpoints[lineNumber];
180
181            var lineNumber = breakpoint.lineNumber;
182            this.removeBreakpoint(sourceFileId, lineNumber);
183
184            var newLineNumber = diff.left[lineNumber].row;
185            if (newLineNumber === undefined) {
186                for (var i = lineNumber - 1; i >= 0; --i) {
187                    if (diff.left[i].row === undefined)
188                        continue;
189                    var shiftedLineNumber = diff.left[i].row + lineNumber - i;
190                    if (shiftedLineNumber < diff.right.length) {
191                        var originalLineNumber = diff.right[shiftedLineNumber].row;
192                        if (originalLineNumber === lineNumber || originalLineNumber === undefined)
193                            newLineNumber = shiftedLineNumber;
194                    }
195                    break;
196                }
197            }
198            if (newLineNumber !== undefined)
199                this.setBreakpoint(sourceFileId, newLineNumber, breakpoint.condition, breakpoint.enabled);
200        }
201    },
202
203    toggleFormatSourceFiles: function()
204    {
205        this._formatSourceFiles = !this._formatSourceFiles;
206
207        for (var id in this._sourceFiles) {
208            var sourceFile = this._sourceFiles[id];
209            for (var line in sourceFile.breakpoints)
210                this._removeBreakpointFromDebugger(sourceFile.breakpoints[line]);
211        }
212
213        var messages = this._messages;
214        this._reset();
215
216        var scripts = WebInspector.debuggerModel.scripts;
217        for (var id in scripts)
218            this._addScript(scripts[id]);
219
220        for (var i = 0; i < messages.length; ++i)
221            this.addConsoleMessage(messages[i]);
222
223        if (WebInspector.debuggerModel.callFrames)
224            this._debuggerPaused();
225    },
226
227    formatSourceFilesToggled: function()
228    {
229        return this._formatSourceFiles;
230    },
231
232    _formatter: function()
233    {
234        if (!this._scriptFormatter)
235            this._scriptFormatter = new WebInspector.ScriptFormatter();
236        return this._scriptFormatter;
237    },
238
239    addConsoleMessage: function(message)
240    {
241        this._messages.push(message);
242
243        var sourceFile = this._sourceFileForScript(message.url);
244        if (!sourceFile)
245            return;
246
247        function didRequestSourceMapping(mapping)
248        {
249            var presentationMessage = {};
250            presentationMessage.sourceFileId = sourceFile.id;
251            presentationMessage.lineNumber = mapping.scriptLocationToSourceLine({lineNumber:message.line - 1, columnNumber:0});
252            presentationMessage.originalMessage = message;
253            sourceFile.messages.push(presentationMessage);
254            this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, presentationMessage);
255        }
256        sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
257    },
258
259    clearConsoleMessages: function()
260    {
261        this._messages = [];
262        for (var id in this._sourceFiles)
263            this._sourceFiles[id].messages = [];
264    },
265
266    continueToLine: function(sourceFileId, lineNumber)
267    {
268        function didRequestSourceMapping(mapping)
269        {
270            var location = mapping.sourceLineToScriptLocation(lineNumber);
271            WebInspector.debuggerModel.continueToLocation(location);
272        }
273        this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this));
274    },
275
276    breakpointsForSourceFileId: function(sourceFileId)
277    {
278        var sourceFile = this.sourceFile(sourceFileId);
279        if (!sourceFile)
280            return [];
281        var breakpoints = [];
282        for (var lineNumber in sourceFile.breakpoints)
283            breakpoints.push(sourceFile.breakpoints[lineNumber]);
284        return breakpoints;
285    },
286
287    setBreakpoint: function(sourceFileId, lineNumber, condition, enabled, dontSaveBreakpoints)
288    {
289        var sourceFile = this._sourceFiles[sourceFileId];
290        if (!sourceFile)
291            return;
292
293        var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, lineNumber, condition, enabled);
294        if (!enabled) {
295            this._breakpointAdded(breakpoint);
296            if (!dontSaveBreakpoints)
297                this._saveBreakpoints();
298            return;
299        }
300
301        function callback()
302        {
303            this._breakpointAdded(breakpoint);
304            if (!dontSaveBreakpoints)
305                this._saveBreakpoints();
306        }
307        this._setBreakpointInDebugger(breakpoint, callback.bind(this));
308    },
309
310    _setBreakpointInDebugger: function(breakpoint, callback)
311    {
312        function didSetBreakpoint(breakpointId, locations)
313        {
314            if (!breakpointId)
315                return;
316
317            this._bindDebuggerId(breakpoint, breakpointId);
318            breakpoint.location = locations[0];
319            callback();
320        }
321
322        function didRequestSourceMapping(mapping)
323        {
324            var location = mapping.sourceLineToScriptLocation(breakpoint.lineNumber);
325            var script = WebInspector.debuggerModel.scriptForSourceID(location.sourceID);
326            if (script.sourceURL)
327                WebInspector.debuggerModel.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, breakpoint.condition, didSetBreakpoint.bind(this));
328            else {
329                location.sourceID = script.sourceID;
330                WebInspector.debuggerModel.setBreakpointBySourceId(location, breakpoint.condition, didSetBreakpoint.bind(this));
331            }
332        }
333        breakpoint.sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
334    },
335
336    _removeBreakpointFromDebugger: function(breakpoint, callback)
337    {
338        if (!("debuggerId" in breakpoint)) {
339            if (callback)
340                callback();
341            return;
342        }
343
344        function didRemoveBreakpoint()
345        {
346            this._unbindDebuggerId(breakpoint);
347            if (callback)
348                callback();
349        }
350        WebInspector.debuggerModel.removeBreakpoint(breakpoint.debuggerId, didRemoveBreakpoint.bind(this));
351    },
352
353    _bindDebuggerId: function(breakpoint, debuggerId)
354    {
355        breakpoint.debuggerId = debuggerId;
356        this._breakpointsByDebuggerId[debuggerId] = breakpoint;
357    },
358
359    _unbindDebuggerId: function(breakpoint)
360    {
361        delete this._breakpointsByDebuggerId[breakpoint.debuggerId];
362        delete breakpoint.debuggerId;
363    },
364
365    setBreakpointEnabled: function(sourceFileId, lineNumber, enabled)
366    {
367        var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
368        if (!breakpoint)
369            return;
370
371        this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
372
373        breakpoint.enabled = enabled;
374
375        function afterUpdate()
376        {
377            this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
378            this._saveBreakpoints();
379        }
380
381        if (!enabled)
382            this._removeBreakpointFromDebugger(breakpoint, afterUpdate.call(this));
383        else
384            this._setBreakpointInDebugger(breakpoint, afterUpdate.bind(this));
385    },
386
387    updateBreakpoint: function(sourceFileId, lineNumber, condition, enabled)
388    {
389        this.removeBreakpoint(sourceFileId, lineNumber);
390        this.setBreakpoint(sourceFileId, lineNumber, condition, enabled);
391    },
392
393    removeBreakpoint: function(sourceFileId, lineNumber)
394    {
395        var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
396        if (!breakpoint)
397            return;
398
399        function callback()
400        {
401            this._breakpointRemoved(breakpoint);
402            this._saveBreakpoints();
403        }
404        this._removeBreakpointFromDebugger(breakpoint, callback.bind(this));
405    },
406
407    findBreakpoint: function(sourceFileId, lineNumber)
408    {
409        var sourceFile = this.sourceFile(sourceFileId);
410        if (sourceFile)
411            return sourceFile.breakpoints[lineNumber];
412    },
413
414    _breakpointAdded: function(breakpoint)
415    {
416        var sourceFile = breakpoint.sourceFile;
417        if (!sourceFile)
418            return;
419
420        function didRequestSourceMapping(mapping)
421        {
422            // Refine line number based on resolved location.
423            if (breakpoint.location)
424                breakpoint.lineNumber = mapping.scriptLocationToSourceLine(breakpoint.location);
425
426            var existingBreakpoint = this.findBreakpoint(sourceFile.id, breakpoint.lineNumber);
427            if (existingBreakpoint) {
428                // We can't show more than one breakpoint on a single source file line.
429                this._removeBreakpointFromDebugger(breakpoint);
430                return;
431            }
432            sourceFile.breakpoints[breakpoint.lineNumber] = breakpoint;
433            this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
434        }
435        sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
436    },
437
438    _breakpointRemoved: function(breakpoint)
439    {
440        var sourceFile = breakpoint.sourceFile;
441        if (sourceFile.breakpoints[breakpoint.lineNumber] === breakpoint) {
442            // There can already be a newer breakpoint;
443            delete sourceFile.breakpoints[breakpoint.lineNumber];
444            this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
445        }
446    },
447
448    _breakpointResolved: function(event)
449    {
450        var debuggerId = event.data.breakpointId;
451        if (!(debuggerId in this._breakpointsByDebuggerId))
452            return;
453        var breakpoint = this._breakpointsByDebuggerId[debuggerId];
454
455        this._breakpointRemoved(breakpoint);
456        breakpoint.location = event.data.location;
457        this._breakpointAdded(breakpoint);
458    },
459
460    _restoreBreakpointsFromSettings: function()
461    {
462        var breakpoints = WebInspector.settings.breakpoints;
463        for (var i = 0; i < breakpoints.length; ++i) {
464            var breakpointData = breakpoints[i];
465            var sourceFileId = breakpointData.sourceFileId;
466            if (!sourceFileId)
467                continue;
468            var sourceFile = this._sourceFiles[sourceFileId];
469            if (sourceFile) {
470                this.setBreakpoint(sourceFileId, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
471                continue;
472            }
473
474            // Add breakpoint once source file becomes available.
475            var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFileId];
476            if (!pendingBreakpoints) {
477                pendingBreakpoints = [];
478                this._breakpointsWithoutSourceFile[sourceFileId] = pendingBreakpoints;
479            }
480            pendingBreakpoints.push(breakpointData);
481        }
482    },
483
484    _saveBreakpoints: function()
485    {
486        var serializedBreakpoints = [];
487
488        // Store added breakpoints.
489        for (var sourceFileId in this._sourceFiles) {
490            var sourceFile = this._sourceFiles[sourceFileId];
491            if (!sourceFile.url)
492                continue;
493
494            for (var lineNumber in sourceFile.breakpoints)
495                serializedBreakpoints.push(sourceFile.breakpoints[lineNumber].serialize());
496        }
497
498        // Store not added breakpoints.
499        for (var sourceFileId in this._breakpointsWithoutSourceFile)
500            serializedBreakpoints = serializedBreakpoints.concat(this._breakpointsWithoutSourceFile[sourceFileId]);
501
502        // Sanitize debugger ids.
503        for (var i = 0; i < serializedBreakpoints.length; ++i) {
504            var breakpoint = serializedBreakpoints[i];
505            var breakpointCopy = {};
506            for (var property in breakpoint) {
507                if (property !== "debuggerId")
508                    breakpointCopy[property] = breakpoint[property];
509            }
510            serializedBreakpoints[i] = breakpointCopy;
511        }
512
513        WebInspector.settings.breakpoints = serializedBreakpoints;
514    },
515
516    _debuggerPaused: function()
517    {
518        var callFrames = WebInspector.debuggerModel.callFrames;
519        this._presentationCallFrames = [];
520        for (var i = 0; i < callFrames.length; ++i) {
521            var callFrame = callFrames[i];
522            var sourceFile;
523            var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
524            if (script)
525                sourceFile = this._sourceFileForScript(script.sourceURL, script.sourceID);
526            this._presentationCallFrames.push(new WebInspector.PresenationCallFrame(callFrame, i, sourceFile));
527        }
528        var details = WebInspector.debuggerModel.debuggerPausedDetails;
529        this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
530
531        this.selectedCallFrame = this._presentationCallFrames[this._selectedCallFrameIndex];
532    },
533
534    _debuggerResumed: function()
535    {
536        this._presentationCallFrames = [];
537        this._selectedCallFrameIndex = 0;
538        this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
539    },
540
541    set selectedCallFrame(callFrame)
542    {
543        this._selectedCallFrameIndex = callFrame.index;
544        callFrame.select();
545        this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
546    },
547
548    get selectedCallFrame()
549    {
550        return this._presentationCallFrames[this._selectedCallFrameIndex];
551    },
552
553    _sourceFileForScript: function(sourceURL, sourceID)
554    {
555        return this._sourceFiles[this._createSourceFileId(sourceURL, sourceID)];
556    },
557
558    _scriptForSourceFileId: function(sourceFileId)
559    {
560        function filter(script)
561        {
562            return this._createSourceFileId(script.sourceURL, script.sourceID) === sourceFileId;
563        }
564        return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0];
565    },
566
567    _createSourceFileId: function(sourceURL, sourceID)
568    {
569        var prefix = this._formatSourceFiles ? "deobfuscated:" : "";
570        return prefix + (sourceURL || sourceID);
571    },
572
573    _reset: function()
574    {
575        for (var id in this._sourceFiles) {
576            var sourceFile = this._sourceFiles[id];
577            for (var line in sourceFile.breakpoints) {
578                var breakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
579                if (!breakpoints) {
580                    breakpoints = [];
581                    this._breakpointsWithoutSourceFile[sourceFile.id] = breakpoints;
582                }
583                breakpoints.push(sourceFile.breakpoints[line].serialize());
584            }
585        }
586
587        this._sourceFiles = {};
588        this._messages = [];
589        this._breakpointsByDebuggerId = {};
590    },
591
592    _debuggerReset: function()
593    {
594        this._reset();
595        this._presentationCallFrames = [];
596        this._selectedCallFrameIndex = 0;
597    }
598}
599
600WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
601
602WebInspector.PresentationBreakpoint = function(sourceFile, lineNumber, condition, enabled)
603{
604    this.sourceFile = sourceFile;
605    this.sourceFileId = sourceFile.id;
606    this.lineNumber = lineNumber;
607    this.condition = condition;
608    this.enabled = enabled;
609}
610
611WebInspector.PresentationBreakpoint.prototype = {
612    get url()
613    {
614        return this.sourceFile.url;
615    },
616
617    get resolved()
618    {
619        return !!this.location;
620    },
621
622    loadSnippet: function(callback)
623    {
624        function didRequestContent(mimeType, content)
625        {
626            var lineEndings = content.lineEndings();
627            var snippet = "";
628            if (this.lineNumber < lineEndings.length)
629                snippet = content.substring(lineEndings[this.lineNumber - 1], lineEndings[this.lineNumber]);
630            callback(snippet);
631        }
632        if (!this.sourceFile) {
633            callback(WebInspector.UIString("N/A"));
634            return;
635        }
636        this.sourceFile.requestContent(didRequestContent.bind(this));
637    },
638
639    serialize: function()
640    {
641        var serializedBreakpoint = {};
642        serializedBreakpoint.sourceFileId = this.sourceFile.id;
643        serializedBreakpoint.lineNumber = this.lineNumber;
644        serializedBreakpoint.condition = this.condition;
645        serializedBreakpoint.enabled = this.enabled;
646        serializedBreakpoint.debuggerId = this.debuggerId;
647        return serializedBreakpoint;
648    }
649}
650
651WebInspector.PresenationCallFrame = function(callFrame, index, sourceFile)
652{
653    this._callFrame = callFrame;
654    this._index = index;
655    this._sourceFile = sourceFile;
656    this._script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
657}
658
659WebInspector.PresenationCallFrame.prototype = {
660    get functionName()
661    {
662        return this._callFrame.functionName;
663    },
664
665    get type()
666    {
667        return this._callFrame.type;
668    },
669
670    get isInternalScript()
671    {
672        return !this._script;
673    },
674
675    get url()
676    {
677        if (this._sourceFile)
678            return this._sourceFile.url;
679    },
680
681    get scopeChain()
682    {
683        return this._callFrame.scopeChain;
684    },
685
686    get index()
687    {
688        return this._index;
689    },
690
691    select: function()
692    {
693        if (this._sourceFile)
694            this._sourceFile.forceLoadContent(this._script);
695    },
696
697    evaluate: function(code, objectGroup, includeCommandLineAPI, callback)
698    {
699        function didEvaluateOnCallFrame(error, result)
700        {
701            callback(WebInspector.RemoteObject.fromPayload(result));
702        }
703        DebuggerAgent.evaluateOnCallFrame(this._callFrame.id, code, objectGroup, includeCommandLineAPI, didEvaluateOnCallFrame.bind(this));
704    },
705
706    sourceLine: function(callback)
707    {
708        if (!this._sourceFile) {
709            callback(undefined, this._callFrame.location.lineNumber);
710            return;
711        }
712
713        function didRequestSourceMapping(mapping)
714        {
715            callback(this._sourceFile.id, mapping.scriptLocationToSourceLine(this._callFrame.location));
716        }
717        this._sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
718    }
719}
720
721WebInspector.DebuggerPresentationModelResourceBinding = function(model)
722{
723    this._presentationModel = model;
724    WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
725}
726
727WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
728    canSetContent: function(resource)
729    {
730        var sourceFile = this._presentationModel._sourceFileForScript(resource.url)
731        if (!sourceFile)
732            return false;
733        return this._presentationModel.canEditScriptSource(sourceFile.id);
734    },
735
736    setContent: function(resource, content, majorChange, userCallback)
737    {
738        if (!majorChange)
739            return;
740
741        var sourceFile = this._presentationModel._sourceFileForScript(resource.url);
742        if (!sourceFile) {
743            userCallback("Resource is not editable");
744            return;
745        }
746
747        resource.requestContent(this._setContentWithInitialContent.bind(this, sourceFile, content, userCallback));
748    },
749
750    _setContentWithInitialContent: function(sourceFile, content, userCallback, oldContent)
751    {
752        function callback(error)
753        {
754            if (userCallback)
755                userCallback(error);
756            if (!error) {
757                this._presentationModel._updateBreakpointsAfterLiveEdit(sourceFile.id, oldContent, content);
758                sourceFile.reload();
759            }
760        }
761        this._presentationModel.editScriptSource(sourceFile.id, content, callback.bind(this));
762    }
763}
764
765WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
766