• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.SDKModel}
34 * @param {!WebInspector.Target} target
35 */
36WebInspector.DebuggerModel = function(target)
37{
38    WebInspector.SDKModel.call(this, WebInspector.DebuggerModel, target);
39
40    target.registerDebuggerDispatcher(new WebInspector.DebuggerDispatcher(this));
41    this._agent = target.debuggerAgent();
42
43    /** @type {?WebInspector.DebuggerPausedDetails} */
44    this._debuggerPausedDetails = null;
45    /** @type {!Object.<string, !WebInspector.Script>} */
46    this._scripts = {};
47    /** @type {!StringMap.<!Array.<!WebInspector.Script>>} */
48    this._scriptsBySourceURL = new StringMap();
49
50    /** @type {!WebInspector.Object} */
51    this._breakpointResolvedEventTarget = new WebInspector.Object();
52
53    this._isPausing = false;
54    WebInspector.settings.pauseOnExceptionEnabled.addChangeListener(this._pauseOnExceptionStateChanged, this);
55    WebInspector.settings.pauseOnCaughtException.addChangeListener(this._pauseOnExceptionStateChanged, this);
56
57    WebInspector.settings.enableAsyncStackTraces.addChangeListener(this._asyncStackTracesStateChanged, this);
58    WebInspector.profilingLock().addEventListener(WebInspector.Lock.Events.StateChanged, this._profilingStateChanged, this);
59
60    this.enableDebugger();
61
62    WebInspector.settings.skipStackFramesPattern.addChangeListener(this._applySkipStackFrameSettings, this);
63    WebInspector.settings.skipContentScripts.addChangeListener(this._applySkipStackFrameSettings, this);
64    this._applySkipStackFrameSettings();
65}
66
67/** @typedef {{location: ?WebInspector.DebuggerModel.Location, sourceURL: ?string, functionName: string, scopeChain: (Array.<!DebuggerAgent.Scope>|null)}} */
68WebInspector.DebuggerModel.FunctionDetails;
69
70/**
71 * Keep these in sync with WebCore::ScriptDebugServer
72 *
73 * @enum {string}
74 */
75WebInspector.DebuggerModel.PauseOnExceptionsState = {
76    DontPauseOnExceptions : "none",
77    PauseOnAllExceptions : "all",
78    PauseOnUncaughtExceptions: "uncaught"
79};
80
81WebInspector.DebuggerModel.Events = {
82    DebuggerWasEnabled: "DebuggerWasEnabled",
83    DebuggerWasDisabled: "DebuggerWasDisabled",
84    DebuggerPaused: "DebuggerPaused",
85    DebuggerResumed: "DebuggerResumed",
86    ParsedScriptSource: "ParsedScriptSource",
87    FailedToParseScriptSource: "FailedToParseScriptSource",
88    GlobalObjectCleared: "GlobalObjectCleared",
89    CallFrameSelected: "CallFrameSelected",
90    ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame",
91}
92
93WebInspector.DebuggerModel.BreakReason = {
94    DOM: "DOM",
95    EventListener: "EventListener",
96    XHR: "XHR",
97    Exception: "exception",
98    Assert: "assert",
99    CSPViolation: "CSPViolation",
100    DebugCommand: "debugCommand"
101}
102
103WebInspector.DebuggerModel.prototype = {
104    /**
105     * @return {boolean}
106     */
107    debuggerEnabled: function()
108    {
109        return !!this._debuggerEnabled;
110    },
111
112    enableDebugger: function()
113    {
114        if (this._debuggerEnabled)
115            return;
116        this._agent.enable();
117        this._debuggerEnabled = true;
118        this._pauseOnExceptionStateChanged();
119        this._asyncStackTracesStateChanged();
120        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasEnabled);
121    },
122
123    disableDebugger: function()
124    {
125        if (!this._debuggerEnabled)
126            return;
127
128        this._agent.disable();
129        this._debuggerEnabled = false;
130        this._isPausing = false;
131        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerWasDisabled);
132    },
133
134    /**
135     * @param {boolean} skip
136     * @param {boolean=} untilReload
137     */
138    skipAllPauses: function(skip, untilReload)
139    {
140        if (this._skipAllPausesTimeout) {
141            clearTimeout(this._skipAllPausesTimeout);
142            delete this._skipAllPausesTimeout;
143        }
144        this._agent.setSkipAllPauses(skip, untilReload);
145    },
146
147    /**
148     * @param {number} timeout
149     */
150    skipAllPausesUntilReloadOrTimeout: function(timeout)
151    {
152        if (this._skipAllPausesTimeout)
153            clearTimeout(this._skipAllPausesTimeout);
154        this._agent.setSkipAllPauses(true, true);
155        // If reload happens before the timeout, the flag will be already unset and the timeout callback won't change anything.
156        this._skipAllPausesTimeout = setTimeout(this.skipAllPauses.bind(this, false), timeout);
157    },
158
159    _pauseOnExceptionStateChanged: function()
160    {
161        var state;
162        if (!WebInspector.settings.pauseOnExceptionEnabled.get()) {
163            state = WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions;
164        } else if (WebInspector.settings.pauseOnCaughtException.get()) {
165            state = WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions;
166        } else {
167            state = WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnUncaughtExceptions;
168        }
169        this._agent.setPauseOnExceptions(state);
170    },
171
172    _profilingStateChanged: function()
173    {
174        if (Runtime.experiments.isEnabled("disableAgentsWhenProfile")) {
175            if (WebInspector.profilingLock().isAcquired())
176                this.disableDebugger();
177            else
178                this.enableDebugger();
179        }
180        this._asyncStackTracesStateChanged();
181    },
182
183    _asyncStackTracesStateChanged: function()
184    {
185        const maxAsyncStackChainDepth = 4;
186        var enabled = WebInspector.settings.enableAsyncStackTraces.get() && !WebInspector.profilingLock().isAcquired();
187        this._agent.setAsyncCallStackDepth(enabled ? maxAsyncStackChainDepth : 0);
188    },
189
190    stepInto: function()
191    {
192        /**
193         * @this {WebInspector.DebuggerModel}
194         */
195        function callback()
196        {
197            this._agent.stepInto();
198        }
199        this._agent.setOverlayMessage(undefined, callback.bind(this));
200    },
201
202    stepOver: function()
203    {
204        /**
205         * @this {WebInspector.DebuggerModel}
206         */
207        function callback()
208        {
209            this._agent.stepOver();
210        }
211        this._agent.setOverlayMessage(undefined, callback.bind(this));
212    },
213
214    stepOut: function()
215    {
216        /**
217         * @this {WebInspector.DebuggerModel}
218         */
219        function callback()
220        {
221            this._agent.stepOut();
222        }
223        this._agent.setOverlayMessage(undefined, callback.bind(this));
224    },
225
226    resume: function()
227    {
228        /**
229         * @this {WebInspector.DebuggerModel}
230         */
231        function callback()
232        {
233            this._agent.resume();
234        }
235        this._agent.setOverlayMessage(undefined, callback.bind(this));
236        this._isPausing = false;
237    },
238
239    pause: function()
240    {
241        this._isPausing = true;
242        this.skipAllPauses(false);
243        this._agent.pause();
244    },
245
246    /**
247     * @param {string} url
248     * @param {number} lineNumber
249     * @param {number=} columnNumber
250     * @param {string=} condition
251     * @param {function(?DebuggerAgent.BreakpointId, !Array.<!WebInspector.DebuggerModel.Location>)=} callback
252     */
253    setBreakpointByURL: function(url, lineNumber, columnNumber, condition, callback)
254    {
255        // Adjust column if needed.
256        var minColumnNumber = 0;
257        var scripts = this._scriptsBySourceURL.get(url) || [];
258        for (var i = 0, l = scripts.length; i < l; ++i) {
259            var script = scripts[i];
260            if (lineNumber === script.lineOffset)
261                minColumnNumber = minColumnNumber ? Math.min(minColumnNumber, script.columnOffset) : script.columnOffset;
262        }
263        columnNumber = Math.max(columnNumber, minColumnNumber);
264
265        var target = this.target();
266        /**
267         * @param {?Protocol.Error} error
268         * @param {!DebuggerAgent.BreakpointId} breakpointId
269         * @param {!Array.<!DebuggerAgent.Location>} locations
270         */
271        function didSetBreakpoint(error, breakpointId, locations)
272        {
273            if (callback) {
274                var rawLocations = locations ? locations.map(WebInspector.DebuggerModel.Location.fromPayload.bind(WebInspector.DebuggerModel.Location, target)) : [];
275                callback(error ? null : breakpointId, rawLocations);
276            }
277        }
278        this._agent.setBreakpointByUrl(lineNumber, url, undefined, columnNumber, condition, undefined, didSetBreakpoint);
279        WebInspector.userMetrics.ScriptsBreakpointSet.record();
280    },
281
282    /**
283     * @param {!WebInspector.DebuggerModel.Location} rawLocation
284     * @param {string} condition
285     * @param {function(?DebuggerAgent.BreakpointId, !Array.<!WebInspector.DebuggerModel.Location>)=} callback
286     */
287    setBreakpointBySourceId: function(rawLocation, condition, callback)
288    {
289        var target = this.target();
290
291        /**
292         * @param {?Protocol.Error} error
293         * @param {!DebuggerAgent.BreakpointId} breakpointId
294         * @param {!DebuggerAgent.Location} actualLocation
295         */
296        function didSetBreakpoint(error, breakpointId, actualLocation)
297        {
298            if (callback) {
299                var location = WebInspector.DebuggerModel.Location.fromPayload(target, actualLocation);
300                callback(error ? null : breakpointId, [location]);
301            }
302        }
303        this._agent.setBreakpoint(rawLocation.payload(), condition, didSetBreakpoint);
304        WebInspector.userMetrics.ScriptsBreakpointSet.record();
305    },
306
307    /**
308     * @param {!DebuggerAgent.BreakpointId} breakpointId
309     * @param {function()=} callback
310     */
311    removeBreakpoint: function(breakpointId, callback)
312    {
313        this._agent.removeBreakpoint(breakpointId, innerCallback);
314
315        /**
316         * @param {?Protocol.Error} error
317         */
318        function innerCallback(error)
319        {
320            if (error)
321                console.error("Failed to remove breakpoint: " + error);
322            if (callback)
323                callback();
324        }
325    },
326
327    /**
328     * @param {!DebuggerAgent.BreakpointId} breakpointId
329     * @param {!DebuggerAgent.Location} location
330     */
331    _breakpointResolved: function(breakpointId, location)
332    {
333        this._breakpointResolvedEventTarget.dispatchEventToListeners(breakpointId, WebInspector.DebuggerModel.Location.fromPayload(this.target(), location));
334    },
335
336    _globalObjectCleared: function()
337    {
338        this._setDebuggerPausedDetails(null);
339        this._reset();
340        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared);
341    },
342
343    _reset: function()
344    {
345        this._scripts = {};
346        this._scriptsBySourceURL.clear();
347    },
348
349    /**
350     * @return {!Object.<string, !WebInspector.Script>}
351     */
352    get scripts()
353    {
354        return this._scripts;
355    },
356
357    /**
358     * @param {!DebuggerAgent.ScriptId} scriptId
359     * @return {!WebInspector.Script}
360     */
361    scriptForId: function(scriptId)
362    {
363        return this._scripts[scriptId] || null;
364    },
365
366    /**
367     * @return {!Array.<!WebInspector.Script>}
368     */
369    scriptsForSourceURL: function(sourceURL)
370    {
371        if (!sourceURL)
372            return [];
373        return this._scriptsBySourceURL.get(sourceURL) || [];
374    },
375
376    /**
377     * @param {!DebuggerAgent.ScriptId} scriptId
378     * @param {string} newSource
379     * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=)} callback
380     */
381    setScriptSource: function(scriptId, newSource, callback)
382    {
383        this._scripts[scriptId].editSource(newSource, this._didEditScriptSource.bind(this, scriptId, newSource, callback));
384    },
385
386    /**
387     * @param {!DebuggerAgent.ScriptId} scriptId
388     * @param {string} newSource
389     * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=)} callback
390     * @param {?Protocol.Error} error
391     * @param {!DebuggerAgent.SetScriptSourceError=} errorData
392     * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames
393     * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
394     * @param {boolean=} needsStepIn
395     */
396    _didEditScriptSource: function(scriptId, newSource, callback, error, errorData, callFrames, asyncStackTrace, needsStepIn)
397    {
398        if (needsStepIn) {
399            this.stepInto();
400            this._pendingLiveEditCallback = callback.bind(this, error, errorData);
401            return;
402        }
403
404        if (!error && callFrames && callFrames.length)
405            this._pausedScript(callFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData, this._debuggerPausedDetails.breakpointIds, asyncStackTrace);
406        callback(error, errorData);
407    },
408
409    /**
410     * @return {?Array.<!WebInspector.DebuggerModel.CallFrame>}
411     */
412    get callFrames()
413    {
414        return this._debuggerPausedDetails ? this._debuggerPausedDetails.callFrames : null;
415    },
416
417    /**
418     * @return {?WebInspector.DebuggerPausedDetails}
419     */
420    debuggerPausedDetails: function()
421    {
422        return this._debuggerPausedDetails;
423    },
424
425    /**
426     * @param {?WebInspector.DebuggerPausedDetails} debuggerPausedDetails
427     */
428    _setDebuggerPausedDetails: function(debuggerPausedDetails)
429    {
430        this._isPausing = false;
431        this._debuggerPausedDetails = debuggerPausedDetails;
432        if (this._debuggerPausedDetails)
433            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPausedDetails);
434        if (debuggerPausedDetails) {
435            this.setSelectedCallFrame(debuggerPausedDetails.callFrames[0]);
436            this._agent.setOverlayMessage(WebInspector.UIString("Paused in debugger"));
437        } else {
438            this.setSelectedCallFrame(null);
439            this._agent.setOverlayMessage();
440        }
441    },
442
443    /**
444     * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames
445     * @param {string} reason
446     * @param {!Object|undefined} auxData
447     * @param {!Array.<string>} breakpointIds
448     * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
449     */
450    _pausedScript: function(callFrames, reason, auxData, breakpointIds, asyncStackTrace)
451    {
452        this._setDebuggerPausedDetails(new WebInspector.DebuggerPausedDetails(this.target(), callFrames, reason, auxData, breakpointIds, asyncStackTrace));
453        if (this._pendingLiveEditCallback) {
454            var callback = this._pendingLiveEditCallback;
455            delete this._pendingLiveEditCallback;
456            callback();
457        }
458    },
459
460    _resumedScript: function()
461    {
462        this._setDebuggerPausedDetails(null);
463        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.DebuggerResumed);
464    },
465
466    /**
467     * @param {!DebuggerAgent.ScriptId} scriptId
468     * @param {string} sourceURL
469     * @param {number} startLine
470     * @param {number} startColumn
471     * @param {number} endLine
472     * @param {number} endColumn
473     * @param {boolean} isContentScript
474     * @param {string=} sourceMapURL
475     * @param {boolean=} hasSourceURL
476     * @param {boolean=} hasSyntaxError
477     */
478    _parsedScriptSource: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL, hasSyntaxError)
479    {
480        var script = new WebInspector.Script(this.target(), scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL);
481        this._registerScript(script);
482        if (!hasSyntaxError)
483            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ParsedScriptSource, script);
484        else
485            this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, script);
486    },
487
488    /**
489     * @param {!WebInspector.Script} script
490     */
491    _registerScript: function(script)
492    {
493        this._scripts[script.scriptId] = script;
494        if (script.isAnonymousScript())
495            return;
496
497        var scripts = this._scriptsBySourceURL.get(script.sourceURL);
498        if (!scripts) {
499            scripts = [];
500            this._scriptsBySourceURL.set(script.sourceURL, scripts);
501        }
502        scripts.push(script);
503    },
504
505    /**
506     * @param {!WebInspector.Script} script
507     * @param {number} lineNumber
508     * @param {number} columnNumber
509     * @return {?WebInspector.DebuggerModel.Location}
510     */
511    createRawLocation: function(script, lineNumber, columnNumber)
512    {
513        if (script.sourceURL)
514            return this.createRawLocationByURL(script.sourceURL, lineNumber, columnNumber);
515        return new WebInspector.DebuggerModel.Location(this.target(), script.scriptId, lineNumber, columnNumber);
516    },
517
518    /**
519     * @param {string} sourceURL
520     * @param {number} lineNumber
521     * @param {number} columnNumber
522     * @return {?WebInspector.DebuggerModel.Location}
523     */
524    createRawLocationByURL: function(sourceURL, lineNumber, columnNumber)
525    {
526        var closestScript = null;
527        var scripts = this._scriptsBySourceURL.get(sourceURL) || [];
528        for (var i = 0, l = scripts.length; i < l; ++i) {
529            var script = scripts[i];
530            if (!closestScript)
531                closestScript = script;
532            if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber))
533                continue;
534            if (script.endLine < lineNumber || (script.endLine === lineNumber && script.endColumn <= columnNumber))
535                continue;
536            closestScript = script;
537            break;
538        }
539        return closestScript ? new WebInspector.DebuggerModel.Location(this.target(), closestScript.scriptId, lineNumber, columnNumber) : null;
540    },
541
542    /**
543     * @param {?DebuggerAgent.ScriptId} scriptId
544     * @param {string} sourceUrl
545     * @param {number} lineNumber
546     * @param {number} columnNumber
547     * @return {?WebInspector.DebuggerModel.Location}
548     */
549    createRawLocationByScriptId: function(scriptId, sourceUrl, lineNumber, columnNumber)
550    {
551        var script = scriptId ? this.scriptForId(scriptId) : null;
552        return script ? this.createRawLocation(script, lineNumber, columnNumber) : this.createRawLocationByURL(sourceUrl, lineNumber, columnNumber);
553    },
554
555    /**
556     * @return {boolean}
557     */
558    isPaused: function()
559    {
560        return !!this.debuggerPausedDetails();
561    },
562
563    /**
564     * @return {boolean}
565     */
566    isPausing: function()
567    {
568        return this._isPausing;
569    },
570
571    /**
572     * @param {?WebInspector.DebuggerModel.CallFrame} callFrame
573     */
574    setSelectedCallFrame: function(callFrame)
575    {
576        this._selectedCallFrame = callFrame;
577        if (!this._selectedCallFrame)
578            return;
579
580        this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.CallFrameSelected, callFrame);
581    },
582
583    /**
584     * @return {?WebInspector.DebuggerModel.CallFrame}
585     */
586    selectedCallFrame: function()
587    {
588        return this._selectedCallFrame;
589    },
590
591    /**
592     * @param {string} code
593     * @param {string} objectGroup
594     * @param {boolean} includeCommandLineAPI
595     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
596     * @param {boolean} returnByValue
597     * @param {boolean} generatePreview
598     * @param {function(?WebInspector.RemoteObject, boolean, ?RuntimeAgent.RemoteObject=, ?DebuggerAgent.ExceptionDetails=)} callback
599     */
600    evaluateOnSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
601    {
602        /**
603         * @param {?RuntimeAgent.RemoteObject} result
604         * @param {boolean=} wasThrown
605         * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
606         * @this {WebInspector.DebuggerModel}
607         */
608        function didEvaluate(result, wasThrown, exceptionDetails)
609        {
610            if (!result)
611                callback(null, false);
612            else if (returnByValue)
613                callback(null, !!wasThrown, wasThrown ? null : result, exceptionDetails);
614            else
615                callback(this.target().runtimeModel.createRemoteObject(result), !!wasThrown, undefined, exceptionDetails);
616
617            if (objectGroup === "console")
618                this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame);
619        }
620
621        this.selectedCallFrame().evaluate(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluate.bind(this));
622    },
623
624    /**
625     * @param {function(!Object)} callback
626     */
627    getSelectedCallFrameVariables: function(callback)
628    {
629        var result = { this: true };
630
631        var selectedCallFrame = this._selectedCallFrame;
632        if (!selectedCallFrame)
633            callback(result);
634
635        var pendingRequests = 0;
636
637        function propertiesCollected(properties)
638        {
639            for (var i = 0; properties && i < properties.length; ++i)
640                result[properties[i].name] = true;
641            if (--pendingRequests == 0)
642                callback(result);
643        }
644
645        for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) {
646            var scope = selectedCallFrame.scopeChain[i];
647            var object = this.target().runtimeModel.createRemoteObject(scope.object);
648            pendingRequests++;
649            object.getAllProperties(false, propertiesCollected);
650        }
651    },
652
653    /**
654     * Handles notification from JavaScript VM about updated stack (liveedit or frame restart action).
655     * @param {!Array.<!DebuggerAgent.CallFrame>=} newCallFrames
656     * @param {!Object=} details
657     * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
658     */
659    callStackModified: function(newCallFrames, details, asyncStackTrace)
660    {
661        // FIXME: declare this property in protocol and in JavaScript.
662        if (details && details["stack_update_needs_step_in"])
663            this.stepInto();
664        else if (newCallFrames && newCallFrames.length)
665            this._pausedScript(newCallFrames, this._debuggerPausedDetails.reason, this._debuggerPausedDetails.auxData, this._debuggerPausedDetails.breakpointIds, asyncStackTrace);
666    },
667
668    _applySkipStackFrameSettings: function()
669    {
670        this._agent.skipStackFrames(WebInspector.settings.skipStackFramesPattern.get(), WebInspector.settings.skipContentScripts.get());
671    },
672
673    /**
674     * @param {!WebInspector.RemoteObject} remoteObject
675     * @param {function(?WebInspector.DebuggerModel.FunctionDetails)} callback
676     */
677    functionDetails: function(remoteObject, callback)
678    {
679        this._agent.getFunctionDetails(remoteObject.objectId, didGetDetails.bind(this));
680
681        /**
682         * @param {?Protocol.Error} error
683         * @param {!DebuggerAgent.FunctionDetails} response
684         * @this {WebInspector.DebuggerModel}
685         */
686        function didGetDetails(error, response)
687        {
688            if (error) {
689                console.error(error);
690                callback(null);
691                return;
692            }
693            var location = response.location;
694            var script = this.scriptForId(location.scriptId);
695            var rawLocation = script ? this.createRawLocation(script, location.lineNumber, location.columnNumber || 0) : null;
696            var sourceURL = script ? script.contentURL() : null;
697            callback({location: rawLocation, sourceURL: sourceURL, functionName: response.functionName, scopeChain: response.scopeChain || null});
698        }
699    },
700
701    /**
702     * @param {!DebuggerAgent.BreakpointId} breakpointId
703     * @param {function(!WebInspector.Event)} listener
704     * @param {!Object=} thisObject
705     */
706    addBreakpointListener: function(breakpointId, listener, thisObject)
707    {
708        this._breakpointResolvedEventTarget.addEventListener(breakpointId, listener, thisObject)
709    },
710
711    /**
712     * @param {!DebuggerAgent.BreakpointId} breakpointId
713     * @param {function(!WebInspector.Event)} listener
714     * @param {!Object=} thisObject
715     */
716    removeBreakpointListener: function(breakpointId, listener, thisObject)
717    {
718        this._breakpointResolvedEventTarget.removeEventListener(breakpointId, listener, thisObject);
719    },
720
721    dispose: function()
722    {
723        WebInspector.settings.pauseOnExceptionEnabled.removeChangeListener(this._pauseOnExceptionStateChanged, this);
724        WebInspector.settings.pauseOnCaughtException.removeChangeListener(this._pauseOnExceptionStateChanged, this);
725        WebInspector.settings.skipStackFramesPattern.removeChangeListener(this._applySkipStackFrameSettings, this);
726        WebInspector.settings.skipContentScripts.removeChangeListener(this._applySkipStackFrameSettings, this);
727        WebInspector.settings.enableAsyncStackTraces.removeChangeListener(this._asyncStackTracesStateChanged, this);
728    },
729
730    __proto__: WebInspector.SDKModel.prototype
731}
732
733WebInspector.DebuggerEventTypes = {
734    JavaScriptPause: 0,
735    JavaScriptBreakpoint: 1,
736    NativeBreakpoint: 2
737};
738
739/**
740 * @constructor
741 * @implements {DebuggerAgent.Dispatcher}
742 * @param {!WebInspector.DebuggerModel} debuggerModel
743 */
744WebInspector.DebuggerDispatcher = function(debuggerModel)
745{
746    this._debuggerModel = debuggerModel;
747}
748
749WebInspector.DebuggerDispatcher.prototype = {
750    /**
751     * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames
752     * @param {string} reason
753     * @param {!Object=} auxData
754     * @param {!Array.<string>=} breakpointIds
755     * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
756     */
757    paused: function(callFrames, reason, auxData, breakpointIds, asyncStackTrace)
758    {
759        this._debuggerModel._pausedScript(callFrames, reason, auxData, breakpointIds || [], asyncStackTrace);
760    },
761
762    /**
763     * @override
764     */
765    resumed: function()
766    {
767        this._debuggerModel._resumedScript();
768    },
769
770    /**
771     * @override
772     */
773    globalObjectCleared: function()
774    {
775        this._debuggerModel._globalObjectCleared();
776    },
777
778    /**
779     * @param {!DebuggerAgent.ScriptId} scriptId
780     * @param {string} sourceURL
781     * @param {number} startLine
782     * @param {number} startColumn
783     * @param {number} endLine
784     * @param {number} endColumn
785     * @param {boolean=} isContentScript
786     * @param {string=} sourceMapURL
787     * @param {boolean=} hasSourceURL
788     */
789    scriptParsed: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
790    {
791        this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript, sourceMapURL, hasSourceURL, false);
792    },
793
794    /**
795     * @param {!DebuggerAgent.ScriptId} scriptId
796     * @param {string} sourceURL
797     * @param {number} startLine
798     * @param {number} startColumn
799     * @param {number} endLine
800     * @param {number} endColumn
801     * @param {boolean=} isContentScript
802     * @param {string=} sourceMapURL
803     * @param {boolean=} hasSourceURL
804     */
805    scriptFailedToParse: function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL)
806    {
807        this._debuggerModel._parsedScriptSource(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, !!isContentScript, sourceMapURL, hasSourceURL, true);
808    },
809
810    /**
811     * @param {!DebuggerAgent.BreakpointId} breakpointId
812     * @param {!DebuggerAgent.Location} location
813     */
814    breakpointResolved: function(breakpointId, location)
815    {
816        this._debuggerModel._breakpointResolved(breakpointId, location);
817    }
818}
819
820/**
821 * @constructor
822 * @extends {WebInspector.SDKObject}
823 * @param {!WebInspector.Target} target
824 * @param {string} scriptId
825 * @param {number} lineNumber
826 * @param {number=} columnNumber
827 */
828WebInspector.DebuggerModel.Location = function(target, scriptId, lineNumber, columnNumber)
829{
830    WebInspector.SDKObject.call(this, target);
831    this._debuggerModel = target.debuggerModel;
832    this.scriptId = scriptId;
833    this.lineNumber = lineNumber;
834    this.columnNumber = columnNumber || 0;
835}
836
837/**
838 * @param {!WebInspector.Target} target
839 * @param {!DebuggerAgent.Location} payload
840 * @return {!WebInspector.DebuggerModel.Location}
841 */
842WebInspector.DebuggerModel.Location.fromPayload = function(target, payload)
843{
844    return new WebInspector.DebuggerModel.Location(target, payload.scriptId, payload.lineNumber, payload.columnNumber);
845}
846
847WebInspector.DebuggerModel.Location.prototype = {
848    /**
849     * @return {!DebuggerAgent.Location}
850     */
851    payload: function()
852    {
853        return { scriptId: this.scriptId, lineNumber: this.lineNumber, columnNumber: this.columnNumber };
854    },
855
856    /**
857     * @return {!WebInspector.Script}
858     */
859    script: function()
860    {
861        return this._debuggerModel.scriptForId(this.scriptId);
862    },
863
864    continueToLocation: function()
865    {
866        this._debuggerModel._agent.continueToLocation(this.payload());
867    },
868
869    /**
870     * @return {string}
871     */
872    id: function()
873    {
874        return this.target().id() + ":" + this.scriptId + ":" + this.lineNumber + ":" + this.columnNumber
875    },
876
877    __proto__: WebInspector.SDKObject.prototype
878}
879
880/**
881 * @constructor
882 * @extends {WebInspector.SDKObject}
883 * @param {!WebInspector.Target} target
884 * @param {!WebInspector.Script} script
885 * @param {!DebuggerAgent.CallFrame} payload
886 * @param {boolean=} isAsync
887 */
888WebInspector.DebuggerModel.CallFrame = function(target, script, payload, isAsync)
889{
890    WebInspector.SDKObject.call(this, target);
891    this._debuggerAgent = target.debuggerModel._agent;
892    this._script = script;
893    this._payload = payload;
894    this._isAsync = isAsync;
895    this._location = WebInspector.DebuggerModel.Location.fromPayload(target, payload.location);
896}
897
898/**
899 * @param {!WebInspector.Target} target
900 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames
901 * @param {boolean=} isAsync
902 * @return {!Array.<!WebInspector.DebuggerModel.CallFrame>}
903 */
904WebInspector.DebuggerModel.CallFrame.fromPayloadArray = function(target, callFrames, isAsync)
905{
906    var result = [];
907    for (var i = 0; i < callFrames.length; ++i) {
908        var callFrame = callFrames[i];
909        var script = target.debuggerModel.scriptForId(callFrame.location.scriptId);
910        if (script)
911            result.push(new WebInspector.DebuggerModel.CallFrame(target, script, callFrame, isAsync));
912    }
913    return result;
914}
915
916WebInspector.DebuggerModel.CallFrame.prototype = {
917
918    /**
919     * @return {!WebInspector.Script}
920     */
921    get script()
922    {
923        return this._script;
924    },
925
926    /**
927     * @return {string}
928     */
929    get type()
930    {
931        return this._payload.type;
932    },
933
934    /**
935     * @return {string}
936     */
937    get id()
938    {
939        return this._payload.callFrameId;
940    },
941
942    /**
943     * @return {!Array.<!DebuggerAgent.Scope>}
944     */
945    get scopeChain()
946    {
947        return this._payload.scopeChain;
948    },
949
950    /**
951     * @return {?WebInspector.RemoteObject}
952     */
953    thisObject: function()
954    {
955        return this._payload.this ? this.target().runtimeModel.createRemoteObject(this._payload.this) : null;
956    },
957
958    /**
959     * @return {?WebInspector.RemoteObject}
960     */
961    returnValue: function()
962    {
963        return this._payload.returnValue ?  this.target().runtimeModel.createRemoteObject(this._payload.returnValue) : null
964    },
965
966    /**
967     * @return {string}
968     */
969    get functionName()
970    {
971        return this._payload.functionName;
972    },
973
974    /**
975     * @return {!WebInspector.DebuggerModel.Location}
976     */
977    location: function()
978    {
979        return this._location;
980    },
981
982    /**
983     * @return {boolean}
984     */
985    isAsync: function()
986    {
987        return !!this._isAsync;
988    },
989
990    /**
991     * @param {string} code
992     * @param {string} objectGroup
993     * @param {boolean} includeCommandLineAPI
994     * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
995     * @param {boolean} returnByValue
996     * @param {boolean} generatePreview
997     * @param {function(?RuntimeAgent.RemoteObject, boolean=, ?DebuggerAgent.ExceptionDetails=)} callback
998     */
999    evaluate: function(code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
1000    {
1001        /**
1002         * @param {?Protocol.Error} error
1003         * @param {!RuntimeAgent.RemoteObject} result
1004         * @param {boolean=} wasThrown
1005         * @param {?DebuggerAgent.ExceptionDetails=} exceptionDetails
1006         */
1007        function didEvaluateOnCallFrame(error, result, wasThrown, exceptionDetails)
1008        {
1009            if (error) {
1010                console.error(error);
1011                callback(null, false);
1012                return;
1013            }
1014            callback(result, wasThrown, exceptionDetails);
1015        }
1016        this._debuggerAgent.evaluateOnCallFrame(this._payload.callFrameId, code, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, didEvaluateOnCallFrame);
1017    },
1018
1019    /**
1020     * @param {function(?Protocol.Error=)=} callback
1021     */
1022    restart: function(callback)
1023    {
1024        /**
1025         * @param {?Protocol.Error} error
1026         * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames
1027         * @param {!Object=} details
1028         * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
1029         * @this {WebInspector.DebuggerModel.CallFrame}
1030         */
1031        function protocolCallback(error, callFrames, details, asyncStackTrace)
1032        {
1033            if (!error)
1034                this.target().debuggerModel.callStackModified(callFrames, details, asyncStackTrace);
1035            if (callback)
1036                callback(error);
1037        }
1038        this._debuggerAgent.restartFrame(this._payload.callFrameId, protocolCallback.bind(this));
1039    },
1040
1041    __proto__: WebInspector.SDKObject.prototype
1042}
1043
1044/**
1045 * @constructor
1046 * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
1047 * @param {?WebInspector.DebuggerModel.StackTrace} asyncStackTrace
1048 * @param {string=} description
1049 */
1050WebInspector.DebuggerModel.StackTrace = function(callFrames, asyncStackTrace, description)
1051{
1052    this.callFrames = callFrames;
1053    this.asyncStackTrace = asyncStackTrace;
1054    this.description = description;
1055}
1056
1057/**
1058 * @param {!WebInspector.Target} target
1059 * @param {!DebuggerAgent.StackTrace=} payload
1060 * @param {boolean=} isAsync
1061 * @return {?WebInspector.DebuggerModel.StackTrace}
1062 */
1063WebInspector.DebuggerModel.StackTrace.fromPayload = function(target, payload, isAsync)
1064{
1065    if (!payload)
1066        return null;
1067    var callFrames = WebInspector.DebuggerModel.CallFrame.fromPayloadArray(target, payload.callFrames, isAsync);
1068    if (!callFrames.length)
1069        return null;
1070    var asyncStackTrace = WebInspector.DebuggerModel.StackTrace.fromPayload(target, payload.asyncStackTrace, true);
1071    return new WebInspector.DebuggerModel.StackTrace(callFrames, asyncStackTrace, payload.description);
1072}
1073
1074/**
1075 * @constructor
1076 * @extends {WebInspector.SDKObject}
1077 * @param {!WebInspector.Target} target
1078 * @param {!Array.<!DebuggerAgent.CallFrame>} callFrames
1079 * @param {string} reason
1080 * @param {!Object|undefined} auxData
1081 * @param {!Array.<string>} breakpointIds
1082 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace
1083 */
1084WebInspector.DebuggerPausedDetails = function(target, callFrames, reason, auxData, breakpointIds, asyncStackTrace)
1085{
1086    WebInspector.SDKObject.call(this, target);
1087    this.callFrames = WebInspector.DebuggerModel.CallFrame.fromPayloadArray(target, callFrames);
1088    this.reason = reason;
1089    this.auxData = auxData;
1090    this.breakpointIds = breakpointIds;
1091    this.asyncStackTrace = WebInspector.DebuggerModel.StackTrace.fromPayload(target, asyncStackTrace, true);
1092}
1093
1094WebInspector.DebuggerPausedDetails.prototype = {
1095    /**
1096     * @return {?WebInspector.RemoteObject}
1097     */
1098    exception: function()
1099    {
1100        if (this.reason !== WebInspector.DebuggerModel.BreakReason.Exception)
1101            return null;
1102        return this.target().runtimeModel.createRemoteObject(/** @type {!RuntimeAgent.RemoteObject} */(this.auxData));
1103    },
1104
1105    __proto__: WebInspector.SDKObject.prototype
1106}
1107
1108/**
1109 * @type {!WebInspector.DebuggerModel}
1110 */
1111WebInspector.debuggerModel;
1112