• 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.Workspace} workspace
36 * @param {!WebInspector.TargetManager} targetManager
37 */
38WebInspector.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
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    return uiSourceCode.uri();
64}
65
66/**
67 * @param {string} sourceFileId
68 * @param {number} lineNumber
69 * @param {number} columnNumber
70 * @return {string}
71 */
72WebInspector.BreakpointManager._breakpointStorageId = function(sourceFileId, lineNumber, columnNumber)
73{
74    if (!sourceFileId)
75        return "";
76    return sourceFileId + ":" + lineNumber + ":" + columnNumber;
77}
78
79WebInspector.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 */
421WebInspector.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
445WebInspector.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 */
663WebInspector.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
678WebInspector.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 */
822WebInspector.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
836WebInspector.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 */
898WebInspector.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} */
908WebInspector.breakpointManager;
909