Home | History | Annotate | Download | only in front-end
      1 /*
      2  * Copyright (C) 2011 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 WebInspector.DebuggerPresentationModel = function()
     32 {
     33     this._sourceFiles = {};
     34     this._messages = [];
     35     this._breakpointsByDebuggerId = {};
     36     this._breakpointsWithoutSourceFile = {};
     37 
     38     this._presentationCallFrames = [];
     39     this._selectedCallFrameIndex = 0;
     40 
     41     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
     42     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
     43     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
     44     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
     45     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
     46     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
     47     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._debuggerReset, this);
     48 
     49     new WebInspector.DebuggerPresentationModelResourceBinding(this);
     50 }
     51 
     52 WebInspector.DebuggerPresentationModel.Events = {
     53     SourceFileAdded: "source-file-added",
     54     SourceFileChanged: "source-file-changed",
     55     ConsoleMessageAdded: "console-message-added",
     56     BreakpointAdded: "breakpoint-added",
     57     BreakpointRemoved: "breakpoint-removed",
     58     DebuggerPaused: "debugger-paused",
     59     DebuggerResumed: "debugger-resumed",
     60     CallFrameSelected: "call-frame-selected"
     61 }
     62 
     63 WebInspector.DebuggerPresentationModel.prototype = {
     64     _debuggerWasEnabled: function()
     65     {
     66         if (this._breakpointsRestored)
     67             return;
     68         this._restoreBreakpointsFromSettings();
     69         this._breakpointsRestored = true;
     70     },
     71 
     72     sourceFile: function(sourceFileId)
     73     {
     74         return this._sourceFiles[sourceFileId];
     75     },
     76 
     77     sourceFileForScriptURL: function(scriptURL)
     78     {
     79         return this._sourceFiles[scriptURL];
     80     },
     81 
     82     requestSourceFileContent: function(sourceFileId, callback)
     83     {
     84         this._sourceFiles[sourceFileId].requestContent(callback);
     85     },
     86 
     87     _parsedScriptSource: function(event)
     88     {
     89         this._addScript(event.data);
     90     },
     91 
     92     _failedToParseScriptSource: function(event)
     93     {
     94         this._addScript(event.data);
     95     },
     96 
     97     _addScript: function(script)
     98     {
     99         var sourceFileId = this._createSourceFileId(script.sourceURL, script.sourceID);
    100         var sourceFile = this._sourceFiles[sourceFileId];
    101         if (sourceFile) {
    102             sourceFile.addScript(script);
    103             return;
    104         }
    105 
    106         function contentChanged(sourceFile)
    107         {
    108             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFiles[sourceFileId]);
    109         }
    110         if (!this._formatSourceFiles)
    111             sourceFile = new WebInspector.SourceFile(sourceFileId, script, contentChanged.bind(this));
    112         else
    113             sourceFile = new WebInspector.FormattedSourceFile(sourceFileId, script, contentChanged.bind(this), this._formatter());
    114         this._sourceFiles[sourceFileId] = sourceFile;
    115 
    116         this._restoreBreakpoints(sourceFile);
    117 
    118         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, sourceFile);
    119     },
    120 
    121     _restoreBreakpoints: function(sourceFile)
    122     {
    123         var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
    124         for (var i = 0; pendingBreakpoints && i < pendingBreakpoints.length; ++i) {
    125             var breakpointData = pendingBreakpoints[i];
    126             if ("debuggerId" in breakpointData) {
    127                 var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
    128                 this._bindDebuggerId(breakpoint, breakpointData.debuggerId);
    129                 this._breakpointAdded(breakpoint);
    130             } else
    131                 this.setBreakpoint(sourceFile.id, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled, true);
    132         }
    133         delete this._breakpointsWithoutSourceFile[sourceFile.id];
    134     },
    135 
    136     canEditScriptSource: function(sourceFileId)
    137     {
    138         if (!Preferences.canEditScriptSource || this._formatSourceFiles)
    139             return false;
    140         var script = this._scriptForSourceFileId(sourceFileId);
    141         return !script.lineOffset && !script.columnOffset;
    142     },
    143 
    144     editScriptSource: function(sourceFileId, newSource, callback)
    145     {
    146         var script = this._scriptForSourceFileId(sourceFileId);
    147         var sourceFile = this._sourceFiles[sourceFileId];
    148 
    149         function didEditScriptSource(oldSource, error)
    150         {
    151             if (!error) {
    152                 sourceFile.content = newSource;
    153 
    154                 var resource = WebInspector.resourceForURL(sourceFile.url);
    155                 if (resource)
    156                     resource.addRevision(newSource);
    157             }
    158 
    159             callback(error);
    160 
    161             if (!error && WebInspector.debuggerModel.callFrames)
    162                 this._debuggerPaused();
    163         }
    164 
    165         var oldSource = sourceFile.requestContent(didReceiveSource.bind(this));
    166         function didReceiveSource(oldSource)
    167         {
    168             WebInspector.debuggerModel.editScriptSource(script.sourceID, newSource, didEditScriptSource.bind(this, oldSource));
    169         }
    170     },
    171 
    172     _updateBreakpointsAfterLiveEdit: function(sourceFileId, oldSource, newSource)
    173     {
    174         var sourceFile = this._sourceFiles[sourceFileId];
    175 
    176         // Clear and re-create breakpoints according to text diff.
    177         var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
    178         for (var lineNumber in sourceFile.breakpoints) {
    179             var breakpoint = sourceFile.breakpoints[lineNumber];
    180 
    181             var lineNumber = breakpoint.lineNumber;
    182             this.removeBreakpoint(sourceFileId, lineNumber);
    183 
    184             var newLineNumber = diff.left[lineNumber].row;
    185             if (newLineNumber === undefined) {
    186                 for (var i = lineNumber - 1; i >= 0; --i) {
    187                     if (diff.left[i].row === undefined)
    188                         continue;
    189                     var shiftedLineNumber = diff.left[i].row + lineNumber - i;
    190                     if (shiftedLineNumber < diff.right.length) {
    191                         var originalLineNumber = diff.right[shiftedLineNumber].row;
    192                         if (originalLineNumber === lineNumber || originalLineNumber === undefined)
    193                             newLineNumber = shiftedLineNumber;
    194                     }
    195                     break;
    196                 }
    197             }
    198             if (newLineNumber !== undefined)
    199                 this.setBreakpoint(sourceFileId, newLineNumber, breakpoint.condition, breakpoint.enabled);
    200         }
    201     },
    202 
    203     toggleFormatSourceFiles: function()
    204     {
    205         this._formatSourceFiles = !this._formatSourceFiles;
    206 
    207         for (var id in this._sourceFiles) {
    208             var sourceFile = this._sourceFiles[id];
    209             for (var line in sourceFile.breakpoints)
    210                 this._removeBreakpointFromDebugger(sourceFile.breakpoints[line]);
    211         }
    212 
    213         var messages = this._messages;
    214         this._reset();
    215 
    216         var scripts = WebInspector.debuggerModel.scripts;
    217         for (var id in scripts)
    218             this._addScript(scripts[id]);
    219 
    220         for (var i = 0; i < messages.length; ++i)
    221             this.addConsoleMessage(messages[i]);
    222 
    223         if (WebInspector.debuggerModel.callFrames)
    224             this._debuggerPaused();
    225     },
    226 
    227     formatSourceFilesToggled: function()
    228     {
    229         return this._formatSourceFiles;
    230     },
    231 
    232     _formatter: function()
    233     {
    234         if (!this._scriptFormatter)
    235             this._scriptFormatter = new WebInspector.ScriptFormatter();
    236         return this._scriptFormatter;
    237     },
    238 
    239     addConsoleMessage: function(message)
    240     {
    241         this._messages.push(message);
    242 
    243         var sourceFile = this._sourceFileForScript(message.url);
    244         if (!sourceFile)
    245             return;
    246 
    247         function didRequestSourceMapping(mapping)
    248         {
    249             var presentationMessage = {};
    250             presentationMessage.sourceFileId = sourceFile.id;
    251             presentationMessage.lineNumber = mapping.scriptLocationToSourceLine({lineNumber:message.line - 1, columnNumber:0});
    252             presentationMessage.originalMessage = message;
    253             sourceFile.messages.push(presentationMessage);
    254             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, presentationMessage);
    255         }
    256         sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
    257     },
    258 
    259     clearConsoleMessages: function()
    260     {
    261         this._messages = [];
    262         for (var id in this._sourceFiles)
    263             this._sourceFiles[id].messages = [];
    264     },
    265 
    266     continueToLine: function(sourceFileId, lineNumber)
    267     {
    268         function didRequestSourceMapping(mapping)
    269         {
    270             var location = mapping.sourceLineToScriptLocation(lineNumber);
    271             WebInspector.debuggerModel.continueToLocation(location);
    272         }
    273         this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this));
    274     },
    275 
    276     breakpointsForSourceFileId: function(sourceFileId)
    277     {
    278         var sourceFile = this.sourceFile(sourceFileId);
    279         if (!sourceFile)
    280             return [];
    281         var breakpoints = [];
    282         for (var lineNumber in sourceFile.breakpoints)
    283             breakpoints.push(sourceFile.breakpoints[lineNumber]);
    284         return breakpoints;
    285     },
    286 
    287     setBreakpoint: function(sourceFileId, lineNumber, condition, enabled, dontSaveBreakpoints)
    288     {
    289         var sourceFile = this._sourceFiles[sourceFileId];
    290         if (!sourceFile)
    291             return;
    292 
    293         var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, lineNumber, condition, enabled);
    294         if (!enabled) {
    295             this._breakpointAdded(breakpoint);
    296             if (!dontSaveBreakpoints)
    297                 this._saveBreakpoints();
    298             return;
    299         }
    300 
    301         function callback()
    302         {
    303             this._breakpointAdded(breakpoint);
    304             if (!dontSaveBreakpoints)
    305                 this._saveBreakpoints();
    306         }
    307         this._setBreakpointInDebugger(breakpoint, callback.bind(this));
    308     },
    309 
    310     _setBreakpointInDebugger: function(breakpoint, callback)
    311     {
    312         function didSetBreakpoint(breakpointId, locations)
    313         {
    314             if (!breakpointId)
    315                 return;
    316 
    317             this._bindDebuggerId(breakpoint, breakpointId);
    318             breakpoint.location = locations[0];
    319             callback();
    320         }
    321 
    322         function didRequestSourceMapping(mapping)
    323         {
    324             var location = mapping.sourceLineToScriptLocation(breakpoint.lineNumber);
    325             var script = WebInspector.debuggerModel.scriptForSourceID(location.sourceID);
    326             if (script.sourceURL)
    327                 WebInspector.debuggerModel.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, breakpoint.condition, didSetBreakpoint.bind(this));
    328             else {
    329                 location.sourceID = script.sourceID;
    330                 WebInspector.debuggerModel.setBreakpointBySourceId(location, breakpoint.condition, didSetBreakpoint.bind(this));
    331             }
    332         }
    333         breakpoint.sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
    334     },
    335 
    336     _removeBreakpointFromDebugger: function(breakpoint, callback)
    337     {
    338         if (!("debuggerId" in breakpoint)) {
    339             if (callback)
    340                 callback();
    341             return;
    342         }
    343 
    344         function didRemoveBreakpoint()
    345         {
    346             this._unbindDebuggerId(breakpoint);
    347             if (callback)
    348                 callback();
    349         }
    350         WebInspector.debuggerModel.removeBreakpoint(breakpoint.debuggerId, didRemoveBreakpoint.bind(this));
    351     },
    352 
    353     _bindDebuggerId: function(breakpoint, debuggerId)
    354     {
    355         breakpoint.debuggerId = debuggerId;
    356         this._breakpointsByDebuggerId[debuggerId] = breakpoint;
    357     },
    358 
    359     _unbindDebuggerId: function(breakpoint)
    360     {
    361         delete this._breakpointsByDebuggerId[breakpoint.debuggerId];
    362         delete breakpoint.debuggerId;
    363     },
    364 
    365     setBreakpointEnabled: function(sourceFileId, lineNumber, enabled)
    366     {
    367         var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
    368         if (!breakpoint)
    369             return;
    370 
    371         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
    372 
    373         breakpoint.enabled = enabled;
    374 
    375         function afterUpdate()
    376         {
    377             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
    378             this._saveBreakpoints();
    379         }
    380 
    381         if (!enabled)
    382             this._removeBreakpointFromDebugger(breakpoint, afterUpdate.call(this));
    383         else
    384             this._setBreakpointInDebugger(breakpoint, afterUpdate.bind(this));
    385     },
    386 
    387     updateBreakpoint: function(sourceFileId, lineNumber, condition, enabled)
    388     {
    389         this.removeBreakpoint(sourceFileId, lineNumber);
    390         this.setBreakpoint(sourceFileId, lineNumber, condition, enabled);
    391     },
    392 
    393     removeBreakpoint: function(sourceFileId, lineNumber)
    394     {
    395         var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
    396         if (!breakpoint)
    397             return;
    398 
    399         function callback()
    400         {
    401             this._breakpointRemoved(breakpoint);
    402             this._saveBreakpoints();
    403         }
    404         this._removeBreakpointFromDebugger(breakpoint, callback.bind(this));
    405     },
    406 
    407     findBreakpoint: function(sourceFileId, lineNumber)
    408     {
    409         var sourceFile = this.sourceFile(sourceFileId);
    410         if (sourceFile)
    411             return sourceFile.breakpoints[lineNumber];
    412     },
    413 
    414     _breakpointAdded: function(breakpoint)
    415     {
    416         var sourceFile = breakpoint.sourceFile;
    417         if (!sourceFile)
    418             return;
    419 
    420         function didRequestSourceMapping(mapping)
    421         {
    422             // Refine line number based on resolved location.
    423             if (breakpoint.location)
    424                 breakpoint.lineNumber = mapping.scriptLocationToSourceLine(breakpoint.location);
    425 
    426             var existingBreakpoint = this.findBreakpoint(sourceFile.id, breakpoint.lineNumber);
    427             if (existingBreakpoint) {
    428                 // We can't show more than one breakpoint on a single source file line.
    429                 this._removeBreakpointFromDebugger(breakpoint);
    430                 return;
    431             }
    432             sourceFile.breakpoints[breakpoint.lineNumber] = breakpoint;
    433             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
    434         }
    435         sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
    436     },
    437 
    438     _breakpointRemoved: function(breakpoint)
    439     {
    440         var sourceFile = breakpoint.sourceFile;
    441         if (sourceFile.breakpoints[breakpoint.lineNumber] === breakpoint) {
    442             // There can already be a newer breakpoint;
    443             delete sourceFile.breakpoints[breakpoint.lineNumber];
    444             this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
    445         }
    446     },
    447 
    448     _breakpointResolved: function(event)
    449     {
    450         var debuggerId = event.data.breakpointId;
    451         if (!(debuggerId in this._breakpointsByDebuggerId))
    452             return;
    453         var breakpoint = this._breakpointsByDebuggerId[debuggerId];
    454 
    455         this._breakpointRemoved(breakpoint);
    456         breakpoint.location = event.data.location;
    457         this._breakpointAdded(breakpoint);
    458     },
    459 
    460     _restoreBreakpointsFromSettings: function()
    461     {
    462         var breakpoints = WebInspector.settings.breakpoints;
    463         for (var i = 0; i < breakpoints.length; ++i) {
    464             var breakpointData = breakpoints[i];
    465             var sourceFileId = breakpointData.sourceFileId;
    466             if (!sourceFileId)
    467                 continue;
    468             var sourceFile = this._sourceFiles[sourceFileId];
    469             if (sourceFile) {
    470                 this.setBreakpoint(sourceFileId, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
    471                 continue;
    472             }
    473 
    474             // Add breakpoint once source file becomes available.
    475             var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFileId];
    476             if (!pendingBreakpoints) {
    477                 pendingBreakpoints = [];
    478                 this._breakpointsWithoutSourceFile[sourceFileId] = pendingBreakpoints;
    479             }
    480             pendingBreakpoints.push(breakpointData);
    481         }
    482     },
    483 
    484     _saveBreakpoints: function()
    485     {
    486         var serializedBreakpoints = [];
    487 
    488         // Store added breakpoints.
    489         for (var sourceFileId in this._sourceFiles) {
    490             var sourceFile = this._sourceFiles[sourceFileId];
    491             if (!sourceFile.url)
    492                 continue;
    493 
    494             for (var lineNumber in sourceFile.breakpoints)
    495                 serializedBreakpoints.push(sourceFile.breakpoints[lineNumber].serialize());
    496         }
    497 
    498         // Store not added breakpoints.
    499         for (var sourceFileId in this._breakpointsWithoutSourceFile)
    500             serializedBreakpoints = serializedBreakpoints.concat(this._breakpointsWithoutSourceFile[sourceFileId]);
    501 
    502         // Sanitize debugger ids.
    503         for (var i = 0; i < serializedBreakpoints.length; ++i) {
    504             var breakpoint = serializedBreakpoints[i];
    505             var breakpointCopy = {};
    506             for (var property in breakpoint) {
    507                 if (property !== "debuggerId")
    508                     breakpointCopy[property] = breakpoint[property];
    509             }
    510             serializedBreakpoints[i] = breakpointCopy;
    511         }
    512 
    513         WebInspector.settings.breakpoints = serializedBreakpoints;
    514     },
    515 
    516     _debuggerPaused: function()
    517     {
    518         var callFrames = WebInspector.debuggerModel.callFrames;
    519         this._presentationCallFrames = [];
    520         for (var i = 0; i < callFrames.length; ++i) {
    521             var callFrame = callFrames[i];
    522             var sourceFile;
    523             var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
    524             if (script)
    525                 sourceFile = this._sourceFileForScript(script.sourceURL, script.sourceID);
    526             this._presentationCallFrames.push(new WebInspector.PresenationCallFrame(callFrame, i, sourceFile));
    527         }
    528         var details = WebInspector.debuggerModel.debuggerPausedDetails;
    529         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
    530 
    531         this.selectedCallFrame = this._presentationCallFrames[this._selectedCallFrameIndex];
    532     },
    533 
    534     _debuggerResumed: function()
    535     {
    536         this._presentationCallFrames = [];
    537         this._selectedCallFrameIndex = 0;
    538         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
    539     },
    540 
    541     set selectedCallFrame(callFrame)
    542     {
    543         this._selectedCallFrameIndex = callFrame.index;
    544         callFrame.select();
    545         this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
    546     },
    547 
    548     get selectedCallFrame()
    549     {
    550         return this._presentationCallFrames[this._selectedCallFrameIndex];
    551     },
    552 
    553     _sourceFileForScript: function(sourceURL, sourceID)
    554     {
    555         return this._sourceFiles[this._createSourceFileId(sourceURL, sourceID)];
    556     },
    557 
    558     _scriptForSourceFileId: function(sourceFileId)
    559     {
    560         function filter(script)
    561         {
    562             return this._createSourceFileId(script.sourceURL, script.sourceID) === sourceFileId;
    563         }
    564         return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0];
    565     },
    566 
    567     _createSourceFileId: function(sourceURL, sourceID)
    568     {
    569         var prefix = this._formatSourceFiles ? "deobfuscated:" : "";
    570         return prefix + (sourceURL || sourceID);
    571     },
    572 
    573     _reset: function()
    574     {
    575         for (var id in this._sourceFiles) {
    576             var sourceFile = this._sourceFiles[id];
    577             for (var line in sourceFile.breakpoints) {
    578                 var breakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
    579                 if (!breakpoints) {
    580                     breakpoints = [];
    581                     this._breakpointsWithoutSourceFile[sourceFile.id] = breakpoints;
    582                 }
    583                 breakpoints.push(sourceFile.breakpoints[line].serialize());
    584             }
    585         }
    586 
    587         this._sourceFiles = {};
    588         this._messages = [];
    589         this._breakpointsByDebuggerId = {};
    590     },
    591 
    592     _debuggerReset: function()
    593     {
    594         this._reset();
    595         this._presentationCallFrames = [];
    596         this._selectedCallFrameIndex = 0;
    597     }
    598 }
    599 
    600 WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
    601 
    602 WebInspector.PresentationBreakpoint = function(sourceFile, lineNumber, condition, enabled)
    603 {
    604     this.sourceFile = sourceFile;
    605     this.sourceFileId = sourceFile.id;
    606     this.lineNumber = lineNumber;
    607     this.condition = condition;
    608     this.enabled = enabled;
    609 }
    610 
    611 WebInspector.PresentationBreakpoint.prototype = {
    612     get url()
    613     {
    614         return this.sourceFile.url;
    615     },
    616 
    617     get resolved()
    618     {
    619         return !!this.location;
    620     },
    621 
    622     loadSnippet: function(callback)
    623     {
    624         function didRequestContent(mimeType, content)
    625         {
    626             var lineEndings = content.lineEndings();
    627             var snippet = "";
    628             if (this.lineNumber < lineEndings.length)
    629                 snippet = content.substring(lineEndings[this.lineNumber - 1], lineEndings[this.lineNumber]);
    630             callback(snippet);
    631         }
    632         if (!this.sourceFile) {
    633             callback(WebInspector.UIString("N/A"));
    634             return;
    635         }
    636         this.sourceFile.requestContent(didRequestContent.bind(this));
    637     },
    638 
    639     serialize: function()
    640     {
    641         var serializedBreakpoint = {};
    642         serializedBreakpoint.sourceFileId = this.sourceFile.id;
    643         serializedBreakpoint.lineNumber = this.lineNumber;
    644         serializedBreakpoint.condition = this.condition;
    645         serializedBreakpoint.enabled = this.enabled;
    646         serializedBreakpoint.debuggerId = this.debuggerId;
    647         return serializedBreakpoint;
    648     }
    649 }
    650 
    651 WebInspector.PresenationCallFrame = function(callFrame, index, sourceFile)
    652 {
    653     this._callFrame = callFrame;
    654     this._index = index;
    655     this._sourceFile = sourceFile;
    656     this._script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
    657 }
    658 
    659 WebInspector.PresenationCallFrame.prototype = {
    660     get functionName()
    661     {
    662         return this._callFrame.functionName;
    663     },
    664 
    665     get type()
    666     {
    667         return this._callFrame.type;
    668     },
    669 
    670     get isInternalScript()
    671     {
    672         return !this._script;
    673     },
    674 
    675     get url()
    676     {
    677         if (this._sourceFile)
    678             return this._sourceFile.url;
    679     },
    680 
    681     get scopeChain()
    682     {
    683         return this._callFrame.scopeChain;
    684     },
    685 
    686     get index()
    687     {
    688         return this._index;
    689     },
    690 
    691     select: function()
    692     {
    693         if (this._sourceFile)
    694             this._sourceFile.forceLoadContent(this._script);
    695     },
    696 
    697     evaluate: function(code, objectGroup, includeCommandLineAPI, callback)
    698     {
    699         function didEvaluateOnCallFrame(error, result)
    700         {
    701             callback(WebInspector.RemoteObject.fromPayload(result));
    702         }
    703         DebuggerAgent.evaluateOnCallFrame(this._callFrame.id, code, objectGroup, includeCommandLineAPI, didEvaluateOnCallFrame.bind(this));
    704     },
    705 
    706     sourceLine: function(callback)
    707     {
    708         if (!this._sourceFile) {
    709             callback(undefined, this._callFrame.location.lineNumber);
    710             return;
    711         }
    712 
    713         function didRequestSourceMapping(mapping)
    714         {
    715             callback(this._sourceFile.id, mapping.scriptLocationToSourceLine(this._callFrame.location));
    716         }
    717         this._sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
    718     }
    719 }
    720 
    721 WebInspector.DebuggerPresentationModelResourceBinding = function(model)
    722 {
    723     this._presentationModel = model;
    724     WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
    725 }
    726 
    727 WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
    728     canSetContent: function(resource)
    729     {
    730         var sourceFile = this._presentationModel._sourceFileForScript(resource.url)
    731         if (!sourceFile)
    732             return false;
    733         return this._presentationModel.canEditScriptSource(sourceFile.id);
    734     },
    735 
    736     setContent: function(resource, content, majorChange, userCallback)
    737     {
    738         if (!majorChange)
    739             return;
    740 
    741         var sourceFile = this._presentationModel._sourceFileForScript(resource.url);
    742         if (!sourceFile) {
    743             userCallback("Resource is not editable");
    744             return;
    745         }
    746 
    747         resource.requestContent(this._setContentWithInitialContent.bind(this, sourceFile, content, userCallback));
    748     },
    749 
    750     _setContentWithInitialContent: function(sourceFile, content, userCallback, oldContent)
    751     {
    752         function callback(error)
    753         {
    754             if (userCallback)
    755                 userCallback(error);
    756             if (!error) {
    757                 this._presentationModel._updateBreakpointsAfterLiveEdit(sourceFile.id, oldContent, content);
    758                 sourceFile.reload();
    759             }
    760         }
    761         this._presentationModel.editScriptSource(sourceFile.id, content, callback.bind(this));
    762     }
    763 }
    764 
    765 WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;
    766