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