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