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 /**
     32  * @constructor
     33  * @extends {WebInspector.Object}
     34  * @param {!WebInspector.Setting} breakpointStorage
     35  * @param {!WebInspector.DebuggerModel} debuggerModel
     36  * @param {!WebInspector.Workspace} workspace
     37  */
     38 WebInspector.BreakpointManager = function(breakpointStorage, debuggerModel, workspace)
     39 {
     40     this._storage = new WebInspector.BreakpointManager.Storage(this, breakpointStorage);
     41     this._debuggerModel = debuggerModel;
     42     this._workspace = workspace;
     43 
     44     this._breakpointForDebuggerId = {};
     45     this._breakpointsForUISourceCode = new Map();
     46     this._breakpointsForPrimaryUISourceCode = new Map();
     47     this._sourceFilesWithRestoredBreakpoints = {};
     48 
     49     this._debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
     50     this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
     51     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
     52     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
     53 }
     54 
     55 WebInspector.BreakpointManager.Events = {
     56     BreakpointAdded: "breakpoint-added",
     57     BreakpointRemoved: "breakpoint-removed"
     58 }
     59 
     60 WebInspector.BreakpointManager._sourceFileId = function(uiSourceCode)
     61 {
     62     if (!uiSourceCode.url)
     63         return "";
     64     var deobfuscatedPrefix = uiSourceCode.formatted() ? "deobfuscated:" : "";
     65     return deobfuscatedPrefix + uiSourceCode.uri();
     66 }
     67 
     68 /**
     69  * @param {string} sourceFileId
     70  * @param {number} lineNumber
     71  * @return {string}
     72  */
     73 WebInspector.BreakpointManager._breakpointStorageId = function(sourceFileId, lineNumber)
     74 {
     75     if (!sourceFileId)
     76         return "";
     77     return sourceFileId + ":" + lineNumber;
     78 }
     79 
     80 WebInspector.BreakpointManager.prototype = {
     81     /**
     82      * @param {string} sourceFileId
     83      */
     84     _provisionalBreakpointsForSourceFileId: function(sourceFileId)
     85     {
     86         var result = new StringMap();
     87         for (var debuggerId in this._breakpointForDebuggerId) {
     88             var breakpoint = this._breakpointForDebuggerId[debuggerId];
     89             if (breakpoint._sourceFileId === sourceFileId)
     90                 result.put(breakpoint._breakpointStorageId(), breakpoint);
     91         }
     92         return result;
     93     },
     94 
     95     /**
     96      * @param {!WebInspector.UISourceCode} uiSourceCode
     97      */
     98     _restoreBreakpoints: function(uiSourceCode)
     99     {
    100         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    101         if (!sourceFileId || this._sourceFilesWithRestoredBreakpoints[sourceFileId])
    102             return;
    103         this._sourceFilesWithRestoredBreakpoints[sourceFileId] = true;
    104 
    105         this._storage.mute();
    106         var breakpointItems = this._storage.breakpointItems(uiSourceCode);
    107         var provisionalBreakpoints = this._provisionalBreakpointsForSourceFileId(sourceFileId);
    108         for (var i = 0; i < breakpointItems.length; ++i) {
    109             var breakpointItem = breakpointItems[i];
    110             var itemStorageId = WebInspector.BreakpointManager._breakpointStorageId(breakpointItem.sourceFileId, breakpointItem.lineNumber);
    111             var provisionalBreakpoint = provisionalBreakpoints.get(itemStorageId);
    112             if (provisionalBreakpoint) {
    113                 if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
    114                     this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []);
    115                 this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(provisionalBreakpoint);
    116                 provisionalBreakpoint._updateInDebugger();
    117             } else {
    118                 this._innerSetBreakpoint(uiSourceCode, breakpointItem.lineNumber, breakpointItem.condition, breakpointItem.enabled);
    119             }
    120         }
    121         this._storage.unmute();
    122     },
    123 
    124     /**
    125      * @param {!WebInspector.Event} event
    126      */
    127     _uiSourceCodeAdded: function(event)
    128     {
    129         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
    130         this._restoreBreakpoints(uiSourceCode);
    131         if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) {
    132             uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this);
    133             uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormatted, this);
    134         }
    135     },
    136 
    137     /**
    138      * @param {!WebInspector.Event} event
    139      */
    140     _uiSourceCodeFormatted: function(event)
    141     {
    142         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
    143         this._restoreBreakpoints(uiSourceCode);
    144     },
    145 
    146     /**
    147      * @param {!WebInspector.Event} event
    148      */
    149     _uiSourceCodeRemoved: function(event)
    150     {
    151         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
    152         this._removeUISourceCode(uiSourceCode);
    153     },
    154 
    155     /**
    156      * @param {!WebInspector.Event} event
    157      */
    158     _uiSourceCodeMappingChanged: function(event)
    159     {
    160         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
    161         var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [];
    162         for (var i = 0; i < breakpoints.length; ++i)
    163             breakpoints[i]._updateInDebugger();
    164     },
    165 
    166     /**
    167      * @param {!WebInspector.UISourceCode} uiSourceCode
    168      */
    169     _removeUISourceCode: function(uiSourceCode)
    170     {
    171         var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [];
    172         for (var i = 0; i < breakpoints.length; ++i)
    173             breakpoints[i]._resetLocations();
    174         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    175         delete this._sourceFilesWithRestoredBreakpoints[sourceFileId];
    176         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormatted, this);
    177         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this);
    178         this._breakpointsForPrimaryUISourceCode.remove(uiSourceCode);
    179     },
    180 
    181     /**
    182      * @param {!WebInspector.UISourceCode} uiSourceCode
    183      * @param {number} lineNumber
    184      * @param {string} condition
    185      * @param {boolean} enabled
    186      * @return {!WebInspector.BreakpointManager.Breakpoint}
    187      */
    188     setBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
    189     {
    190         this._debuggerModel.setBreakpointsActive(true);
    191         return this._innerSetBreakpoint(uiSourceCode, lineNumber, condition, enabled);
    192     },
    193 
    194     /**
    195      * @param {!WebInspector.UISourceCode} uiSourceCode
    196      * @param {number} lineNumber
    197      * @param {string} condition
    198      * @param {boolean} enabled
    199      * @return {!WebInspector.BreakpointManager.Breakpoint}
    200      */
    201     _innerSetBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
    202     {
    203         var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber);
    204         if (breakpoint) {
    205             breakpoint._updateBreakpoint(condition, enabled);
    206             return breakpoint;
    207         }
    208         var projectId = uiSourceCode.project().id();
    209         var path = uiSourceCode.path();
    210         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    211         breakpoint = new WebInspector.BreakpointManager.Breakpoint(this, projectId, path, sourceFileId, lineNumber, condition, enabled);
    212         if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
    213             this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []);
    214         this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(breakpoint);
    215         return breakpoint;
    216     },
    217 
    218     /**
    219      * @param {!WebInspector.UISourceCode} uiSourceCode
    220      * @param {number} lineNumber
    221      * @return {?WebInspector.BreakpointManager.Breakpoint}
    222      */
    223     findBreakpoint: function(uiSourceCode, lineNumber)
    224     {
    225         var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    226         var lineBreakpoints = breakpoints ? breakpoints[lineNumber] : null;
    227         return lineBreakpoints ? lineBreakpoints[0] : null;
    228     },
    229 
    230     /**
    231      * @param {!WebInspector.UISourceCode} uiSourceCode
    232      * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>}
    233      */
    234     breakpointsForUISourceCode: function(uiSourceCode)
    235     {
    236         var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    237         var allLineBreakpoints = breakpoints ? Object.values(breakpoints) : [];
    238         var result = [];
    239         for (var i = 0; i < allLineBreakpoints.length; ++i)
    240             result = result.concat(allLineBreakpoints[i]);
    241         return result;
    242     },
    243 
    244     /**
    245      * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>}
    246      */
    247     allBreakpoints: function()
    248     {
    249         var result = [];
    250         var uiSourceCodes = this._breakpointsForUISourceCode.keys();
    251         for (var i = 0; i < uiSourceCodes.length; ++i)
    252             result = result.concat(this.breakpointsForUISourceCode(uiSourceCodes[i]));
    253         return result;
    254     },
    255 
    256     /**
    257      * @param {!WebInspector.UISourceCode} uiSourceCode
    258      * @return {!Array.<{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
    259      */
    260     breakpointLocationsForUISourceCode: function(uiSourceCode)
    261     {
    262         var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    263         var breakpointLines = breakpoints ? Object.keys(breakpoints) : [];
    264         var result = [];
    265         for (var i = 0; i < breakpointLines.length; ++i) {
    266             var lineNumber = parseInt(breakpointLines[i], 10);
    267             if (isNaN(lineNumber))
    268                 continue;
    269             var lineBreakpoints = breakpoints[lineNumber];
    270             for (var j = 0; j < lineBreakpoints.length; ++j) {
    271                 var breakpoint = lineBreakpoints[j];
    272                 var uiLocation = new WebInspector.UILocation(uiSourceCode, lineNumber, 0);
    273                 result.push({breakpoint: breakpoint, uiLocation: uiLocation});
    274             }
    275         }
    276         return result;
    277     },
    278 
    279     /**
    280      * @return {!Array.<{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
    281      */
    282     allBreakpointLocations: function()
    283     {
    284         var result = [];
    285         var uiSourceCodes = this._breakpointsForUISourceCode.keys();
    286         for (var i = 0; i < uiSourceCodes.length; ++i)
    287             result = result.concat(this.breakpointLocationsForUISourceCode(uiSourceCodes[i]));
    288         return result;
    289     },
    290 
    291     /**
    292      * @param {boolean} toggleState
    293      */
    294     toggleAllBreakpoints: function(toggleState)
    295     {
    296         var breakpoints = this.allBreakpoints();
    297         for (var i = 0; i < breakpoints.length; ++i)
    298             breakpoints[i].setEnabled(toggleState);
    299     },
    300 
    301     removeAllBreakpoints: function()
    302     {
    303         var breakpoints = this.allBreakpoints();
    304         for (var i = 0; i < breakpoints.length; ++i)
    305             breakpoints[i].remove();
    306     },
    307 
    308     removeProvisionalBreakpoints: function()
    309     {
    310         for (var debuggerId in this._breakpointForDebuggerId)
    311             this._debuggerModel.removeBreakpoint(debuggerId);
    312     },
    313 
    314     _projectWillReset: function(event)
    315     {
    316         var project = /** @type {!WebInspector.Project} */ (event.data);
    317         var uiSourceCodes = project.uiSourceCodes();
    318         for (var i = 0; i < uiSourceCodes.length; ++i)
    319             this._removeUISourceCode(uiSourceCodes[i]);
    320     },
    321 
    322     _breakpointResolved: function(event)
    323     {
    324         var breakpointId = /** @type {!DebuggerAgent.BreakpointId} */ (event.data.breakpointId);
    325         var location = /** @type {!WebInspector.DebuggerModel.Location} */ (event.data.location);
    326         var breakpoint = this._breakpointForDebuggerId[breakpointId];
    327         if (!breakpoint)
    328             return;
    329         breakpoint._addResolvedLocation(location);
    330     },
    331 
    332     /**
    333      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    334      * @param {boolean} removeFromStorage
    335      */
    336     _removeBreakpoint: function(breakpoint, removeFromStorage)
    337     {
    338         var uiSourceCode = breakpoint.uiSourceCode();
    339         var breakpoints = uiSourceCode ? this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [] : [];
    340         var index = breakpoints.indexOf(breakpoint);
    341         if (index > -1)
    342             breakpoints.splice(index, 1);
    343         console.assert(!breakpoint._debuggerId)
    344         if (removeFromStorage)
    345             this._storage._removeBreakpoint(breakpoint);
    346     },
    347 
    348     /**
    349      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    350      * @param {!WebInspector.UILocation} uiLocation
    351      */
    352     _uiLocationAdded: function(breakpoint, uiLocation)
    353     {
    354         var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
    355         if (!breakpoints) {
    356             breakpoints = {};
    357             this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints);
    358         }
    359 
    360         var lineBreakpoints = breakpoints[uiLocation.lineNumber];
    361         if (!lineBreakpoints) {
    362             lineBreakpoints = [];
    363             breakpoints[uiLocation.lineNumber] = lineBreakpoints;
    364         }
    365 
    366         lineBreakpoints.push(breakpoint);
    367         this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
    368     },
    369 
    370     /**
    371      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    372      * @param {!WebInspector.UILocation} uiLocation
    373      */
    374     _uiLocationRemoved: function(breakpoint, uiLocation)
    375     {
    376         var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
    377         if (!breakpoints)
    378             return;
    379 
    380         var lineBreakpoints = breakpoints[uiLocation.lineNumber];
    381         if (!lineBreakpoints)
    382             return;
    383 
    384         lineBreakpoints.remove(breakpoint);
    385         if (!lineBreakpoints.length)
    386             delete breakpoints[uiLocation.lineNumber];
    387         if (Object.keys(breakpoints).length === 0)
    388             this._breakpointsForUISourceCode.remove(uiLocation.uiSourceCode);
    389         this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
    390     },
    391 
    392     __proto__: WebInspector.Object.prototype
    393 }
    394 
    395 /**
    396  * @constructor
    397  * @param {!WebInspector.BreakpointManager} breakpointManager
    398  * @param {string} projectId
    399  * @param {string} path
    400  * @param {string} sourceFileId
    401  * @param {number} lineNumber
    402  * @param {string} condition
    403  * @param {boolean} enabled
    404  */
    405 WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, projectId, path, sourceFileId, lineNumber, condition, enabled)
    406 {
    407     this._breakpointManager = breakpointManager;
    408     this._projectId = projectId;
    409     this._path = path;
    410     this._lineNumber = lineNumber;
    411     this._sourceFileId = sourceFileId;
    412     /** @type {!Array.<!WebInspector.Script.Location>} */
    413     this._liveLocations = [];
    414     /** @type {!Object.<string, !WebInspector.UILocation>} */
    415     this._uiLocations = {};
    416 
    417     // Force breakpoint update.
    418     /** @type {string} */ this._condition;
    419     /** @type {boolean} */ this._enabled;
    420     this._updateBreakpoint(condition, enabled);
    421 }
    422 
    423 WebInspector.BreakpointManager.Breakpoint.prototype = {
    424     /**
    425      * @return {string}
    426      */
    427     projectId: function()
    428     {
    429         return this._projectId;
    430     },
    431 
    432     /**
    433      * @return {string}
    434      */
    435     path: function()
    436     {
    437         return this._path;
    438     },
    439 
    440     /**
    441      * @return {number}
    442      */
    443     lineNumber: function()
    444     {
    445         return this._lineNumber;
    446     },
    447 
    448     /**
    449      * @return {?WebInspector.UISourceCode}
    450      */
    451     uiSourceCode: function()
    452     {
    453         return this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path);
    454     },
    455 
    456     /**
    457      * @param {!WebInspector.DebuggerModel.Location} location
    458      */
    459     _addResolvedLocation: function(location)
    460     {
    461         this._liveLocations.push(this._breakpointManager._debuggerModel.createLiveLocation(location, this._locationUpdated.bind(this, location)));
    462     },
    463 
    464     /**
    465      * @param {!WebInspector.DebuggerModel.Location} location
    466      * @param {!WebInspector.UILocation} uiLocation
    467      */
    468     _locationUpdated: function(location, uiLocation)
    469     {
    470         var stringifiedLocation = location.scriptId + ":" + location.lineNumber + ":" + location.columnNumber;
    471         var oldUILocation = /** @type {!WebInspector.UILocation} */ (this._uiLocations[stringifiedLocation]);
    472         if (oldUILocation)
    473             this._breakpointManager._uiLocationRemoved(this, oldUILocation);
    474         if (this._uiLocations[""]) {
    475             var defaultLocation = this._uiLocations[""];
    476             delete this._uiLocations[""];
    477             this._breakpointManager._uiLocationRemoved(this, defaultLocation);
    478         }
    479         this._uiLocations[stringifiedLocation] = uiLocation;
    480         this._breakpointManager._uiLocationAdded(this, uiLocation);
    481     },
    482 
    483     /**
    484      * @return {boolean}
    485      */
    486     enabled: function()
    487     {
    488         return this._enabled;
    489     },
    490 
    491     /**
    492      * @param {boolean} enabled
    493      */
    494     setEnabled: function(enabled)
    495     {
    496         this._updateBreakpoint(this._condition, enabled);
    497     },
    498 
    499     /**
    500      * @return {string}
    501      */
    502     condition: function()
    503     {
    504         return this._condition;
    505     },
    506 
    507     /**
    508      * @param {string} condition
    509      */
    510     setCondition: function(condition)
    511     {
    512         this._updateBreakpoint(condition, this._enabled);
    513     },
    514 
    515     /**
    516      * @param {string} condition
    517      * @param {boolean} enabled
    518      */
    519     _updateBreakpoint: function(condition, enabled)
    520     {
    521         if (this._enabled === enabled && this._condition === condition)
    522             return;
    523         this._removeFromDebugger();
    524         this._enabled = enabled;
    525         this._condition = condition;
    526         this._breakpointManager._storage._updateBreakpoint(this);
    527         this._fakeBreakpointAtPrimaryLocation();
    528         this._updateInDebugger();
    529     },
    530 
    531     _updateInDebugger: function()
    532     {
    533         var uiSourceCode = this.uiSourceCode();
    534         if (!uiSourceCode || !uiSourceCode.hasSourceMapping())
    535             return;
    536         var scriptFile = uiSourceCode && uiSourceCode.scriptFile();
    537         if (this._enabled && !(scriptFile && scriptFile.hasDivergedFromVM()))
    538             this._setInDebugger();
    539     },
    540 
    541     /**
    542      * @param {boolean=} keepInStorage
    543      */
    544     remove: function(keepInStorage)
    545     {
    546         var removeFromStorage = !keepInStorage;
    547         this._resetLocations();
    548         this._removeFromDebugger();
    549         this._breakpointManager._removeBreakpoint(this, removeFromStorage);
    550     },
    551 
    552     _setInDebugger: function()
    553     {
    554         this._removeFromDebugger();
    555         var uiSourceCode = this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path);
    556         if (!uiSourceCode)
    557             return;
    558         var rawLocation = uiSourceCode.uiLocationToRawLocation(this._lineNumber, 0);
    559         var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
    560         if (debuggerModelLocation)
    561             this._breakpointManager._debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, this._condition, this._didSetBreakpointInDebugger.bind(this));
    562         else if (uiSourceCode.url)
    563             this._breakpointManager._debuggerModel.setBreakpointByURL(uiSourceCode.url, this._lineNumber, 0, this._condition, this._didSetBreakpointInDebugger.bind(this));
    564     },
    565 
    566     /**
    567     * @this {WebInspector.BreakpointManager.Breakpoint}
    568     * @param {?DebuggerAgent.BreakpointId} breakpointId
    569     * @param {!Array.<!WebInspector.DebuggerModel.Location>} locations
    570     */
    571     _didSetBreakpointInDebugger: function(breakpointId, locations)
    572     {
    573         if (!breakpointId) {
    574             this._resetLocations();
    575             this._breakpointManager._removeBreakpoint(this, false);
    576             return;
    577         }
    578 
    579         this._debuggerId = breakpointId;
    580         this._breakpointManager._breakpointForDebuggerId[breakpointId] = this;
    581 
    582         if (!locations.length) {
    583             this._fakeBreakpointAtPrimaryLocation();
    584             return;
    585         }
    586 
    587         this._resetLocations();
    588         for (var i = 0; i < locations.length; ++i) {
    589             var script = this._breakpointManager._debuggerModel.scriptForId(locations[i].scriptId);
    590             var uiLocation = script.rawLocationToUILocation(locations[i].lineNumber, locations[i].columnNumber);
    591             if (this._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber)) {
    592                 // location clash
    593                 this.remove();
    594                 return;
    595             }
    596         }
    597 
    598         for (var i = 0; i < locations.length; ++i)
    599             this._addResolvedLocation(locations[i]);
    600     },
    601 
    602     _removeFromDebugger: function()
    603     {
    604         if (!this._debuggerId)
    605             return;
    606         this._breakpointManager._debuggerModel.removeBreakpoint(this._debuggerId);
    607         delete this._breakpointManager._breakpointForDebuggerId[this._debuggerId];
    608         delete this._debuggerId;
    609     },
    610 
    611     _resetLocations: function()
    612     {
    613         for (var stringifiedLocation in this._uiLocations)
    614             this._breakpointManager._uiLocationRemoved(this, this._uiLocations[stringifiedLocation]);
    615 
    616         for (var i = 0; i < this._liveLocations.length; ++i)
    617             this._liveLocations[i].dispose();
    618         this._liveLocations = [];
    619 
    620         this._uiLocations = {};
    621     },
    622 
    623     /**
    624      * @return {string}
    625      */
    626     _breakpointStorageId: function()
    627     {
    628         return WebInspector.BreakpointManager._breakpointStorageId(this._sourceFileId, this._lineNumber);
    629     },
    630 
    631     _fakeBreakpointAtPrimaryLocation: function()
    632     {
    633         this._resetLocations();
    634         var uiSourceCode = this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path);
    635         if (!uiSourceCode)
    636             return;
    637         var uiLocation = new WebInspector.UILocation(uiSourceCode, this._lineNumber, 0);
    638         this._uiLocations[""] = uiLocation;
    639         this._breakpointManager._uiLocationAdded(this, uiLocation);
    640     }
    641 }
    642 
    643 /**
    644  * @constructor
    645  * @param {!WebInspector.BreakpointManager} breakpointManager
    646  * @param {!WebInspector.Setting} setting
    647  */
    648 WebInspector.BreakpointManager.Storage = function(breakpointManager, setting)
    649 {
    650     this._breakpointManager = breakpointManager;
    651     this._setting = setting;
    652     var breakpoints = this._setting.get();
    653     /** @type {!Object.<string, !WebInspector.BreakpointManager.Storage.Item>} */
    654     this._breakpoints = {};
    655     for (var i = 0; i < breakpoints.length; ++i) {
    656         var breakpoint = /** @type {!WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]);
    657         this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber] = breakpoint;
    658     }
    659 }
    660 
    661 WebInspector.BreakpointManager.Storage.prototype = {
    662     mute: function()
    663     {
    664         this._muted = true;
    665     },
    666 
    667     unmute: function()
    668     {
    669         delete this._muted;
    670     },
    671 
    672     /**
    673      * @param {!WebInspector.UISourceCode} uiSourceCode
    674      * @return {!Array.<!WebInspector.BreakpointManager.Storage.Item>}
    675      */
    676     breakpointItems: function(uiSourceCode)
    677     {
    678         var result = [];
    679         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    680         for (var id in this._breakpoints) {
    681             var breakpoint = this._breakpoints[id];
    682             if (breakpoint.sourceFileId === sourceFileId)
    683                 result.push(breakpoint);
    684         }
    685         return result;
    686     },
    687 
    688     /**
    689      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    690      */
    691     _updateBreakpoint: function(breakpoint)
    692     {
    693         if (this._muted || !breakpoint._breakpointStorageId())
    694             return;
    695         this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint);
    696         this._save();
    697     },
    698 
    699     /**
    700      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    701      */
    702     _removeBreakpoint: function(breakpoint)
    703     {
    704         if (this._muted)
    705             return;
    706         delete this._breakpoints[breakpoint._breakpointStorageId()];
    707         this._save();
    708     },
    709 
    710     _save: function()
    711     {
    712         var breakpointsArray = [];
    713         for (var id in this._breakpoints)
    714             breakpointsArray.push(this._breakpoints[id]);
    715         this._setting.set(breakpointsArray);
    716     }
    717 }
    718 
    719 /**
    720  * @constructor
    721  * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    722  */
    723 WebInspector.BreakpointManager.Storage.Item = function(breakpoint)
    724 {
    725     this.sourceFileId = breakpoint._sourceFileId;
    726     this.lineNumber = breakpoint.lineNumber();
    727     this.condition = breakpoint.condition();
    728     this.enabled = breakpoint.enabled();
    729 }
    730 
    731 /** @type {!WebInspector.BreakpointManager} */
    732 WebInspector.breakpointManager;
    733