Home | History | Annotate | Download | only in sdk
      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.Workspace} workspace
     36  * @param {!WebInspector.TargetManager} targetManager
     37  */
     38 WebInspector.BreakpointManager = function(breakpointStorage, workspace, targetManager)
     39 {
     40     this._storage = new WebInspector.BreakpointManager.Storage(this, breakpointStorage);
     41     this._workspace = workspace;
     42     this._targetManager = targetManager;
     43 
     44     this._breakpointsForUISourceCode = new Map();
     45     this._breakpointsForPrimaryUISourceCode = new Map();
     46     /** @type {!StringMultimap.<!WebInspector.BreakpointManager.Breakpoint>} */
     47     this._provisionalBreakpoints = new StringMultimap();
     48 
     49     this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
     50     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
     51     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, 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     return uiSourceCode.uri();
     64 }
     65 
     66 /**
     67  * @param {string} sourceFileId
     68  * @param {number} lineNumber
     69  * @param {number} columnNumber
     70  * @return {string}
     71  */
     72 WebInspector.BreakpointManager._breakpointStorageId = function(sourceFileId, lineNumber, columnNumber)
     73 {
     74     if (!sourceFileId)
     75         return "";
     76     return sourceFileId + ":" + lineNumber + ":" + columnNumber;
     77 }
     78 
     79 WebInspector.BreakpointManager.prototype = {
     80 
     81     /**
     82      * @param {string} sourceFileId
     83      * @return {!StringMap.<!WebInspector.BreakpointManager.Breakpoint>}
     84      */
     85     _provisionalBreakpointsForSourceFileId: function(sourceFileId)
     86     {
     87         var result = new StringMap();
     88         var breakpoints = this._provisionalBreakpoints.get(sourceFileId).values();
     89         for (var i = 0; i < breakpoints.length; ++i)
     90             result.put(breakpoints[i]._breakpointStorageId(), breakpoints[i]);
     91         return result;
     92     },
     93 
     94     removeProvisionalBreakpointsForTest: function()
     95     {
     96         var breakpoints = this._provisionalBreakpoints.values();
     97         for (var i = 0; i < breakpoints.length; ++i)
     98             breakpoints[i].remove();
     99         this._provisionalBreakpoints.clear();
    100     },
    101 
    102     /**
    103      * @param {!WebInspector.UISourceCode} uiSourceCode
    104      */
    105     _restoreBreakpoints: function(uiSourceCode)
    106     {
    107         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    108         if (!sourceFileId)
    109             return;
    110 
    111         this._storage.mute();
    112         var breakpointItems = this._storage.breakpointItems(uiSourceCode);
    113         var provisionalBreakpoints = this._provisionalBreakpointsForSourceFileId(sourceFileId);
    114         for (var i = 0; i < breakpointItems.length; ++i) {
    115             var breakpointItem = breakpointItems[i];
    116             var itemStorageId = WebInspector.BreakpointManager._breakpointStorageId(breakpointItem.sourceFileId, breakpointItem.lineNumber, breakpointItem.columnNumber);
    117             var provisionalBreakpoint = provisionalBreakpoints.get(itemStorageId);
    118             if (provisionalBreakpoint) {
    119                 if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
    120                     this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []);
    121                 this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(provisionalBreakpoint);
    122                 provisionalBreakpoint._updateBreakpoint();
    123             } else {
    124                 this._innerSetBreakpoint(uiSourceCode, breakpointItem.lineNumber, breakpointItem.columnNumber, breakpointItem.condition, breakpointItem.enabled);
    125             }
    126         }
    127         this._provisionalBreakpoints.removeAll(sourceFileId);
    128         this._storage.unmute();
    129     },
    130 
    131     /**
    132      * @param {!WebInspector.Event} event
    133      */
    134     _uiSourceCodeAdded: function(event)
    135     {
    136         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
    137         this._restoreBreakpoints(uiSourceCode);
    138         if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document)
    139             uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this);
    140     },
    141 
    142     /**
    143      * @param {!WebInspector.Event} event
    144      */
    145     _uiSourceCodeRemoved: function(event)
    146     {
    147         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
    148         this._removeUISourceCode(uiSourceCode);
    149     },
    150 
    151     /**
    152      * @param {!WebInspector.Event} event
    153      */
    154     _uiSourceCodeMappingChanged: function(event)
    155     {
    156         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
    157         var isIdentity = /** @type {boolean} */ (event.data.isIdentity);
    158         var target = /** @type {!WebInspector.Target} */ (event.data.target);
    159         if (isIdentity)
    160             return;
    161         var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [];
    162         for (var i = 0; i < breakpoints.length; ++i)
    163             breakpoints[i]._updateInDebuggerForTarget(target);
    164     },
    165 
    166     /**
    167      * @param {!WebInspector.UISourceCode} uiSourceCode
    168      */
    169     _removeUISourceCode: function(uiSourceCode)
    170     {
    171         var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [];
    172         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    173         for (var i = 0; i < breakpoints.length; ++i) {
    174             breakpoints[i]._resetLocations();
    175             if (breakpoints[i].enabled())
    176                 this._provisionalBreakpoints.put(sourceFileId, breakpoints[i]);
    177         }
    178         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this);
    179         this._breakpointsForPrimaryUISourceCode.remove(uiSourceCode);
    180     },
    181 
    182     /**
    183      * @param {!WebInspector.UISourceCode} uiSourceCode
    184      * @param {number} lineNumber
    185      * @param {number} columnNumber
    186      * @param {string} condition
    187      * @param {boolean} enabled
    188      * @return {!WebInspector.BreakpointManager.Breakpoint}
    189      */
    190     setBreakpoint: function(uiSourceCode, lineNumber, columnNumber, condition, enabled)
    191     {
    192         var targets = this._targetManager.targets();
    193         for (var i = 0; i < targets.length; ++i)
    194             targets[i].debuggerModel.setBreakpointsActive(true);
    195         return this._innerSetBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled);
    196     },
    197 
    198     /**
    199      * @param {!WebInspector.UISourceCode} uiSourceCode
    200      * @param {number} lineNumber
    201      * @param {number} columnNumber
    202      * @param {string} condition
    203      * @param {boolean} enabled
    204      * @return {!WebInspector.BreakpointManager.Breakpoint}
    205      */
    206     _innerSetBreakpoint: function(uiSourceCode, lineNumber, columnNumber, condition, enabled)
    207     {
    208         var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber, columnNumber);
    209         if (breakpoint) {
    210             breakpoint._updateState(condition, enabled);
    211             return breakpoint;
    212         }
    213         var projectId = uiSourceCode.project().id();
    214         var path = uiSourceCode.path();
    215         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    216         breakpoint = new WebInspector.BreakpointManager.Breakpoint(this, projectId, path, sourceFileId, lineNumber, columnNumber, condition, enabled);
    217         if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
    218             this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []);
    219         this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(breakpoint);
    220         return breakpoint;
    221     },
    222 
    223     /**
    224      * @param {!WebInspector.UISourceCode} uiSourceCode
    225      * @param {number} lineNumber
    226      * @param {number} columnNumber
    227      * @return {?WebInspector.BreakpointManager.Breakpoint}
    228      */
    229     findBreakpoint: function(uiSourceCode, lineNumber, columnNumber)
    230     {
    231         var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    232         var lineBreakpoints = breakpoints ? breakpoints.get(String(lineNumber)) : null;
    233         var columnBreakpoints = lineBreakpoints ? lineBreakpoints.get(String(columnNumber)) : null;
    234         return columnBreakpoints ? columnBreakpoints[0] : null;
    235     },
    236 
    237     /**
    238      * @param {!WebInspector.UISourceCode} uiSourceCode
    239      * @param {number} lineNumber
    240      * @return {?WebInspector.BreakpointManager.Breakpoint}
    241      */
    242     findBreakpointOnLine: function(uiSourceCode, lineNumber)
    243     {
    244         var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    245         var lineBreakpoints = breakpoints ? breakpoints.get(String(lineNumber)) : null;
    246         return lineBreakpoints ? lineBreakpoints.values()[0][0] : null;
    247     },
    248 
    249     /**
    250      * @param {!WebInspector.UISourceCode} uiSourceCode
    251      * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>}
    252      */
    253     breakpointsForUISourceCode: function(uiSourceCode)
    254     {
    255         var result = [];
    256         var uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    257         var breakpoints = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.values() : [];
    258         for (var i = 0; i < breakpoints.length; ++i) {
    259             var lineBreakpoints = breakpoints[i];
    260             var columnBreakpointArrays = lineBreakpoints ? lineBreakpoints.values() : [];
    261             result = result.concat.apply(result, columnBreakpointArrays);
    262         }
    263         return result;
    264     },
    265 
    266     /**
    267      * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>}
    268      */
    269     allBreakpoints: function()
    270     {
    271         var result = [];
    272         var uiSourceCodes = this._breakpointsForUISourceCode.keys();
    273         for (var i = 0; i < uiSourceCodes.length; ++i)
    274             result = result.concat(this.breakpointsForUISourceCode(uiSourceCodes[i]));
    275         return result;
    276     },
    277 
    278     /**
    279      * @param {!WebInspector.UISourceCode} uiSourceCode
    280      * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
    281      */
    282     breakpointLocationsForUISourceCode: function(uiSourceCode)
    283     {
    284         var uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
    285         var lineNumbers = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.keys() : [];
    286         var result = [];
    287         for (var i = 0; i < lineNumbers.length; ++i) {
    288             var lineBreakpoints = uiSourceCodeBreakpoints.get(lineNumbers[i]);
    289             var columnNumbers = lineBreakpoints.keys();
    290             for (var j = 0; j < columnNumbers.length; ++j) {
    291                 var columnBreakpoints = lineBreakpoints.get(columnNumbers[j]);
    292                 var lineNumber = parseInt(lineNumbers[i], 10);
    293                 var columnNumber = parseInt(columnNumbers[j], 10);
    294                 for (var k = 0; k < columnBreakpoints.length; ++k) {
    295                     var breakpoint = columnBreakpoints[k];
    296                     var uiLocation = uiSourceCode.uiLocation(lineNumber, columnNumber);
    297                     result.push({breakpoint: breakpoint, uiLocation: uiLocation});
    298                 }
    299             }
    300         }
    301         return result;
    302     },
    303 
    304     /**
    305      * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>}
    306      */
    307     allBreakpointLocations: function()
    308     {
    309         var result = [];
    310         var uiSourceCodes = this._breakpointsForUISourceCode.keys();
    311         for (var i = 0; i < uiSourceCodes.length; ++i)
    312             result = result.concat(this.breakpointLocationsForUISourceCode(uiSourceCodes[i]));
    313         return result;
    314     },
    315 
    316     /**
    317      * @param {boolean} toggleState
    318      */
    319     toggleAllBreakpoints: function(toggleState)
    320     {
    321         var breakpoints = this.allBreakpoints();
    322         for (var i = 0; i < breakpoints.length; ++i)
    323             breakpoints[i].setEnabled(toggleState);
    324     },
    325 
    326     removeAllBreakpoints: function()
    327     {
    328         var breakpoints = this.allBreakpoints();
    329         for (var i = 0; i < breakpoints.length; ++i)
    330             breakpoints[i].remove();
    331     },
    332 
    333     _projectRemoved: function(event)
    334     {
    335         var project = /** @type {!WebInspector.Project} */ (event.data);
    336         var uiSourceCodes = project.uiSourceCodes();
    337         for (var i = 0; i < uiSourceCodes.length; ++i)
    338             this._removeUISourceCode(uiSourceCodes[i]);
    339     },
    340 
    341     /**
    342      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    343      * @param {boolean} removeFromStorage
    344      */
    345     _removeBreakpoint: function(breakpoint, removeFromStorage)
    346     {
    347         var uiSourceCode = breakpoint.uiSourceCode();
    348         var breakpoints = uiSourceCode ? this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [] : [];
    349         breakpoints.remove(breakpoint);
    350         if (removeFromStorage)
    351             this._storage._removeBreakpoint(breakpoint);
    352         this._provisionalBreakpoints.remove(breakpoint._sourceFileId, breakpoint);
    353     },
    354 
    355     /**
    356      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    357      * @param {!WebInspector.UILocation} uiLocation
    358      */
    359     _uiLocationAdded: function(breakpoint, uiLocation)
    360     {
    361         var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
    362         if (!breakpoints) {
    363             breakpoints = new StringMap();
    364             this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints);
    365         }
    366         var lineBreakpoints = breakpoints.get(String(uiLocation.lineNumber));
    367         if (!lineBreakpoints) {
    368             lineBreakpoints = new StringMap();
    369             breakpoints.put(String(uiLocation.lineNumber), lineBreakpoints);
    370         }
    371         var columnBreakpoints = lineBreakpoints.get(String(uiLocation.columnNumber));
    372         if (!columnBreakpoints) {
    373             columnBreakpoints = [];
    374             lineBreakpoints.put(String(uiLocation.columnNumber), columnBreakpoints);
    375         }
    376         columnBreakpoints.push(breakpoint);
    377         this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
    378     },
    379 
    380     /**
    381      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    382      * @param {!WebInspector.UILocation} uiLocation
    383      */
    384     _uiLocationRemoved: function(breakpoint, uiLocation)
    385     {
    386         var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
    387         if (!breakpoints)
    388             return;
    389 
    390         var lineBreakpoints = breakpoints.get(String(uiLocation.lineNumber));
    391         if (!lineBreakpoints)
    392             return;
    393         var columnBreakpoints = lineBreakpoints.get(String(uiLocation.columnNumber));
    394         if (!columnBreakpoints)
    395             return;
    396         columnBreakpoints.remove(breakpoint);
    397         if (!columnBreakpoints.length)
    398             lineBreakpoints.remove(String(uiLocation.columnNumber));
    399         if (!lineBreakpoints.size())
    400             breakpoints.remove(String(uiLocation.lineNumber));
    401         if (!breakpoints.size())
    402             this._breakpointsForUISourceCode.remove(uiLocation.uiSourceCode);
    403         this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
    404     },
    405 
    406     __proto__: WebInspector.Object.prototype
    407 }
    408 
    409 /**
    410  * @constructor
    411  * @implements {WebInspector.TargetManager.Observer}
    412  * @param {!WebInspector.BreakpointManager} breakpointManager
    413  * @param {string} projectId
    414  * @param {string} path
    415  * @param {string} sourceFileId
    416  * @param {number} lineNumber
    417  * @param {number} columnNumber
    418  * @param {string} condition
    419  * @param {boolean} enabled
    420  */
    421 WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, projectId, path, sourceFileId, lineNumber, columnNumber, condition, enabled)
    422 {
    423     this._breakpointManager = breakpointManager;
    424     this._projectId = projectId;
    425     this._path = path;
    426     this._lineNumber = lineNumber;
    427     this._columnNumber = columnNumber;
    428     this._sourceFileId = sourceFileId;
    429 
    430     /** @type {!Object.<string, number>} */
    431     this._numberOfDebuggerLocationForUILocation = {};
    432 
    433     // Force breakpoint update.
    434     /** @type {string} */ this._condition;
    435     /** @type {boolean} */ this._enabled;
    436     /** @type {boolean} */ this._isRemoved;
    437     /** @type {!WebInspector.UILocation|undefined} */ this._fakePrimaryLocation;
    438 
    439     /** @type {!Map.<!WebInspector.Target, !WebInspector.BreakpointManager.TargetBreakpoint>}*/
    440     this._targetBreakpoints = new Map();
    441     this._updateState(condition, enabled);
    442     this._breakpointManager._targetManager.observeTargets(this);
    443 }
    444 
    445 WebInspector.BreakpointManager.Breakpoint.prototype = {
    446     /**
    447      * @param {!WebInspector.Target} target
    448      */
    449     targetAdded: function(target)
    450     {
    451         this._targetBreakpoints.put(target, new WebInspector.BreakpointManager.TargetBreakpoint(target, this));
    452     },
    453 
    454     /**
    455      * @param {!WebInspector.Target} target
    456      */
    457     targetRemoved: function(target)
    458     {
    459         var targetBreakpoint = this._targetBreakpoints.remove(target);
    460         targetBreakpoint._cleanUpAfterDebuggerIsGone();
    461         targetBreakpoint._removeEventListeners();
    462     },
    463 
    464     /**
    465      * @return {string}
    466      */
    467     projectId: function()
    468     {
    469         return this._projectId;
    470     },
    471 
    472     /**
    473      * @return {string}
    474      */
    475     path: function()
    476     {
    477         return this._path;
    478     },
    479 
    480     /**
    481      * @return {number}
    482      */
    483     lineNumber: function()
    484     {
    485         return this._lineNumber;
    486     },
    487 
    488     /**
    489      * @return {number}
    490      */
    491     columnNumber: function()
    492     {
    493         return this._columnNumber;
    494     },
    495 
    496     /**
    497      * @return {?WebInspector.UISourceCode}
    498      */
    499     uiSourceCode: function()
    500     {
    501         return this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path);
    502     },
    503 
    504     /**
    505      * @param {?WebInspector.UILocation} oldUILocation
    506      * @param {!WebInspector.UILocation} newUILocation
    507      */
    508     _replaceUILocation: function(oldUILocation, newUILocation)
    509     {
    510         if (this._isRemoved)
    511             return;
    512 
    513         this._removeUILocation(oldUILocation, true);
    514         this._removeFakeBreakpointAtPrimaryLocation();
    515 
    516         if (!this._numberOfDebuggerLocationForUILocation[newUILocation.id()])
    517             this._numberOfDebuggerLocationForUILocation[newUILocation.id()] = 0;
    518 
    519         if (++this._numberOfDebuggerLocationForUILocation[newUILocation.id()] === 1)
    520             this._breakpointManager._uiLocationAdded(this, newUILocation);
    521     },
    522 
    523     /**
    524      * @param {?WebInspector.UILocation} uiLocation
    525      * @param {boolean=} muteCreationFakeBreakpoint
    526      */
    527     _removeUILocation: function(uiLocation, muteCreationFakeBreakpoint)
    528     {
    529         if (!uiLocation || --this._numberOfDebuggerLocationForUILocation[uiLocation.id()] !== 0)
    530             return;
    531 
    532         delete this._numberOfDebuggerLocationForUILocation[uiLocation.id()];
    533         this._breakpointManager._uiLocationRemoved(this, uiLocation);
    534         if (!muteCreationFakeBreakpoint)
    535             this._fakeBreakpointAtPrimaryLocation();
    536     },
    537 
    538     /**
    539      * @return {boolean}
    540      */
    541     enabled: function()
    542     {
    543         return this._enabled;
    544     },
    545 
    546     /**
    547      * @param {boolean} enabled
    548      */
    549     setEnabled: function(enabled)
    550     {
    551         this._updateState(this._condition, enabled);
    552     },
    553 
    554     /**
    555      * @return {string}
    556      */
    557     condition: function()
    558     {
    559         return this._condition;
    560     },
    561 
    562     /**
    563      * @param {string} condition
    564      */
    565     setCondition: function(condition)
    566     {
    567         this._updateState(condition, this._enabled);
    568     },
    569 
    570     /**
    571      * @param {string} condition
    572      * @param {boolean} enabled
    573      */
    574     _updateState: function(condition, enabled)
    575     {
    576         if (this._enabled === enabled && this._condition === condition)
    577             return;
    578         this._enabled = enabled;
    579         this._condition = condition;
    580         this._breakpointManager._storage._updateBreakpoint(this);
    581         this._updateBreakpoint();
    582     },
    583 
    584     _updateBreakpoint: function()
    585     {
    586         this._removeFakeBreakpointAtPrimaryLocation();
    587         this._fakeBreakpointAtPrimaryLocation();
    588         var targetBreakpoints = this._targetBreakpoints.values();
    589         for (var i = 0; i < targetBreakpoints.length; ++i)
    590             targetBreakpoints[i]._updateInDebugger();
    591     },
    592 
    593     /**
    594      * @param {boolean=} keepInStorage
    595      */
    596     remove: function(keepInStorage)
    597     {
    598         this._isRemoved = true;
    599         var removeFromStorage = !keepInStorage;
    600         this._removeFakeBreakpointAtPrimaryLocation();
    601         var targetBreakpoints = this._targetBreakpoints.values();
    602         for (var i = 0; i < targetBreakpoints.length; ++i) {
    603             targetBreakpoints[i]._removeFromDebugger();
    604             targetBreakpoints[i]._removeEventListeners();
    605         }
    606 
    607         this._breakpointManager._removeBreakpoint(this, removeFromStorage);
    608         this._breakpointManager._targetManager.unobserveTargets(this);
    609     },
    610 
    611     /**
    612      * @param {!WebInspector.Target} target
    613      */
    614     _updateInDebuggerForTarget: function(target)
    615     {
    616         this._targetBreakpoints.get(target)._updateInDebugger();
    617     },
    618 
    619     /**
    620      * @return {string}
    621      */
    622     _breakpointStorageId: function()
    623     {
    624         return WebInspector.BreakpointManager._breakpointStorageId(this._sourceFileId, this._lineNumber, this._columnNumber);
    625     },
    626 
    627     _fakeBreakpointAtPrimaryLocation: function()
    628     {
    629         if (this._isRemoved || !Object.isEmpty(this._numberOfDebuggerLocationForUILocation) || this._fakePrimaryLocation)
    630             return;
    631 
    632         var uiSourceCode = this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path);
    633         if (!uiSourceCode)
    634             return;
    635 
    636         this._fakePrimaryLocation = uiSourceCode.uiLocation(this._lineNumber, this._columnNumber);
    637         this._breakpointManager._uiLocationAdded(this, this._fakePrimaryLocation);
    638     },
    639 
    640     _removeFakeBreakpointAtPrimaryLocation: function()
    641     {
    642         if (this._fakePrimaryLocation) {
    643             this._breakpointManager._uiLocationRemoved(this, this._fakePrimaryLocation);
    644             delete this._fakePrimaryLocation;
    645         }
    646     },
    647 
    648     _resetLocations: function()
    649     {
    650         this._removeFakeBreakpointAtPrimaryLocation();
    651         var targetBreakpoints = this._targetBreakpoints.values();
    652         for (var i = 0; i < targetBreakpoints.length; ++i)
    653             targetBreakpoints[i]._resetLocations();
    654     }
    655 }
    656 
    657 /**
    658  * @constructor
    659  * @extends {WebInspector.TargetAware}
    660  * @param {!WebInspector.Target} target
    661  * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    662  */
    663 WebInspector.BreakpointManager.TargetBreakpoint = function(target, breakpoint)
    664 {
    665     WebInspector.TargetAware.call(this, target);
    666     this._breakpoint = breakpoint;
    667     /** @type {!Array.<!WebInspector.Script.Location>} */
    668     this._liveLocations = [];
    669 
    670     /** @type {!Object.<string, !WebInspector.UILocation>} */
    671     this._uiLocations = {};
    672     target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this);
    673     target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._updateInDebugger, this);
    674     if (target.debuggerModel.debuggerEnabled())
    675         this._updateInDebugger();
    676 }
    677 
    678 WebInspector.BreakpointManager.TargetBreakpoint.prototype = {
    679 
    680     _resetLocations: function()
    681     {
    682         var uiLocations = Object.values(this._uiLocations);
    683         for (var i = 0; i < uiLocations.length; ++i)
    684             this._breakpoint._removeUILocation(uiLocations[i]);
    685 
    686         this._uiLocations = {};
    687 
    688         for (var i = 0; i < this._liveLocations.length; ++i)
    689             this._liveLocations[i].dispose();
    690         this._liveLocations = [];
    691     },
    692 
    693     /**
    694      * @param {boolean=} callbackImmediately
    695      */
    696     _removeFromDebugger: function(callbackImmediately)
    697     {
    698         this._resetLocations();
    699         if (!this._debuggerId)
    700             return;
    701         var debuggerId = this._debuggerId;
    702         this.target().debuggerModel.removeBreakpoint(this._debuggerId, callbackImmediately ? undefined : didRemoveFromDebugger.bind(this));
    703 
    704         /**
    705          * @this {WebInspector.BreakpointManager.TargetBreakpoint}
    706          */
    707         function didRemoveFromDebugger()
    708         {
    709             if (this._debuggerId === debuggerId)
    710                 this._didRemoveFromDebugger();
    711         }
    712         if (callbackImmediately)
    713             this._didRemoveFromDebugger();
    714     },
    715 
    716     _updateInDebugger: function()
    717     {
    718         this._removeFromDebugger();
    719         var uiSourceCode = this._breakpoint.uiSourceCode();
    720         if (!uiSourceCode || !this._breakpoint._enabled)
    721             return;
    722         var scriptFile = uiSourceCode.scriptFileForTarget(this._target);
    723         if (scriptFile && scriptFile.hasDivergedFromVM())
    724             return;
    725 
    726         var lineNumber = this._breakpoint._lineNumber;
    727         var columnNumber = this._breakpoint._columnNumber;
    728         var rawLocation = uiSourceCode.uiLocationToRawLocation(this._target, lineNumber, columnNumber);
    729         var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
    730         var condition = this._breakpoint.condition();
    731         if (debuggerModelLocation)
    732             this.target().debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, condition, this._didSetBreakpointInDebugger.bind(this));
    733         else if (uiSourceCode.url)
    734             this.target().debuggerModel.setBreakpointByURL(uiSourceCode.url, lineNumber, columnNumber, condition, this._didSetBreakpointInDebugger.bind(this));
    735     },
    736 
    737     /**
    738      * @param {?DebuggerAgent.BreakpointId} breakpointId
    739      * @param {!Array.<!WebInspector.DebuggerModel.Location>} locations
    740      */
    741     _didSetBreakpointInDebugger: function(breakpointId, locations)
    742     {
    743         if (!breakpointId) {
    744             this._breakpoint.remove(true);
    745             return;
    746         }
    747 
    748         if (this._debuggerId)
    749             this._removeFromDebugger(true);
    750 
    751         this._debuggerId = breakpointId;
    752         this.target().debuggerModel.addBreakpointListener(this._debuggerId, this._breakpointResolved, this);
    753         for (var i = 0; i < locations.length; ++i)
    754             if (!this._addResolvedLocation(locations[i]))
    755                 return;
    756     },
    757 
    758     _didRemoveFromDebugger: function()
    759     {
    760         this.target().debuggerModel.removeBreakpointListener(this._debuggerId, this._breakpointResolved, this);
    761         delete this._debuggerId;
    762     },
    763 
    764     /**
    765      * @param {!WebInspector.Event} event
    766      */
    767     _breakpointResolved: function(event)
    768     {
    769         this._addResolvedLocation(/** @type {!WebInspector.DebuggerModel.Location}*/ (event.data));
    770     },
    771 
    772     /**
    773      * @param {!WebInspector.DebuggerModel.Location} location
    774      * @param {!WebInspector.UILocation} uiLocation
    775      */
    776     _locationUpdated: function(location, uiLocation)
    777     {
    778         var oldUILocation = this._uiLocations[location.id()] || null;
    779         this._uiLocations[location.id()] = uiLocation;
    780         this._breakpoint._replaceUILocation(oldUILocation, uiLocation);
    781     },
    782 
    783     /**
    784      * @param {!WebInspector.DebuggerModel.Location} location
    785      * @return {boolean}
    786      */
    787     _addResolvedLocation: function(location)
    788     {
    789         var script = location.script();
    790         var uiLocation = script.rawLocationToUILocation(location.lineNumber, location.columnNumber);
    791         var breakpoint = this._breakpoint._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
    792         if (breakpoint && breakpoint != this._breakpoint) {
    793             // location clash
    794             this._breakpoint.remove();
    795             return false;
    796         }
    797         this._liveLocations.push(location.createLiveLocation(this._locationUpdated.bind(this, location)));
    798         return true;
    799     },
    800 
    801     _cleanUpAfterDebuggerIsGone: function()
    802     {
    803         this._resetLocations();
    804         if (this._debuggerId)
    805             this._didRemoveFromDebugger();
    806     },
    807 
    808     _removeEventListeners: function()
    809     {
    810         this.target().debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this);
    811         this.target().debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._updateInDebugger, this);
    812     },
    813 
    814     __proto__: WebInspector.TargetAware.prototype
    815 }
    816 
    817 /**
    818  * @constructor
    819  * @param {!WebInspector.BreakpointManager} breakpointManager
    820  * @param {!WebInspector.Setting} setting
    821  */
    822 WebInspector.BreakpointManager.Storage = function(breakpointManager, setting)
    823 {
    824     this._breakpointManager = breakpointManager;
    825     this._setting = setting;
    826     var breakpoints = this._setting.get();
    827     /** @type {!Object.<string, !WebInspector.BreakpointManager.Storage.Item>} */
    828     this._breakpoints = {};
    829     for (var i = 0; i < breakpoints.length; ++i) {
    830         var breakpoint = /** @type {!WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]);
    831         breakpoint.columnNumber = breakpoint.columnNumber || 0;
    832         this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber + ":" + breakpoint.columnNumber] = breakpoint;
    833     }
    834 }
    835 
    836 WebInspector.BreakpointManager.Storage.prototype = {
    837     mute: function()
    838     {
    839         this._muted = true;
    840     },
    841 
    842     unmute: function()
    843     {
    844         delete this._muted;
    845     },
    846 
    847     /**
    848      * @param {!WebInspector.UISourceCode} uiSourceCode
    849      * @return {!Array.<!WebInspector.BreakpointManager.Storage.Item>}
    850      */
    851     breakpointItems: function(uiSourceCode)
    852     {
    853         var result = [];
    854         var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode);
    855         for (var id in this._breakpoints) {
    856             var breakpoint = this._breakpoints[id];
    857             if (breakpoint.sourceFileId === sourceFileId)
    858                 result.push(breakpoint);
    859         }
    860         return result;
    861     },
    862 
    863     /**
    864      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    865      */
    866     _updateBreakpoint: function(breakpoint)
    867     {
    868         if (this._muted || !breakpoint._breakpointStorageId())
    869             return;
    870         this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint);
    871         this._save();
    872     },
    873 
    874     /**
    875      * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    876      */
    877     _removeBreakpoint: function(breakpoint)
    878     {
    879         if (this._muted)
    880             return;
    881         delete this._breakpoints[breakpoint._breakpointStorageId()];
    882         this._save();
    883     },
    884 
    885     _save: function()
    886     {
    887         var breakpointsArray = [];
    888         for (var id in this._breakpoints)
    889             breakpointsArray.push(this._breakpoints[id]);
    890         this._setting.set(breakpointsArray);
    891     }
    892 }
    893 
    894 /**
    895  * @constructor
    896  * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint
    897  */
    898 WebInspector.BreakpointManager.Storage.Item = function(breakpoint)
    899 {
    900     this.sourceFileId = breakpoint._sourceFileId;
    901     this.lineNumber = breakpoint.lineNumber();
    902     this.columnNumber = breakpoint.columnNumber();
    903     this.condition = breakpoint.condition();
    904     this.enabled = breakpoint.enabled();
    905 }
    906 
    907 /** @type {!WebInspector.BreakpointManager} */
    908 WebInspector.breakpointManager;
    909