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