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