Home | History | Annotate | Download | only in sdk
      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  */
     36 WebInspector.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  */
     73 WebInspector.DebuggerModel.PauseOnExceptionsState = {
     74     DontPauseOnExceptions : "none",
     75     PauseOnAllExceptions : "all",
     76     PauseOnUncaughtExceptions: "uncaught"
     77 };
     78 
     79 WebInspector.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 
     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 
    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 
    763 WebInspector.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  */
    774 WebInspector.DebuggerDispatcher = function(debuggerModel)
    775 {
    776     this._debuggerModel = debuggerModel;
    777 }
    778 
    779 WebInspector.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  */
    854 WebInspector.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  */
    868 WebInspector.DebuggerModel.Location.fromPayload = function(target, payload)
    869 {
    870     return new WebInspector.DebuggerModel.Location(target, payload.scriptId, payload.lineNumber, payload.columnNumber);
    871 }
    872 
    873 WebInspector.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  */
    931 WebInspector.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  */
    949 WebInspector.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 
    961 WebInspector.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  */
   1112 WebInspector.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  */
   1125 WebInspector.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 
   1136 WebInspector.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  */
   1156 WebInspector.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 
   1166 WebInspector.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  */
   1191 WebInspector.debuggerModel;
   1192