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 */
38WebInspector.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.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
52}
53
54WebInspector.BreakpointManager.Events = {
55    BreakpointAdded: "breakpoint-added",
56    BreakpointRemoved: "breakpoint-removed"
57}
58
59WebInspector.BreakpointManager.sourceFileId = function(uiSourceCode)
60{
61    if (!uiSourceCode.url)
62        return "";
63    var deobfuscatedPrefix = uiSourceCode.formatted() ? "deobfuscated:" : "";
64    return deobfuscatedPrefix + uiSourceCode.uri();
65}
66
67WebInspector.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                delete breakpoint._primaryUILocation;
302            }
303
304            var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
305            delete this._sourceFilesWithRestoredBreakpoints[sourceFileId];
306        }
307    },
308
309    _breakpointResolved: function(event)
310    {
311        var breakpointId = /** @type {DebuggerAgent.BreakpointId} */ (event.data.breakpointId);
312        var location = /** @type {WebInspector.DebuggerModel.Location} */ (event.data.location);
313        var breakpoint = this._breakpointForDebuggerId[breakpointId];
314        if (!breakpoint)
315            return;
316        if (!this._breakpoints.contains(breakpoint))
317            this._breakpoints.put(breakpoint);
318        breakpoint._addResolvedLocation(location);
319    },
320
321    /**
322     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
323     * @param {boolean} removeFromStorage
324     */
325    _removeBreakpoint: function(breakpoint, removeFromStorage)
326    {
327        console.assert(!breakpoint._debuggerId)
328        this._breakpoints.remove(breakpoint);
329        if (removeFromStorage)
330            this._storage._removeBreakpoint(breakpoint);
331    },
332
333    /**
334     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
335     * @param {WebInspector.UILocation} uiLocation
336     */
337    _uiLocationAdded: function(breakpoint, uiLocation)
338    {
339        var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
340        if (!breakpoints) {
341            breakpoints = {};
342            this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints);
343        }
344
345        var lineBreakpoints = breakpoints[uiLocation.lineNumber];
346        if (!lineBreakpoints) {
347            lineBreakpoints = [];
348            breakpoints[uiLocation.lineNumber] = lineBreakpoints;
349        }
350
351        lineBreakpoints.push(breakpoint);
352        this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
353    },
354
355    /**
356     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
357     * @param {WebInspector.UILocation} uiLocation
358     */
359    _uiLocationRemoved: function(breakpoint, uiLocation)
360    {
361      var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
362        if (!breakpoints)
363            return;
364
365        var lineBreakpoints = breakpoints[uiLocation.lineNumber];
366        if (!lineBreakpoints)
367            return;
368
369        lineBreakpoints.remove(breakpoint);
370        if (!lineBreakpoints.length)
371            delete breakpoints[uiLocation.lineNumber];
372        this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
373    },
374
375    __proto__: WebInspector.Object.prototype
376}
377
378/**
379 * @constructor
380 * @param {WebInspector.BreakpointManager} breakpointManager
381 * @param {WebInspector.UISourceCode} uiSourceCode
382 * @param {number} lineNumber
383 * @param {string} condition
384 * @param {boolean} enabled
385 */
386WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, uiSourceCode, lineNumber, condition, enabled)
387{
388    this._breakpointManager = breakpointManager;
389    this._primaryUILocation = new WebInspector.UILocation(uiSourceCode, lineNumber, 0);
390    this._sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
391    /** @type {Array.<WebInspector.Script.Location>} */
392    this._liveLocations = [];
393    /** @type {Object.<string, WebInspector.UILocation>} */
394    this._uiLocations = {};
395
396    // Force breakpoint update.
397    /** @type {string} */ this._condition;
398    /** @type {boolean} */ this._enabled;
399    this._updateBreakpoint(condition, enabled);
400}
401
402WebInspector.BreakpointManager.Breakpoint.prototype = {
403    /**
404     * @return {WebInspector.UILocation}
405     */
406    primaryUILocation: function()
407    {
408        return this._primaryUILocation;
409    },
410
411    /**
412     * @param {WebInspector.DebuggerModel.Location} location
413     */
414    _addResolvedLocation: function(location)
415    {
416        this._liveLocations.push(this._breakpointManager._debuggerModel.createLiveLocation(location, this._locationUpdated.bind(this, location)));
417    },
418
419    /**
420     * @param {WebInspector.DebuggerModel.Location} location
421     * @param {WebInspector.UILocation} uiLocation
422     */
423    _locationUpdated: function(location, uiLocation)
424    {
425        var stringifiedLocation = location.scriptId + ":" + location.lineNumber + ":" + location.columnNumber;
426        var oldUILocation = /** @type {WebInspector.UILocation} */ (this._uiLocations[stringifiedLocation]);
427        if (oldUILocation)
428            this._breakpointManager._uiLocationRemoved(this, oldUILocation);
429        if (this._uiLocations[""]) {
430            delete this._uiLocations[""];
431            this._breakpointManager._uiLocationRemoved(this, this._primaryUILocation);
432        }
433        this._uiLocations[stringifiedLocation] = uiLocation;
434        this._breakpointManager._uiLocationAdded(this, uiLocation);
435    },
436
437    /**
438     * @return {boolean}
439     */
440    enabled: function()
441    {
442        return this._enabled;
443    },
444
445    /**
446     * @param {boolean} enabled
447     */
448    setEnabled: function(enabled)
449    {
450        this._updateBreakpoint(this._condition, enabled);
451    },
452
453    /**
454     * @return {string}
455     */
456    condition: function()
457    {
458        return this._condition;
459    },
460
461    /**
462     * @param {string} condition
463     */
464    setCondition: function(condition)
465    {
466        this._updateBreakpoint(condition, this._enabled);
467    },
468
469    /**
470     * @param {string} condition
471     * @param {boolean} enabled
472     */
473    _updateBreakpoint: function(condition, enabled)
474    {
475        if (this._enabled === enabled && this._condition === condition)
476            return;
477
478        if (this._enabled)
479            this._removeFromDebugger();
480
481        this._enabled = enabled;
482        this._condition = condition;
483        this._breakpointManager._storage._updateBreakpoint(this);
484
485        var scriptFile = this._primaryUILocation.uiSourceCode.scriptFile();
486        if (this._enabled && !(scriptFile && scriptFile.hasDivergedFromVM())) {
487            this._setInDebugger();
488            return;
489        }
490
491        this._fakeBreakpointAtPrimaryLocation();
492    },
493
494    /**
495     * @param {boolean=} keepInStorage
496     */
497    remove: function(keepInStorage)
498    {
499        var removeFromStorage = !keepInStorage;
500        this._resetLocations();
501        this._removeFromDebugger();
502        this._breakpointManager._removeBreakpoint(this, removeFromStorage);
503    },
504
505    _setInDebugger: function()
506    {
507        console.assert(!this._debuggerId);
508        var rawLocation = this._primaryUILocation.uiLocationToRawLocation();
509        var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
510        if (debuggerModelLocation)
511            this._breakpointManager._debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, this._condition, didSetBreakpoint.bind(this));
512        else
513            this._breakpointManager._debuggerModel.setBreakpointByURL(this._primaryUILocation.uiSourceCode.url, this._primaryUILocation.lineNumber, 0, this._condition, didSetBreakpoint.bind(this));
514
515        /**
516         * @this {WebInspector.BreakpointManager.Breakpoint}
517         * @param {?DebuggerAgent.BreakpointId} breakpointId
518         * @param {Array.<WebInspector.DebuggerModel.Location>} locations
519         */
520        function didSetBreakpoint(breakpointId, locations)
521        {
522            if (!breakpointId) {
523                this._resetLocations();
524                this._breakpointManager._removeBreakpoint(this, false);
525                return;
526            }
527
528            this._debuggerId = breakpointId;
529            this._breakpointManager._breakpointForDebuggerId[breakpointId] = this;
530
531            if (!locations.length) {
532                this._fakeBreakpointAtPrimaryLocation();
533                return;
534            }
535
536            this._resetLocations();
537            for (var i = 0; i < locations.length; ++i) {
538                var script = this._breakpointManager._debuggerModel.scriptForId(locations[i].scriptId);
539                var uiLocation = script.rawLocationToUILocation(locations[i].lineNumber, locations[i].columnNumber);
540                if (this._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber)) {
541                    // location clash
542                    this.remove();
543                    return;
544                }
545            }
546
547            for (var i = 0; i < locations.length; ++i)
548                this._addResolvedLocation(locations[i]);
549        }
550    },
551
552    _removeFromDebugger: function()
553    {
554        if (this._debuggerId) {
555            this._breakpointManager._debuggerModel.removeBreakpoint(this._debuggerId);
556            delete this._breakpointManager._breakpointForDebuggerId[this._debuggerId];
557            delete this._debuggerId;
558        }
559    },
560
561    _resetLocations: function()
562    {
563        for (var stringifiedLocation in this._uiLocations)
564            this._breakpointManager._uiLocationRemoved(this, this._uiLocations[stringifiedLocation]);
565
566        for (var i = 0; i < this._liveLocations.length; ++i)
567            this._liveLocations[i].dispose();
568        this._liveLocations = [];
569
570        this._uiLocations = {};
571    },
572
573    /**
574     * @return {string}
575     */
576    _breakpointStorageId: function()
577    {
578        if (!this._sourceFileId)
579            return "";
580        return this._sourceFileId + ":" + this._primaryUILocation.lineNumber;
581    },
582
583    _fakeBreakpointAtPrimaryLocation: function()
584    {
585        this._resetLocations();
586        this._uiLocations[""] = this._primaryUILocation;
587        this._breakpointManager._uiLocationAdded(this, this._primaryUILocation);
588    }
589}
590
591/**
592 * @constructor
593 * @param {WebInspector.BreakpointManager} breakpointManager
594 * @param {WebInspector.Setting} setting
595 */
596WebInspector.BreakpointManager.Storage = function(breakpointManager, setting)
597{
598    this._breakpointManager = breakpointManager;
599    this._setting = setting;
600    var breakpoints = this._setting.get();
601    /** @type {Object.<string,WebInspector.BreakpointManager.Storage.Item>} */
602    this._breakpoints = {};
603    for (var i = 0; i < breakpoints.length; ++i) {
604        var breakpoint = /** @type {WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]);
605        this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber] = breakpoint;
606    }
607}
608
609WebInspector.BreakpointManager.Storage.prototype = {
610    /**
611     * @param {WebInspector.UISourceCode} uiSourceCode
612     */
613    _restoreBreakpoints: function(uiSourceCode)
614    {
615        this._muted = true;
616        var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
617        for (var id in this._breakpoints) {
618            var breakpoint = this._breakpoints[id];
619            if (breakpoint.sourceFileId === sourceFileId)
620                this._breakpointManager._innerSetBreakpoint(uiSourceCode, breakpoint.lineNumber, breakpoint.condition, breakpoint.enabled);
621        }
622        delete this._muted;
623    },
624
625    /**
626     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
627     */
628    _updateBreakpoint: function(breakpoint)
629    {
630        if (this._muted || !breakpoint._breakpointStorageId())
631            return;
632        this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint);
633        this._save();
634    },
635
636    /**
637     * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
638     */
639    _removeBreakpoint: function(breakpoint)
640    {
641        if (this._muted)
642            return;
643        delete this._breakpoints[breakpoint._breakpointStorageId()];
644        this._save();
645    },
646
647    _save: function()
648    {
649        var breakpointsArray = [];
650        for (var id in this._breakpoints)
651            breakpointsArray.push(this._breakpoints[id]);
652        this._setting.set(breakpointsArray);
653    }
654}
655
656/**
657 * @constructor
658 * @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
659 */
660WebInspector.BreakpointManager.Storage.Item = function(breakpoint)
661{
662    var primaryUILocation = breakpoint.primaryUILocation();
663    this.sourceFileId = breakpoint._sourceFileId;
664    this.lineNumber = primaryUILocation.lineNumber;
665    this.condition = breakpoint.condition();
666    this.enabled = breakpoint.enabled();
667}
668
669/** @type {WebInspector.BreakpointManager} */
670WebInspector.breakpointManager = null;
671