1/*
2 * Copyright (C) 2010 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 */
35WebInspector.DebuggerModel = function()
36{
37    InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerDispatcher(this));
38
39    this._debuggerPausedDetails = null;
40    /**
41     * @type {Object.<string, WebInspector.Script>}
42     */
43    this._scripts = {};
44    this._scriptsBySourceURL = {};
45
46    this._canSetScriptSource = false;
47    this._breakpointsActive = true;
48
49    WebInspector.settings.pauseOnExceptionStateString = WebInspector.settings.createSetting("pauseOnExceptionStateString", WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions);
50    WebInspector.settings.pauseOnExceptionStateString.addChangeListener(this._pauseOnExceptionStateChanged, this);
51
52    if (!Capabilities.debuggerCausesRecompilation || WebInspector.settings.debuggerEnabled.get())
53        this.enableDebugger();
54}
55
56// Keep these in sync with WebCore::ScriptDebugServer
57WebInspector.DebuggerModel.PauseOnExceptionsState = {
58    DontPauseOnExceptions : "none",
59    PauseOnAllExceptions : "all",
60    PauseOnUncaughtExceptions: "uncaught"
61};
62
63/**
64 * @constructor
65 * @implements {WebInspector.RawLocation}
66 * @param {string} scriptId
67 * @param {number} lineNumber
68 * @param {number} columnNumber
69 */
70WebInspector.DebuggerModel.Location = function(scriptId, lineNumber, columnNumber)
71{
72    this.scriptId = scriptId;
73    this.lineNumber = lineNumber;
74    this.columnNumber = columnNumber;
75}
76
77WebInspector.DebuggerModel.Events = {
78    DebuggerWasEnabled: "DebuggerWasEnabled",
79    DebuggerWasDisabled: "DebuggerWasDisabled",
80    DebuggerPaused: "DebuggerPaused",
81    DebuggerResumed: "DebuggerResumed",
82    ParsedScriptSource: "ParsedScriptSource",
83    FailedToParseScriptSource: "FailedToParseScriptSource",
84    BreakpointResolved: "BreakpointResolved",
85    GlobalObjectCleared: "GlobalObjectCleared",
86    CallFrameSelected: "CallFrameSelected",
87    ExecutionLineChanged: "ExecutionLineChanged",
88    ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame",
89    BreakpointsActiveStateChanged: "BreakpointsActiveStateChanged"
90}
91
92WebInspector.DebuggerModel.BreakReason = {
93    DOM: "DOM",
94    EventListener: "EventListener",
95    XHR: "XHR",
96    Exception: "exception",
97    Assert: "assert",
98    CSPViolation: "CSPViolation"
99}
100
101WebInspector.DebuggerModel.prototype = {
102    /**
103     * @return {boolean}
104     */
105    debuggerEnabled: function()
106    {
107        return !!this._debuggerEnabled;
108    },
109
110    enableDebugger: function()
111    {
112        if (this._debuggerEnabled)
113            return;
114
115        function callback(error, result)
116        {
117            this._canSetScriptSource = result;
118        }
119        DebuggerAgent.canSetScriptSource(callback.bind(this));
120        DebuggerAgent.enable(this._debuggerWasEnabled.bind(this));
121    },
122
123    disableDebugger: function()
124    {
125        if (!this._debuggerEnabled)
126            return;
127
128        DebuggerAgent.disable(this._debuggerWasDisabled.bind(this));
129    },
130
131    /**
132     * @return {boolean}
133     */
134    canSetScriptSource: function()
135    {
136        return this._canSetScriptSource;
137    },
138
139    _debuggerWasEnabled: function()
140    {
141        this._debuggerEnabled = true;
142        this._pauseOnExceptionStateChanged();
143        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasEnabled);
144    },
145
146    _pauseOnExceptionStateChanged: function()
147    {
148        DebuggerAgent.setPauseOnExceptions(WebInspector.settings.pauseOnExceptionStateString.get());
149    },
150
151    _debuggerWasDisabled: function()
152    {
153        this._debuggerEnabled = false;
154        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasDisabled);
155    },
156
157    /**
158     * @param {WebInspector.DebuggerModel.Location} rawLocation
159     */
160    continueToLocation: function(rawLocation)
161    {
162        DebuggerAgent.continueToLocation(rawLocation);
163    },
164
165    /**
166     * @param {WebInspector.DebuggerModel.Location} rawLocation
167     * @param {string} condition
168     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>):void=} callback
169     */
170    setBreakpointByScriptLocation: function(rawLocation, condition, callback)
171    {
172        var script = this.scriptForId(rawLocation.scriptId);
173        if (script.sourceURL)
174            this.setBreakpointByURL(script.sourceURL, rawLocation.lineNumber, rawLocation.columnNumber, condition, callback);
175        else
176            this.setBreakpointBySourceId(rawLocation, condition, callback);
177    },
178
179    /**
180     * @param {string} url
181     * @param {number} lineNumber
182     * @param {number=} columnNumber
183     * @param {string=} condition
184     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>)=} callback
185     */
186    setBreakpointByURL: function(url, lineNumber, columnNumber, condition, callback)
187    {
188        // Adjust column if needed.
189        var minColumnNumber = 0;
190        var scripts = this._scriptsBySourceURL[url] || [];
191        for (var i = 0, l = scripts.length; i < l; ++i) {
192            var script = scripts[i];
193            if (lineNumber === script.lineOffset)
194                minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset;
195        }
196        columnNumber = Math.max(columnNumber, minColumnNumber);
197
198        /**
199         * @this {WebInspector.DebuggerModel}
200         * @param {?Protocol.Error} error
201         * @param {DebuggerAgent.BreakpointId} breakpointId
202         * @param {Array.<DebuggerAgent.Location>} locations
203         */
204        function didSetBreakpoint(error, breakpointId, locations)
205        {
206            if (callback) {
207                var rawLocations = /** @type {Array.<WebInspector.DebuggerModel.Location>} */ (locations);
208                callback(error ? null : breakpointId, rawLocations);
209            }
210        }
211        DebuggerAgent.setBreakpointByUrl(lineNumber, url, undefined, columnNumber, condition, didSetBreakpoint.bind(this));
212        WebInspector.userMetrics.ScriptsBreakpointSet.record();
213    },
214
215    /**
216     * @param {WebInspector.DebuggerModel.Location} rawLocation
217     * @param {string} condition
218     * @param {function(?DebuggerAgent.BreakpointId, Array.<WebInspector.DebuggerModel.Location>)=} callback
219     */
220    setBreakpointBySourceId: function(rawLocation, condition, callback)
221    {
222        /**
223         * @this {WebInspector.DebuggerModel}
224         * @param {?Protocol.Error} error
225         * @param {DebuggerAgent.BreakpointId} breakpointId
226         * @param {DebuggerAgent.Location} actualLocation
227         */
228        function didSetBreakpoint(error, breakpointId, actualLocation)
229        {
230            if (callback) {
231                var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (actualLocation);
232                callback(error ? null : breakpointId, [rawLocation]);
233            }
234        }
235        DebuggerAgent.setBreakpoint(rawLocation, condition, didSetBreakpoint.bind(this));
236        WebInspector.userMetrics.ScriptsBreakpointSet.record();
237    },
238
239    /**
240     * @param {DebuggerAgent.BreakpointId} breakpointId
241     * @param {function(?Protocol.Error)=} callback
242     */
243    removeBreakpoint: function(breakpointId, callback)
244    {
245        DebuggerAgent.removeBreakpoint(breakpointId, callback);
246    },
247
248    /**
249     * @param {DebuggerAgent.BreakpointId} breakpointId
250     * @param {DebuggerAgent.Location} location
251     */
252    _breakpointResolved: function(breakpointId, location)
253    {
254        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointResolved, {breakpointId: breakpointId, location: location});
255    },
256
257    _globalObjectCleared: function()
258    {
259        this._setDebuggerPausedDetails(null);
260        this._reset();
261        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared);
262    },
263
264    _reset: function()
265    {
266        this._scripts = {};
267        this._scriptsBySourceURL = {};
268    },
269
270    /**
271     * @return {Object.<string, WebInspector.Script>}
272     */
273    get scripts()
274    {
275        return this._scripts;
276    },
277
278    /**
279     * @param {DebuggerAgent.ScriptId} scriptId
280     * @return {WebInspector.Script}
281     */
282    scriptForId: function(scriptId)
283    {
284        return this._scripts[scriptId] || null;
285    },
286
287    /**
288     * @param {DebuggerAgent.ScriptId} scriptId
289     * @param {string} newSource
290     * @param {function(?Protocol.Error)} callback
291     */
292    setScriptSource: function(scriptId, newSource, callback)
293    {
294        this._scripts[scriptId].editSource(newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback));
295    },
296
297    /**
298     * @param {DebuggerAgent.ScriptId} scriptId
299     * @param {string} newSource
300     * @param {function(?Protocol.Error)} callback
301     * @param {?Protocol.Error} error
302     * @param {Array.<DebuggerAgent.CallFrame>=} callFrames
303     */
304    _didEditScriptSource: function(scriptId, newSource, callback, error, callFrames)
305    {
306        callback(error);
307        if (!error && callFrames && callFrames.length)
308            this._pausedScript(callFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData);
309    },
310
311    /**
312     * @return {Array.<DebuggerAgent.CallFrame>}
313     */
314    get callFrames()
315    {
316        return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null;
317    },
318
319    /**
320     * @return {?WebInspector.DebuggerPausedDetails}
321     */
322    debuggerPausedDetails: function()
323    {
324        return this._debuggerPausedDetails;
325    },
326
327    /**
328     * @param {?WebInspector.DebuggerPausedDetails} debuggerPausedDetails
329     */
330    _setDebuggerPausedDetails: function(debuggerPausedDetails)
331    {
332        if (this._debuggerPausedDetails)
333            this._debuggerPausedDetails.dispose();
334        this._debuggerPausedDetails = debuggerPausedDetails;
335        if (this._debuggerPausedDetails)
336            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPausedDetails);
337        if (debuggerPausedDetails) {
338            this.setSelectedCallFrame(debuggerPausedDetails.callFrames[0]);
339            DebuggerAgent.setOverlayMessage(WebInspector.UIString("Paused in debugger"));
340        } else {
341            this.setSelectedCallFrame(null);
342            DebuggerAgent.setOverlayMessage();
343        }
344    },
345
346    /**
347     * @param {Array.<DebuggerAgent.CallFrame>} callFrames
348     * @param {string} reason
349     * @param {*} auxData
350     */
351    _pausedScript: function(callFrames, reason, auxData)
352    {
353        this._setDebuggerPausedDetails(new WebInspector.DebuggerPausedDetails(this, callFrames, reason, auxData));
354    },
355
356    _resumedScript: function()
357    {
358        this._setDebuggerPausedDetails(null);
359        if (this._executionLineLiveLocation)
360            this._executionLineLiveLocation.dispose();
361        this._executionLineLiveLocation = null;
362        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
363    },
364
365    /**
366     * @param {DebuggerAgent.ScriptId} scriptId
367     * @param {string} sourceURL
368     * @param {number} startLine
369     * @param {number} startColumn
370     * @param {number} endLine
371     * @param {number} endColumn
372     * @param {boolean} isContentScript
373     * @param {string=} sourceMapURL
374     * @param {boolean=} hasSourceURL
375     */
376    _parsedScriptSource: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
377    {
378        var script = new WebInspector.Script(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL);
379        if (!script.isAnonymousScript() && !script.isInlineScript()) {
380            var existingScripts = this._scriptsBySourceURL[script.sourceURL] || [];
381            for (var i = 0; i < existingScripts.length; ++i) {
382                if (existingScripts[i].isInlineScript()) {
383                    script.setIsDynamicScript(true);
384                    break;
385                }
386            }
387        }
388        this._registerScript(script);
389        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, script);
390    },
391
392    /**
393     * @param {WebInspector.Script} script
394     */
395    _registerScript: function(script)
396    {
397        this._scripts[script.scriptId] = script;
398        if (script.sourceURL) {
399            var scripts = this._scriptsBySourceURL[script.sourceURL];
400            if (!scripts) {
401                scripts = [];
402                this._scriptsBySourceURL[script.sourceURL] = scripts;
403            }
404            scripts.push(script);
405        }
406    },
407
408    /**
409     * @param {WebInspector.Script} script
410     * @param {number} lineNumber
411     * @param {number} columnNumber
412     * @return {WebInspector.DebuggerModel.Location}
413     */
414    createRawLocation: function(script, lineNumber, columnNumber)
415    {
416        if (script.sourceURL)
417            return this.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber)
418        return new WebInspector.DebuggerModel.Location(script.scriptId, lineNumber, columnNumber);
419    },
420
421    /**
422     * @param {string} sourceURL
423     * @param {number} lineNumber
424     * @param {number} columnNumber
425     * @return {WebInspector.DebuggerModel.Location}
426     */
427    createRawLocationByURL: function(sourceURL, lineNumber, columnNumber)
428    {
429        var closestScript = null;
430        var scripts = this._scriptsBySourceURL[sourceURL] || [];
431        for (var i = 0, l = scripts.length; i < l; ++i) {
432            var script = scripts[i];
433            if (!closestScript)
434                closestScript = script;
435            if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber))
436                continue;
437            if (script.endLine < lineNumber || (script.endLine === lineNumber && script.endColumn <= columnNumber))
438                continue;
439            closestScript = script;
440            break;
441        }
442        return closestScript ? new WebInspector.DebuggerModel.Location(closestScript.scriptId, lineNumber, columnNumber) : null;
443    },
444
445    /**
446     * @return {boolean}
447     */
448    isPaused: function()
449    {
450        return !!this.debuggerPausedDetails();
451    },
452
453    /**
454     * @param {?WebInspector.DebuggerModel.CallFrame} callFrame
455     */
456    setSelectedCallFrame: function(callFrame)
457    {
458        if (this._executionLineLiveLocation)
459            this._executionLineLiveLocation.dispose();
460        delete this._executionLineLiveLocation;
461
462        this._selectedCallFrame = callFrame;
463        if (!this._selectedCallFrame)
464            return;
465
466        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.CallFrameSelected, callFrame);
467
468        function updateExecutionLine(uiLocation)
469        {
470            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ExecutionLineChanged, uiLocation);
471        }
472        this._executionLineLiveLocation = callFrame.script.createLiveLocation(callFrame.location, updateExecutionLine.bind(this));
473    },
474
475    /**
476     * @return {?WebInspector.DebuggerModel.CallFrame}
477     */
478    selectedCallFrame: function()
479    {
480        return this._selectedCallFrame;
481    },
482
483    /**
484     * @param {string} code
485     * @param {string} objectGroup
486     * @param {boolean} includeCommandLineAPI
487     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
488     * @param {boolean} returnByValue
489     * @param {boolean} generatePreview
490     * @param {function(?WebInspector.RemoteObject, boolean, RuntimeAgent.RemoteObject=)} callback
491     */
492    evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
493    {
494        /**
495         * @param {?RuntimeAgent.RemoteObject} result
496         * @param {boolean=} wasThrown
497         */
498        function didEvaluate(result, wasThrown)
499        {
500            if (returnByValue)
501                callback(null, !!wasThrown, wasThrown ? null : result);
502            else
503                callback(WebInspector.RemoteObject.fromPayload(result), !!wasThrown);
504
505            if (objectGroup === "console")
506                this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame);
507        }
508
509        this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluate.bind(this));
510    },
511
512    /**
513     * @param {function(Object)} callback
514     */
515    getSelectedCallFrameVariables: function(callback)
516    {
517        var result = { this: true };
518
519        var selectedCallFrame = this._selectedCallFrame;
520        if (!selectedCallFrame)
521            callback(result);
522
523        var pendingRequests = 0;
524
525        function propertiesCollected(properties)
526        {
527            for (var i = 0; properties && i < properties.length; ++i)
528                result[properties[i].name] = true;
529            if (--pendingRequests == 0)
530                callback(result);
531        }
532
533        for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) {
534            var scope = selectedCallFrame.scopeChain[i];
535            var object = WebInspector.RemoteObject.fromPayload(scope.object);
536            pendingRequests++;
537            object.getAllProperties(propertiesCollected);
538        }
539    },
540
541    /**
542     * @param {boolean} active
543     */
544    setBreakpointsActive: function(active)
545    {
546        if (this._breakpointsActive === active)
547            return;
548        this._breakpointsActive = active;
549        DebuggerAgent.setBreakpointsActive(active);
550        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, active);
551    },
552
553    /**
554     * @return {boolean}
555     */
556    breakpointsActive: function()
557    {
558        return this._breakpointsActive;
559    },
560
561    /**
562     * @param {WebInspector.DebuggerModel.Location} rawLocation
563     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
564     * @return {WebInspector.Script.Location}
565     */
566    createLiveLocation: function(rawLocation, updateDelegate)
567    {
568        var script = this._scripts[rawLocation.scriptId];
569        return script.createLiveLocation(rawLocation, updateDelegate);
570    },
571
572    /**
573     * @param {WebInspector.DebuggerModel.Location} rawLocation
574     * @return {?WebInspector.UILocation}
575     */
576    rawLocationToUILocation: function(rawLocation)
577    {
578        var script = this._scripts[rawLocation.scriptId];
579        if (!script)
580            return null;
581        return script.rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber);
582    },
583
584    /**
585     * Handles notification from JavaScript VM about updated stack (liveedit or frame restart action).
586     * @this {WebInspector.DebuggerModel}
587     * @param {Array.<DebuggerAgent.CallFrame>=} newCallFrames
588     * @param {Object=} details
589     */
590    callStackModified: function(newCallFrames, details)
591    {
592        // FIXME: declare this property in protocol and in JavaScript.
593        if (details && details["stack_update_needs_step_in"])
594            DebuggerAgent.stepInto();
595        else {
596            if (newCallFrames && newCallFrames.length)
597                this._pausedScript(newCallFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData);
598
599        }
600    },
601
602    __proto__: WebInspector.Object.prototype
603}
604
605WebInspector.DebuggerEventTypes = {
606    JavaScriptPause: 0,
607    JavaScriptBreakpoint: 1,
608    NativeBreakpoint: 2
609};
610
611/**
612 * @constructor
613 * @implements {DebuggerAgent.Dispatcher}
614 * @param {WebInspector.DebuggerModel} debuggerModel
615 */
616WebInspector.DebuggerDispatcher = function(debuggerModel)
617{
618    this._debuggerModel = debuggerModel;
619}
620
621WebInspector.DebuggerDispatcher.prototype = {
622    /**
623     * @param {Array.<DebuggerAgent.CallFrame>} callFrames
624     * @param {string} reason
625     * @param {Object=} auxData
626     */
627    paused: function(callFrames, reason, auxData)
628    {
629        this._debuggerModel._pausedScript(callFrames, reason, auxData);
630    },
631
632    resumed: function()
633    {
634        this._debuggerModel._resumedScript();
635    },
636
637    globalObjectCleared: function()
638    {
639        this._debuggerModel._globalObjectCleared();
640    },
641
642    /**
643     * @param {DebuggerAgent.ScriptId} scriptId
644     * @param {string} sourceURL
645     * @param {number} startLine
646     * @param {number} startColumn
647     * @param {number} endLine
648     * @param {number} endColumn
649     * @param {boolean=} isContentScript
650     * @param {string=} sourceMapURL
651     * @param {boolean=} hasSourceURL
652     */
653    scriptParsed: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
654    {
655        this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript, sourceMapURL, hasSourceURL);
656    },
657
658    /**
659     * @param {string} sourceURL
660     * @param {string} source
661     * @param {number} startingLine
662     * @param {number} errorLine
663     * @param {string} errorMessage
664     */
665    scriptFailedToParse: function(sourceURL, source, startingLine, errorLine, errorMessage)
666    {
667    },
668
669    /**
670    * @param {DebuggerAgent.BreakpointId} breakpointId
671    * @param {DebuggerAgent.Location} location
672     */
673    breakpointResolved: function(breakpointId, location)
674    {
675        this._debuggerModel._breakpointResolved(breakpointId, location);
676    }
677}
678
679/**
680 * @constructor
681 * @param {WebInspector.Script} script
682 * @param {DebuggerAgent.CallFrame} payload
683 */
684WebInspector.DebuggerModel.CallFrame = function(script, payload)
685{
686    this._script = script;
687    this._payload = payload;
688    this._locations = [];
689}
690
691WebInspector.DebuggerModel.CallFrame.prototype = {
692    /**
693     * @return {WebInspector.Script}
694     */
695    get script()
696    {
697        return this._script;
698    },
699
700    /**
701     * @return {string}
702     */
703    get type()
704    {
705        return this._payload.type;
706    },
707
708    /**
709     * @return {string}
710     */
711    get id()
712    {
713        return this._payload.callFrameId;
714    },
715
716    /**
717     * @return {Array.<DebuggerAgent.Scope>}
718     */
719    get scopeChain()
720    {
721        return this._payload.scopeChain;
722    },
723
724    /**
725     * @return {RuntimeAgent.RemoteObject}
726     */
727    get this()
728    {
729        return this._payload.this;
730    },
731
732    /**
733     * @return {string}
734     */
735    get functionName()
736    {
737        return this._payload.functionName;
738    },
739
740    /**
741     * @return {WebInspector.DebuggerModel.Location}
742     */
743    get location()
744    {
745        var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._payload.location);
746        return rawLocation;
747    },
748
749    /**
750     * @param {string} code
751     * @param {string} objectGroup
752     * @param {boolean} includeCommandLineAPI
753     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
754     * @param {boolean} returnByValue
755     * @param {boolean} generatePreview
756     * @param {function(?RuntimeAgent.RemoteObject, boolean=)=} callback
757     */
758    evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
759    {
760        /**
761         * @this {WebInspector.DebuggerModel.CallFrame}
762         * @param {?Protocol.Error} error
763         * @param {RuntimeAgent.RemoteObject} result
764         * @param {boolean=} wasThrown
765         */
766        function didEvaluateOnCallFrame(error, result, wasThrown)
767        {
768            if (error) {
769                console.error(error);
770                callback(null, false);
771                return;
772            }
773            callback(result, wasThrown);
774        }
775        DebuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluateOnCallFrame.bind(this));
776    },
777
778    /**
779     * @param {function(?Protocol.Error=)=} callback
780     */
781    restart: function(callback)
782    {
783        /**
784         * @this {WebInspector.DebuggerModel.CallFrame}
785         * @param {?Protocol.Error} error
786         * @param {Array.<DebuggerAgent.CallFrame>=} callFrames
787         * @param {Object=} details
788         */
789        function protocolCallback(error, callFrames, details)
790        {
791            if (!error)
792                WebInspector.debuggerModel.callStackModified(callFrames, details);
793            if (callback)
794                callback(error);
795        }
796        DebuggerAgent.restartFrame(this._payload.callFrameId, protocolCallback);
797    },
798
799    /**
800     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
801     */
802    createLiveLocation: function(updateDelegate)
803    {
804        var location = this._script.createLiveLocation(this.location, updateDelegate);
805        this._locations.push(location);
806        return location;
807    },
808
809    dispose: function(updateDelegate)
810    {
811        for (var i = 0; i < this._locations.length; ++i)
812            this._locations[i].dispose();
813        this._locations = [];
814    }
815}
816
817/**
818 * @constructor
819 * @param {WebInspector.DebuggerModel} model
820 * @param {Array.<DebuggerAgent.CallFrame>} callFrames
821 * @param {string} reason
822 * @param {*} auxData
823 */
824WebInspector.DebuggerPausedDetails = function(model, callFrames, reason, auxData)
825{
826    this.callFrames = [];
827    for (var i = 0; i < callFrames.length; ++i) {
828        var callFrame = callFrames[i];
829        var script = model.scriptForId(callFrame.location.scriptId);
830        if (script)
831            this.callFrames.push(new WebInspector.DebuggerModel.CallFrame(script, callFrame));
832    }
833    this.reason = reason;
834    this.auxData = auxData;
835}
836
837WebInspector.DebuggerPausedDetails.prototype = {
838    dispose: function()
839    {
840        for (var i = 0; i < this.callFrames.length; ++i) {
841            var callFrame = this.callFrames[i];
842            callFrame.dispose();
843        }
844    }
845}
846
847/**
848 * @type {?WebInspector.DebuggerModel}
849 */
850WebInspector.debuggerModel = null;
851