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