2
* Copyright (C) 2011 Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are
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
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.
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.
33
* @extends {WebInspector.Object}
34
* @param {WebInspector.Setting} breakpointStorage
35
* @param {WebInspector.DebuggerModel} debuggerModel
36
* @param {WebInspector.Workspace} workspace
38
WebInspector.BreakpointManager = function(breakpointStorage, debuggerModel, workspace)
40
this._storage = new WebInspector.BreakpointManager.Storage(this, breakpointStorage);
41
this._debuggerModel = debuggerModel;
42
this._workspace = workspace;
44
this._breakpoints = [];
45
this._breakpointForDebuggerId = {};
46
this._breakpointsForUISourceCode = new Map();
47
this._sourceFilesWithRestoredBreakpoints = {};
49
this._debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
50
this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._workspaceReset, this);
51
this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
52
this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeAdded, this._uiSourceCodeAdded, this);
53
this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeRemoved, this._uiSourceCodeRemoved, this);
56
WebInspector.BreakpointManager.Events = {
57
BreakpointAdded: "breakpoint-added",
58
BreakpointRemoved: "breakpoint-removed"
61
WebInspector.BreakpointManager.sourceFileId = function(uiSourceCode)
63
return uiSourceCode.formatted() ? "deobfuscated:" + uiSourceCode.url : uiSourceCode.url;
66
WebInspector.BreakpointManager.prototype = {
68
* @param {WebInspector.UISourceCode} uiSourceCode
70
_restoreBreakpoints: function(uiSourceCode)
72
var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
73
if (!sourceFileId || this._sourceFilesWithRestoredBreakpoints[sourceFileId])
75
this._sourceFilesWithRestoredBreakpoints[sourceFileId] = true;
77
// Erase provisional breakpoints prior to restoring them.
78
for (var debuggerId in this._breakpointForDebuggerId) {
79
var breakpoint = this._breakpointForDebuggerId[debuggerId];
80
if (breakpoint._sourceFileId !== sourceFileId)
82
this._debuggerModel.removeBreakpoint(debuggerId);
83
delete this._breakpointForDebuggerId[debuggerId];
84
delete breakpoint._debuggerId;
86
this._storage._restoreBreakpoints(uiSourceCode);
90
* @param {WebInspector.Event} event
92
_uiSourceCodeAdded: function(event)
94
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
95
if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) {
96
this._restoreBreakpoints(uiSourceCode);
97
uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormatted, this);
102
* @param {WebInspector.Event} event
104
_uiSourceCodeFormatted: function(event)
106
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target);
107
this._restoreBreakpoints(uiSourceCode);
111
* @param {WebInspector.Event} event
113
_uiSourceCodeRemoved: function(event)
115
var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
116
if (uiSourceCode.contentType() !== WebInspector.resourceTypes.Script && uiSourceCode.contentType() !== WebInspector.resourceTypes.Document)
118
if (uiSourceCode.divergedVersion)
121
var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
125
var breakpoints = this._breakpoints.slice();
126
for (var i = 0; i < breakpoints.length; ++i) {
127
var breakpoint = breakpoints[i];
128
for (var stringifiedLocation in breakpoint._uiLocations) {
129
var uiLocation = breakpoint._uiLocations[stringifiedLocation];
130
if (uiLocation.uiSourceCode === uiSourceCode)
131
breakpoint.remove(true);
135
delete this._sourceFilesWithRestoredBreakpoints[sourceFileId];
137
var uiSourceCodes = this._workspace.uiSourceCodes();
138
for (var i = 0; i < uiSourceCodes.length; ++i)
139
this._restoreBreakpoints(uiSourceCodes[i]);
143
* @param {WebInspector.UISourceCode} uiSourceCode
144
* @param {number} lineNumber
145
* @param {string} condition
146
* @param {boolean} enabled
147
* @return {WebInspector.BreakpointManager.Breakpoint}
149
setBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
151
this._debuggerModel.setBreakpointsActive(true);
152
return this._innerSetBreakpoint(uiSourceCode, lineNumber, condition, enabled);
156
* @param {WebInspector.UISourceCode} uiSourceCode
157
* @param {number} lineNumber
158
* @param {string} condition
159
* @param {boolean} enabled
160
* @return {WebInspector.BreakpointManager.Breakpoint}
162
_innerSetBreakpoint: function(uiSourceCode, lineNumber, condition, enabled)
164
var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber);
166
breakpoint._updateBreakpoint(condition, enabled);
169
breakpoint = new WebInspector.BreakpointManager.Breakpoint(this, uiSourceCode, lineNumber, condition, enabled);
170
this._breakpoints.push(breakpoint);
175
* @param {WebInspector.UISourceCode} uiSourceCode
176
* @param {number} lineNumber
177
* @return {?WebInspector.BreakpointManager.Breakpoint}
179
findBreakpoint: function(uiSourceCode, lineNumber)
181
var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
182
var lineBreakpoints = breakpoints ? breakpoints[lineNumber] : null;
183
return lineBreakpoints ? lineBreakpoints[0] : null;
187
* @param {function(WebInspector.BreakpointManager.Breakpoint, WebInspector.UILocation)} filter
188
* @return {Array.<{breakpoint: WebInspector.BreakpointManager.Breakpoint, uiLocation: WebInspector.UILocation}>}
190
_filteredBreakpointLocations: function(filter)
193
for (var i = 0; i < this._breakpoints.length; ++i) {
194
var breakpoint = this._breakpoints[i];
195
for (var stringifiedLocation in breakpoint._uiLocations) {
196
var uiLocation = breakpoint._uiLocations[stringifiedLocation];
197
if (filter(breakpoint, uiLocation))
198
result.push({breakpoint: breakpoint, uiLocation: uiLocation});
205
* @param {WebInspector.UISourceCode} uiSourceCode
206
* @return {Array.<{breakpoint: WebInspector.BreakpointManager.Breakpoint, uiLocation: WebInspector.UILocation}>}
208
breakpointLocationsForUISourceCode: function(uiSourceCode)
210
function filter(breakpoint, uiLocation)
212
return uiLocation.uiSourceCode === uiSourceCode;
215
return this._filteredBreakpointLocations(filter);
219
* @return {Array.<{breakpoint: WebInspector.BreakpointManager.Breakpoint, uiLocation: WebInspector.UILocation}>}
221
allBreakpointLocations: function()
223
return this._filteredBreakpointLocations(function(breakpoint, uiLocation) { return true; });
227
* @param {boolean} toggleState
229
toggleAllBreakpoints: function(toggleState)
231
for (var i = 0; i < this._breakpoints.length; ++i) {
232
var breakpoint = this._breakpoints[i];
233
if (breakpoint.enabled() != toggleState)
234
breakpoint.setEnabled(toggleState);
238
removeAllBreakpoints: function()
240
var breakpoints = this._breakpoints.slice();
241
for (var i = 0; i < breakpoints.length; ++i)
242
breakpoints[i].remove();
247
// Remove all breakpoints from UI and debugger, do not update storage.
248
this._storage._muted = true;
249
this.removeAllBreakpoints();
250
delete this._storage._muted;
252
// Remove all provisional breakpoints from the debugger.
253
for (var debuggerId in this._breakpointForDebuggerId)
254
this._debuggerModel.removeBreakpoint(debuggerId);
255
this._breakpointForDebuggerId = {};
256
this._sourceFilesWithRestoredBreakpoints = {};
259
_workspaceReset: function()
261
var breakpoints = this._breakpoints.slice();
262
for (var i = 0; i < breakpoints.length; ++i) {
263
breakpoints[i]._resetLocations();
264
breakpoints[i]._isProvisional = true;
266
this._breakpoints = [];
267
this._breakpointsForUISourceCode.clear();
268
this._sourceFilesWithRestoredBreakpoints = {};
271
_breakpointResolved: function(event)
273
var breakpointId = /** @type {DebuggerAgent.BreakpointId} */ (event.data.breakpointId);
274
var location = /** @type {WebInspector.DebuggerModel.Location} */ (event.data.location);
275
var breakpoint = this._breakpointForDebuggerId[breakpointId];
276
if (!breakpoint || breakpoint._isProvisional)
278
breakpoint._addResolvedLocation(location);
282
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
283
* @param {boolean} removeFromStorage
285
_removeBreakpoint: function(breakpoint, removeFromStorage)
287
console.assert(!breakpoint._debuggerId)
288
this._breakpoints.remove(breakpoint);
289
if (removeFromStorage)
290
this._storage._removeBreakpoint(breakpoint);
294
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
295
* @param {WebInspector.UILocation} uiLocation
297
_uiLocationAdded: function(breakpoint, uiLocation)
299
var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
302
this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints);
305
var lineBreakpoints = breakpoints[uiLocation.lineNumber];
306
if (!lineBreakpoints) {
307
lineBreakpoints = [];
308
breakpoints[uiLocation.lineNumber] = lineBreakpoints;
311
lineBreakpoints.push(breakpoint);
312
this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
316
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
317
* @param {WebInspector.UILocation} uiLocation
319
_uiLocationRemoved: function(breakpoint, uiLocation)
321
var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
325
var lineBreakpoints = breakpoints[uiLocation.lineNumber];
326
if (!lineBreakpoints)
329
lineBreakpoints.remove(breakpoint);
330
if (!lineBreakpoints.length)
331
delete breakpoints[uiLocation.lineNumber];
332
this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
335
__proto__: WebInspector.Object.prototype
340
* @param {WebInspector.BreakpointManager} breakpointManager
341
* @param {WebInspector.UISourceCode} uiSourceCode
342
* @param {number} lineNumber
343
* @param {string} condition
344
* @param {boolean} enabled
346
WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, uiSourceCode, lineNumber, condition, enabled)
348
this._breakpointManager = breakpointManager;
349
this._primaryUILocation = new WebInspector.UILocation(uiSourceCode, lineNumber, 0);
350
this._sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
351
/** @type {Array.<WebInspector.Script.Location>} */
352
this._liveLocations = [];
353
/** @type {Object.<string, WebInspector.UILocation>} */
354
this._uiLocations = {};
356
// Force breakpoint update.
357
/** @type {string} */ this._condition;
358
/** @type {boolean} */ this._enabled;
359
this._updateBreakpoint(condition, enabled);
362
WebInspector.BreakpointManager.Breakpoint.prototype = {
364
* @return {WebInspector.UILocation}
366
primaryUILocation: function()
368
return this._primaryUILocation;
372
* @param {WebInspector.DebuggerModel.Location} location
374
_addResolvedLocation: function(location)
376
this._liveLocations.push(this._breakpointManager._debuggerModel.createLiveLocation(location, this._locationUpdated.bind(this, location)));
380
* @param {WebInspector.DebuggerModel.Location} location
381
* @param {WebInspector.UILocation} uiLocation
383
_locationUpdated: function(location, uiLocation)
385
var stringifiedLocation = location.scriptId + ":" + location.lineNumber + ":" + location.columnNumber;
386
var oldUILocation = /** @type {WebInspector.UILocation} */ (this._uiLocations[stringifiedLocation]);
388
this._breakpointManager._uiLocationRemoved(this, oldUILocation);
389
if (this._uiLocations[""]) {
390
delete this._uiLocations[""];
391
this._breakpointManager._uiLocationRemoved(this, this._primaryUILocation);
393
this._uiLocations[stringifiedLocation] = uiLocation;
394
this._breakpointManager._uiLocationAdded(this, uiLocation);
402
return this._enabled;
406
* @param {boolean} enabled
408
setEnabled: function(enabled)
410
this._updateBreakpoint(this._condition, enabled);
416
condition: function()
418
return this._condition;
422
* @param {string} condition
424
setCondition: function(condition)
426
this._updateBreakpoint(condition, this._enabled);
430
* @param {string} condition
431
* @param {boolean} enabled
433
_updateBreakpoint: function(condition, enabled)
435
if (this._enabled === enabled && this._condition === condition)
439
this._removeFromDebugger();
441
this._enabled = enabled;
442
this._condition = condition;
443
this._breakpointManager._storage._updateBreakpoint(this);
445
var scriptFile = this._primaryUILocation.uiSourceCode.scriptFile();
446
if (this._enabled && !(scriptFile && scriptFile.hasDivergedFromVM())) {
447
this._setInDebugger();
451
this._fakeBreakpointAtPrimaryLocation();
455
* @param {boolean=} keepInStorage
457
remove: function(keepInStorage)
459
var removeFromStorage = !keepInStorage;
460
this._resetLocations();
461
this._removeFromDebugger();
462
this._breakpointManager._removeBreakpoint(this, removeFromStorage);
465
_setInDebugger: function()
467
var rawLocation = this._primaryUILocation.uiLocationToRawLocation();
468
var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
469
if (debuggerModelLocation)
470
this._breakpointManager._debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, this._condition, didSetBreakpoint.bind(this));
472
this._breakpointManager._debuggerModel.setBreakpointByURL(this._primaryUILocation.uiSourceCode.url, this._primaryUILocation.lineNumber, 0, this._condition, didSetBreakpoint.bind(this));
475
* @this {WebInspector.BreakpointManager.Breakpoint}
476
* @param {?DebuggerAgent.BreakpointId} breakpointId
477
* @param {Array.<WebInspector.DebuggerModel.Location>} locations
479
function didSetBreakpoint(breakpointId, locations)
482
this._resetLocations();
483
this._breakpointManager._removeBreakpoint(this, false);
487
this._debuggerId = breakpointId;
488
this._breakpointManager._breakpointForDebuggerId[breakpointId] = this;
490
if (!locations.length) {
491
this._fakeBreakpointAtPrimaryLocation();
495
this._resetLocations();
496
for (var i = 0; i < locations.length; ++i) {
497
var script = this._breakpointManager._debuggerModel.scriptForId(locations[i].scriptId);
498
var uiLocation = script.rawLocationToUILocation(locations[i].lineNumber, locations[i].columnNumber);
499
if (this._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber)) {
506
for (var i = 0; i < locations.length; ++i)
507
this._addResolvedLocation(locations[i]);
511
_removeFromDebugger: function()
513
if (this._debuggerId) {
514
this._breakpointManager._debuggerModel.removeBreakpoint(this._debuggerId);
515
delete this._breakpointManager._breakpointForDebuggerId[this._debuggerId];
516
delete this._debuggerId;
520
_resetLocations: function()
522
for (var stringifiedLocation in this._uiLocations)
523
this._breakpointManager._uiLocationRemoved(this, this._uiLocations[stringifiedLocation]);
525
for (var i = 0; i < this._liveLocations.length; ++i)
526
this._liveLocations[i].dispose();
527
this._liveLocations = [];
529
this._uiLocations = {};
535
_breakpointStorageId: function()
537
return this._sourceFileId + ":" + this._primaryUILocation.lineNumber;
540
_fakeBreakpointAtPrimaryLocation: function()
542
this._resetLocations();
543
this._uiLocations[""] = this._primaryUILocation;
544
this._breakpointManager._uiLocationAdded(this, this._primaryUILocation);
550
* @param {WebInspector.BreakpointManager} breakpointManager
551
* @param {WebInspector.Setting} setting
553
WebInspector.BreakpointManager.Storage = function(breakpointManager, setting)
555
this._breakpointManager = breakpointManager;
556
this._setting = setting;
557
var breakpoints = this._setting.get();
558
/** @type {Object.<string,WebInspector.BreakpointManager.Storage.Item>} */
559
this._breakpoints = {};
560
for (var i = 0; i < breakpoints.length; ++i) {
561
var breakpoint = /** @type {WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]);
562
this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber] = breakpoint;
566
WebInspector.BreakpointManager.Storage.prototype = {
568
* @param {WebInspector.UISourceCode} uiSourceCode
570
_restoreBreakpoints: function(uiSourceCode)
573
var sourceFileId = WebInspector.BreakpointManager.sourceFileId(uiSourceCode);
574
for (var id in this._breakpoints) {
575
var breakpoint = this._breakpoints[id];
576
if (breakpoint.sourceFileId === sourceFileId)
577
this._breakpointManager._innerSetBreakpoint(uiSourceCode, breakpoint.lineNumber, breakpoint.condition, breakpoint.enabled);
583
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
585
_updateBreakpoint: function(breakpoint)
587
if (this._muted || !breakpoint._breakpointStorageId())
589
this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint);
594
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
596
_removeBreakpoint: function(breakpoint)
600
delete this._breakpoints[breakpoint._breakpointStorageId()];
606
var breakpointsArray = [];
607
for (var id in this._breakpoints)
608
breakpointsArray.push(this._breakpoints[id]);
609
this._setting.set(breakpointsArray);
615
* @param {WebInspector.BreakpointManager.Breakpoint} breakpoint
617
WebInspector.BreakpointManager.Storage.Item = function(breakpoint)
619
var primaryUILocation = breakpoint.primaryUILocation();
620
this.sourceFileId = breakpoint._sourceFileId;
621
this.lineNumber = primaryUILocation.lineNumber;
622
this.condition = breakpoint.condition();
623
this.enabled = breakpoint.enabled();
626
/** @type {WebInspector.BreakpointManager} */
627
WebInspector.breakpointManager = null;