diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/sdk/BreakpointManager.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/sdk/BreakpointManager.js | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/sdk/BreakpointManager.js b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/BreakpointManager.js new file mode 100644 index 00000000000..69f3d155ad9 --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/sdk/BreakpointManager.js @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @constructor + * @extends {WebInspector.Object} + * @param {!WebInspector.Setting} breakpointStorage + * @param {!WebInspector.Workspace} workspace + * @param {!WebInspector.TargetManager} targetManager + */ +WebInspector.BreakpointManager = function(breakpointStorage, workspace, targetManager) +{ + this._storage = new WebInspector.BreakpointManager.Storage(this, breakpointStorage); + this._workspace = workspace; + this._targetManager = targetManager; + + this._breakpointsForUISourceCode = new Map(); + this._breakpointsForPrimaryUISourceCode = new Map(); + /** @type {!StringMultimap.<!WebInspector.BreakpointManager.Breakpoint>} */ + this._provisionalBreakpoints = new StringMultimap(); + + this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); + this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); + this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this); +} + +WebInspector.BreakpointManager.Events = { + BreakpointAdded: "breakpoint-added", + BreakpointRemoved: "breakpoint-removed" +} + +WebInspector.BreakpointManager._sourceFileId = function(uiSourceCode) +{ + if (!uiSourceCode.url) + return ""; + return uiSourceCode.uri(); +} + +/** + * @param {string} sourceFileId + * @param {number} lineNumber + * @param {number} columnNumber + * @return {string} + */ +WebInspector.BreakpointManager._breakpointStorageId = function(sourceFileId, lineNumber, columnNumber) +{ + if (!sourceFileId) + return ""; + return sourceFileId + ":" + lineNumber + ":" + columnNumber; +} + +WebInspector.BreakpointManager.prototype = { + + /** + * @param {string} sourceFileId + * @return {!StringMap.<!WebInspector.BreakpointManager.Breakpoint>} + */ + _provisionalBreakpointsForSourceFileId: function(sourceFileId) + { + var result = new StringMap(); + var breakpoints = this._provisionalBreakpoints.get(sourceFileId).values(); + for (var i = 0; i < breakpoints.length; ++i) + result.put(breakpoints[i]._breakpointStorageId(), breakpoints[i]); + return result; + }, + + removeProvisionalBreakpointsForTest: function() + { + var breakpoints = this._provisionalBreakpoints.values(); + for (var i = 0; i < breakpoints.length; ++i) + breakpoints[i].remove(); + this._provisionalBreakpoints.clear(); + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + */ + _restoreBreakpoints: function(uiSourceCode) + { + var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode); + if (!sourceFileId) + return; + + this._storage.mute(); + var breakpointItems = this._storage.breakpointItems(uiSourceCode); + var provisionalBreakpoints = this._provisionalBreakpointsForSourceFileId(sourceFileId); + for (var i = 0; i < breakpointItems.length; ++i) { + var breakpointItem = breakpointItems[i]; + var itemStorageId = WebInspector.BreakpointManager._breakpointStorageId(breakpointItem.sourceFileId, breakpointItem.lineNumber, breakpointItem.columnNumber); + var provisionalBreakpoint = provisionalBreakpoints.get(itemStorageId); + if (provisionalBreakpoint) { + if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode)) + this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []); + this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(provisionalBreakpoint); + provisionalBreakpoint._updateBreakpoint(); + } else { + this._innerSetBreakpoint(uiSourceCode, breakpointItem.lineNumber, breakpointItem.columnNumber, breakpointItem.condition, breakpointItem.enabled); + } + } + this._provisionalBreakpoints.removeAll(sourceFileId); + this._storage.unmute(); + }, + + /** + * @param {!WebInspector.Event} event + */ + _uiSourceCodeAdded: function(event) + { + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); + this._restoreBreakpoints(uiSourceCode); + if (uiSourceCode.contentType() === WebInspector.resourceTypes.Script || uiSourceCode.contentType() === WebInspector.resourceTypes.Document) + uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this); + }, + + /** + * @param {!WebInspector.Event} event + */ + _uiSourceCodeRemoved: function(event) + { + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); + this._removeUISourceCode(uiSourceCode); + }, + + /** + * @param {!WebInspector.Event} event + */ + _uiSourceCodeMappingChanged: function(event) + { + var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); + var isIdentity = /** @type {boolean} */ (event.data.isIdentity); + var target = /** @type {!WebInspector.Target} */ (event.data.target); + if (isIdentity) + return; + var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || []; + for (var i = 0; i < breakpoints.length; ++i) + breakpoints[i]._updateInDebuggerForTarget(target); + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + */ + _removeUISourceCode: function(uiSourceCode) + { + var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || []; + var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode); + for (var i = 0; i < breakpoints.length; ++i) { + breakpoints[i]._resetLocations(); + if (breakpoints[i].enabled()) + this._provisionalBreakpoints.put(sourceFileId, breakpoints[i]); + } + uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._uiSourceCodeMappingChanged, this); + this._breakpointsForPrimaryUISourceCode.remove(uiSourceCode); + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string} condition + * @param {boolean} enabled + * @return {!WebInspector.BreakpointManager.Breakpoint} + */ + setBreakpoint: function(uiSourceCode, lineNumber, columnNumber, condition, enabled) + { + var targets = this._targetManager.targets(); + for (var i = 0; i < targets.length; ++i) + targets[i].debuggerModel.setBreakpointsActive(true); + return this._innerSetBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled); + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string} condition + * @param {boolean} enabled + * @return {!WebInspector.BreakpointManager.Breakpoint} + */ + _innerSetBreakpoint: function(uiSourceCode, lineNumber, columnNumber, condition, enabled) + { + var breakpoint = this.findBreakpoint(uiSourceCode, lineNumber, columnNumber); + if (breakpoint) { + breakpoint._updateState(condition, enabled); + return breakpoint; + } + var projectId = uiSourceCode.project().id(); + var path = uiSourceCode.path(); + var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode); + breakpoint = new WebInspector.BreakpointManager.Breakpoint(this, projectId, path, sourceFileId, lineNumber, columnNumber, condition, enabled); + if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode)) + this._breakpointsForPrimaryUISourceCode.put(uiSourceCode, []); + this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(breakpoint); + return breakpoint; + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @param {number} lineNumber + * @param {number} columnNumber + * @return {?WebInspector.BreakpointManager.Breakpoint} + */ + findBreakpoint: function(uiSourceCode, lineNumber, columnNumber) + { + var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode); + var lineBreakpoints = breakpoints ? breakpoints.get(String(lineNumber)) : null; + var columnBreakpoints = lineBreakpoints ? lineBreakpoints.get(String(columnNumber)) : null; + return columnBreakpoints ? columnBreakpoints[0] : null; + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @param {number} lineNumber + * @return {?WebInspector.BreakpointManager.Breakpoint} + */ + findBreakpointOnLine: function(uiSourceCode, lineNumber) + { + var breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode); + var lineBreakpoints = breakpoints ? breakpoints.get(String(lineNumber)) : null; + return lineBreakpoints ? lineBreakpoints.values()[0][0] : null; + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>} + */ + breakpointsForUISourceCode: function(uiSourceCode) + { + var result = []; + var uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode); + var breakpoints = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.values() : []; + for (var i = 0; i < breakpoints.length; ++i) { + var lineBreakpoints = breakpoints[i]; + var columnBreakpointArrays = lineBreakpoints ? lineBreakpoints.values() : []; + result = result.concat.apply(result, columnBreakpointArrays); + } + return result; + }, + + /** + * @return {!Array.<!WebInspector.BreakpointManager.Breakpoint>} + */ + allBreakpoints: function() + { + var result = []; + var uiSourceCodes = this._breakpointsForUISourceCode.keys(); + for (var i = 0; i < uiSourceCodes.length; ++i) + result = result.concat(this.breakpointsForUISourceCode(uiSourceCodes[i])); + return result; + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} + */ + breakpointLocationsForUISourceCode: function(uiSourceCode) + { + var uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode); + var lineNumbers = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.keys() : []; + var result = []; + for (var i = 0; i < lineNumbers.length; ++i) { + var lineBreakpoints = uiSourceCodeBreakpoints.get(lineNumbers[i]); + var columnNumbers = lineBreakpoints.keys(); + for (var j = 0; j < columnNumbers.length; ++j) { + var columnBreakpoints = lineBreakpoints.get(columnNumbers[j]); + var lineNumber = parseInt(lineNumbers[i], 10); + var columnNumber = parseInt(columnNumbers[j], 10); + for (var k = 0; k < columnBreakpoints.length; ++k) { + var breakpoint = columnBreakpoints[k]; + var uiLocation = uiSourceCode.uiLocation(lineNumber, columnNumber); + result.push({breakpoint: breakpoint, uiLocation: uiLocation}); + } + } + } + return result; + }, + + /** + * @return {!Array.<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} + */ + allBreakpointLocations: function() + { + var result = []; + var uiSourceCodes = this._breakpointsForUISourceCode.keys(); + for (var i = 0; i < uiSourceCodes.length; ++i) + result = result.concat(this.breakpointLocationsForUISourceCode(uiSourceCodes[i])); + return result; + }, + + /** + * @param {boolean} toggleState + */ + toggleAllBreakpoints: function(toggleState) + { + var breakpoints = this.allBreakpoints(); + for (var i = 0; i < breakpoints.length; ++i) + breakpoints[i].setEnabled(toggleState); + }, + + removeAllBreakpoints: function() + { + var breakpoints = this.allBreakpoints(); + for (var i = 0; i < breakpoints.length; ++i) + breakpoints[i].remove(); + }, + + _projectRemoved: function(event) + { + var project = /** @type {!WebInspector.Project} */ (event.data); + var uiSourceCodes = project.uiSourceCodes(); + for (var i = 0; i < uiSourceCodes.length; ++i) + this._removeUISourceCode(uiSourceCodes[i]); + }, + + /** + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + * @param {boolean} removeFromStorage + */ + _removeBreakpoint: function(breakpoint, removeFromStorage) + { + var uiSourceCode = breakpoint.uiSourceCode(); + var breakpoints = uiSourceCode ? this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [] : []; + breakpoints.remove(breakpoint); + if (removeFromStorage) + this._storage._removeBreakpoint(breakpoint); + this._provisionalBreakpoints.remove(breakpoint._sourceFileId, breakpoint); + }, + + /** + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + * @param {!WebInspector.UILocation} uiLocation + */ + _uiLocationAdded: function(breakpoint, uiLocation) + { + var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode); + if (!breakpoints) { + breakpoints = new StringMap(); + this._breakpointsForUISourceCode.put(uiLocation.uiSourceCode, breakpoints); + } + var lineBreakpoints = breakpoints.get(String(uiLocation.lineNumber)); + if (!lineBreakpoints) { + lineBreakpoints = new StringMap(); + breakpoints.put(String(uiLocation.lineNumber), lineBreakpoints); + } + var columnBreakpoints = lineBreakpoints.get(String(uiLocation.columnNumber)); + if (!columnBreakpoints) { + columnBreakpoints = []; + lineBreakpoints.put(String(uiLocation.columnNumber), columnBreakpoints); + } + columnBreakpoints.push(breakpoint); + this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation}); + }, + + /** + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + * @param {!WebInspector.UILocation} uiLocation + */ + _uiLocationRemoved: function(breakpoint, uiLocation) + { + var breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode); + if (!breakpoints) + return; + + var lineBreakpoints = breakpoints.get(String(uiLocation.lineNumber)); + if (!lineBreakpoints) + return; + var columnBreakpoints = lineBreakpoints.get(String(uiLocation.columnNumber)); + if (!columnBreakpoints) + return; + columnBreakpoints.remove(breakpoint); + if (!columnBreakpoints.length) + lineBreakpoints.remove(String(uiLocation.columnNumber)); + if (!lineBreakpoints.size()) + breakpoints.remove(String(uiLocation.lineNumber)); + if (!breakpoints.size()) + this._breakpointsForUISourceCode.remove(uiLocation.uiSourceCode); + this.dispatchEventToListeners(WebInspector.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation}); + }, + + __proto__: WebInspector.Object.prototype +} + +/** + * @constructor + * @implements {WebInspector.TargetManager.Observer} + * @param {!WebInspector.BreakpointManager} breakpointManager + * @param {string} projectId + * @param {string} path + * @param {string} sourceFileId + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string} condition + * @param {boolean} enabled + */ +WebInspector.BreakpointManager.Breakpoint = function(breakpointManager, projectId, path, sourceFileId, lineNumber, columnNumber, condition, enabled) +{ + this._breakpointManager = breakpointManager; + this._projectId = projectId; + this._path = path; + this._lineNumber = lineNumber; + this._columnNumber = columnNumber; + this._sourceFileId = sourceFileId; + + /** @type {!Object.<string, number>} */ + this._numberOfDebuggerLocationForUILocation = {}; + + // Force breakpoint update. + /** @type {string} */ this._condition; + /** @type {boolean} */ this._enabled; + /** @type {boolean} */ this._isRemoved; + /** @type {!WebInspector.UILocation|undefined} */ this._fakePrimaryLocation; + + /** @type {!Map.<!WebInspector.Target, !WebInspector.BreakpointManager.TargetBreakpoint>}*/ + this._targetBreakpoints = new Map(); + this._updateState(condition, enabled); + this._breakpointManager._targetManager.observeTargets(this); +} + +WebInspector.BreakpointManager.Breakpoint.prototype = { + /** + * @param {!WebInspector.Target} target + */ + targetAdded: function(target) + { + this._targetBreakpoints.put(target, new WebInspector.BreakpointManager.TargetBreakpoint(target, this)); + }, + + /** + * @param {!WebInspector.Target} target + */ + targetRemoved: function(target) + { + var targetBreakpoint = this._targetBreakpoints.remove(target); + targetBreakpoint._cleanUpAfterDebuggerIsGone(); + targetBreakpoint._removeEventListeners(); + }, + + /** + * @return {string} + */ + projectId: function() + { + return this._projectId; + }, + + /** + * @return {string} + */ + path: function() + { + return this._path; + }, + + /** + * @return {number} + */ + lineNumber: function() + { + return this._lineNumber; + }, + + /** + * @return {number} + */ + columnNumber: function() + { + return this._columnNumber; + }, + + /** + * @return {?WebInspector.UISourceCode} + */ + uiSourceCode: function() + { + return this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path); + }, + + /** + * @param {?WebInspector.UILocation} oldUILocation + * @param {!WebInspector.UILocation} newUILocation + */ + _replaceUILocation: function(oldUILocation, newUILocation) + { + if (this._isRemoved) + return; + + this._removeUILocation(oldUILocation, true); + this._removeFakeBreakpointAtPrimaryLocation(); + + if (!this._numberOfDebuggerLocationForUILocation[newUILocation.id()]) + this._numberOfDebuggerLocationForUILocation[newUILocation.id()] = 0; + + if (++this._numberOfDebuggerLocationForUILocation[newUILocation.id()] === 1) + this._breakpointManager._uiLocationAdded(this, newUILocation); + }, + + /** + * @param {?WebInspector.UILocation} uiLocation + * @param {boolean=} muteCreationFakeBreakpoint + */ + _removeUILocation: function(uiLocation, muteCreationFakeBreakpoint) + { + if (!uiLocation || --this._numberOfDebuggerLocationForUILocation[uiLocation.id()] !== 0) + return; + + delete this._numberOfDebuggerLocationForUILocation[uiLocation.id()]; + this._breakpointManager._uiLocationRemoved(this, uiLocation); + if (!muteCreationFakeBreakpoint) + this._fakeBreakpointAtPrimaryLocation(); + }, + + /** + * @return {boolean} + */ + enabled: function() + { + return this._enabled; + }, + + /** + * @param {boolean} enabled + */ + setEnabled: function(enabled) + { + this._updateState(this._condition, enabled); + }, + + /** + * @return {string} + */ + condition: function() + { + return this._condition; + }, + + /** + * @param {string} condition + */ + setCondition: function(condition) + { + this._updateState(condition, this._enabled); + }, + + /** + * @param {string} condition + * @param {boolean} enabled + */ + _updateState: function(condition, enabled) + { + if (this._enabled === enabled && this._condition === condition) + return; + this._enabled = enabled; + this._condition = condition; + this._breakpointManager._storage._updateBreakpoint(this); + this._updateBreakpoint(); + }, + + _updateBreakpoint: function() + { + this._removeFakeBreakpointAtPrimaryLocation(); + this._fakeBreakpointAtPrimaryLocation(); + var targetBreakpoints = this._targetBreakpoints.values(); + for (var i = 0; i < targetBreakpoints.length; ++i) + targetBreakpoints[i]._updateInDebugger(); + }, + + /** + * @param {boolean=} keepInStorage + */ + remove: function(keepInStorage) + { + this._isRemoved = true; + var removeFromStorage = !keepInStorage; + this._removeFakeBreakpointAtPrimaryLocation(); + var targetBreakpoints = this._targetBreakpoints.values(); + for (var i = 0; i < targetBreakpoints.length; ++i) { + targetBreakpoints[i]._removeFromDebugger(); + targetBreakpoints[i]._removeEventListeners(); + } + + this._breakpointManager._removeBreakpoint(this, removeFromStorage); + this._breakpointManager._targetManager.unobserveTargets(this); + }, + + /** + * @param {!WebInspector.Target} target + */ + _updateInDebuggerForTarget: function(target) + { + this._targetBreakpoints.get(target)._updateInDebugger(); + }, + + /** + * @return {string} + */ + _breakpointStorageId: function() + { + return WebInspector.BreakpointManager._breakpointStorageId(this._sourceFileId, this._lineNumber, this._columnNumber); + }, + + _fakeBreakpointAtPrimaryLocation: function() + { + if (this._isRemoved || !Object.isEmpty(this._numberOfDebuggerLocationForUILocation) || this._fakePrimaryLocation) + return; + + var uiSourceCode = this._breakpointManager._workspace.uiSourceCode(this._projectId, this._path); + if (!uiSourceCode) + return; + + this._fakePrimaryLocation = uiSourceCode.uiLocation(this._lineNumber, this._columnNumber); + this._breakpointManager._uiLocationAdded(this, this._fakePrimaryLocation); + }, + + _removeFakeBreakpointAtPrimaryLocation: function() + { + if (this._fakePrimaryLocation) { + this._breakpointManager._uiLocationRemoved(this, this._fakePrimaryLocation); + delete this._fakePrimaryLocation; + } + }, + + _resetLocations: function() + { + this._removeFakeBreakpointAtPrimaryLocation(); + var targetBreakpoints = this._targetBreakpoints.values(); + for (var i = 0; i < targetBreakpoints.length; ++i) + targetBreakpoints[i]._resetLocations(); + } +} + +/** + * @constructor + * @extends {WebInspector.TargetAware} + * @param {!WebInspector.Target} target + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + */ +WebInspector.BreakpointManager.TargetBreakpoint = function(target, breakpoint) +{ + WebInspector.TargetAware.call(this, target); + this._breakpoint = breakpoint; + /** @type {!Array.<!WebInspector.Script.Location>} */ + this._liveLocations = []; + + /** @type {!Object.<string, !WebInspector.UILocation>} */ + this._uiLocations = {}; + target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this); + target.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._updateInDebugger, this); + if (target.debuggerModel.debuggerEnabled()) + this._updateInDebugger(); +} + +WebInspector.BreakpointManager.TargetBreakpoint.prototype = { + + _resetLocations: function() + { + var uiLocations = Object.values(this._uiLocations); + for (var i = 0; i < uiLocations.length; ++i) + this._breakpoint._removeUILocation(uiLocations[i]); + + this._uiLocations = {}; + + for (var i = 0; i < this._liveLocations.length; ++i) + this._liveLocations[i].dispose(); + this._liveLocations = []; + }, + + /** + * @param {boolean=} callbackImmediately + */ + _removeFromDebugger: function(callbackImmediately) + { + this._resetLocations(); + if (!this._debuggerId) + return; + var debuggerId = this._debuggerId; + this.target().debuggerModel.removeBreakpoint(this._debuggerId, callbackImmediately ? undefined : didRemoveFromDebugger.bind(this)); + + /** + * @this {WebInspector.BreakpointManager.TargetBreakpoint} + */ + function didRemoveFromDebugger() + { + if (this._debuggerId === debuggerId) + this._didRemoveFromDebugger(); + } + if (callbackImmediately) + this._didRemoveFromDebugger(); + }, + + _updateInDebugger: function() + { + this._removeFromDebugger(); + var uiSourceCode = this._breakpoint.uiSourceCode(); + if (!uiSourceCode || !this._breakpoint._enabled) + return; + var scriptFile = uiSourceCode.scriptFileForTarget(this._target); + if (scriptFile && scriptFile.hasDivergedFromVM()) + return; + + var lineNumber = this._breakpoint._lineNumber; + var columnNumber = this._breakpoint._columnNumber; + var rawLocation = uiSourceCode.uiLocationToRawLocation(this._target, lineNumber, columnNumber); + var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation); + var condition = this._breakpoint.condition(); + if (debuggerModelLocation) + this.target().debuggerModel.setBreakpointByScriptLocation(debuggerModelLocation, condition, this._didSetBreakpointInDebugger.bind(this)); + else if (uiSourceCode.url) + this.target().debuggerModel.setBreakpointByURL(uiSourceCode.url, lineNumber, columnNumber, condition, this._didSetBreakpointInDebugger.bind(this)); + }, + + /** + * @param {?DebuggerAgent.BreakpointId} breakpointId + * @param {!Array.<!WebInspector.DebuggerModel.Location>} locations + */ + _didSetBreakpointInDebugger: function(breakpointId, locations) + { + if (!breakpointId) { + this._breakpoint.remove(true); + return; + } + + if (this._debuggerId) + this._removeFromDebugger(true); + + this._debuggerId = breakpointId; + this.target().debuggerModel.addBreakpointListener(this._debuggerId, this._breakpointResolved, this); + for (var i = 0; i < locations.length; ++i) + if (!this._addResolvedLocation(locations[i])) + return; + }, + + _didRemoveFromDebugger: function() + { + this.target().debuggerModel.removeBreakpointListener(this._debuggerId, this._breakpointResolved, this); + delete this._debuggerId; + }, + + /** + * @param {!WebInspector.Event} event + */ + _breakpointResolved: function(event) + { + this._addResolvedLocation(/** @type {!WebInspector.DebuggerModel.Location}*/ (event.data)); + }, + + /** + * @param {!WebInspector.DebuggerModel.Location} location + * @param {!WebInspector.UILocation} uiLocation + */ + _locationUpdated: function(location, uiLocation) + { + var oldUILocation = this._uiLocations[location.id()] || null; + this._uiLocations[location.id()] = uiLocation; + this._breakpoint._replaceUILocation(oldUILocation, uiLocation); + }, + + /** + * @param {!WebInspector.DebuggerModel.Location} location + * @return {boolean} + */ + _addResolvedLocation: function(location) + { + var script = location.script(); + var uiLocation = script.rawLocationToUILocation(location.lineNumber, location.columnNumber); + var breakpoint = this._breakpoint._breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber); + if (breakpoint && breakpoint != this._breakpoint) { + // location clash + this._breakpoint.remove(); + return false; + } + this._liveLocations.push(location.createLiveLocation(this._locationUpdated.bind(this, location))); + return true; + }, + + _cleanUpAfterDebuggerIsGone: function() + { + this._resetLocations(); + if (this._debuggerId) + this._didRemoveFromDebugger(); + }, + + _removeEventListeners: function() + { + this.target().debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this); + this.target().debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._updateInDebugger, this); + }, + + __proto__: WebInspector.TargetAware.prototype +} + +/** + * @constructor + * @param {!WebInspector.BreakpointManager} breakpointManager + * @param {!WebInspector.Setting} setting + */ +WebInspector.BreakpointManager.Storage = function(breakpointManager, setting) +{ + this._breakpointManager = breakpointManager; + this._setting = setting; + var breakpoints = this._setting.get(); + /** @type {!Object.<string, !WebInspector.BreakpointManager.Storage.Item>} */ + this._breakpoints = {}; + for (var i = 0; i < breakpoints.length; ++i) { + var breakpoint = /** @type {!WebInspector.BreakpointManager.Storage.Item} */ (breakpoints[i]); + breakpoint.columnNumber = breakpoint.columnNumber || 0; + this._breakpoints[breakpoint.sourceFileId + ":" + breakpoint.lineNumber + ":" + breakpoint.columnNumber] = breakpoint; + } +} + +WebInspector.BreakpointManager.Storage.prototype = { + mute: function() + { + this._muted = true; + }, + + unmute: function() + { + delete this._muted; + }, + + /** + * @param {!WebInspector.UISourceCode} uiSourceCode + * @return {!Array.<!WebInspector.BreakpointManager.Storage.Item>} + */ + breakpointItems: function(uiSourceCode) + { + var result = []; + var sourceFileId = WebInspector.BreakpointManager._sourceFileId(uiSourceCode); + for (var id in this._breakpoints) { + var breakpoint = this._breakpoints[id]; + if (breakpoint.sourceFileId === sourceFileId) + result.push(breakpoint); + } + return result; + }, + + /** + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + */ + _updateBreakpoint: function(breakpoint) + { + if (this._muted || !breakpoint._breakpointStorageId()) + return; + this._breakpoints[breakpoint._breakpointStorageId()] = new WebInspector.BreakpointManager.Storage.Item(breakpoint); + this._save(); + }, + + /** + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + */ + _removeBreakpoint: function(breakpoint) + { + if (this._muted) + return; + delete this._breakpoints[breakpoint._breakpointStorageId()]; + this._save(); + }, + + _save: function() + { + var breakpointsArray = []; + for (var id in this._breakpoints) + breakpointsArray.push(this._breakpoints[id]); + this._setting.set(breakpointsArray); + } +} + +/** + * @constructor + * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint + */ +WebInspector.BreakpointManager.Storage.Item = function(breakpoint) +{ + this.sourceFileId = breakpoint._sourceFileId; + this.lineNumber = breakpoint.lineNumber(); + this.columnNumber = breakpoint.columnNumber(); + this.condition = breakpoint.condition(); + this.enabled = breakpoint.enabled(); +} + +/** @type {!WebInspector.BreakpointManager} */ +WebInspector.breakpointManager; |