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