blob: ea58a3a5d35659274dbaf0823d35b5a1e96f8c5e [file] [log] [blame]
/*
* 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.
*/
/**
* @unrestricted
*/
export default class BreakpointManager extends Common.Object {
/**
* @param {!Workspace.Workspace} workspace
* @param {!SDK.TargetManager} targetManager
* @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
*/
constructor(workspace, targetManager, debuggerWorkspaceBinding) {
super();
this._storage = new Storage();
this._workspace = workspace;
this._targetManager = targetManager;
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
/** @type {!Map<!Workspace.UISourceCode, !Map<string, !Bindings.BreakpointManager.BreakpointLocation>>} */
this._breakpointsForUISourceCode = new Map();
/** @type {!Map<string, !Breakpoint>} */
this._breakpointByStorageId = new Map();
this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
}
/**
* @param {string} url
* @param {number} lineNumber
* @param {number} columnNumber
* @return {string}
*/
static _breakpointStorageId(url, lineNumber, columnNumber) {
if (!url) {
return '';
}
return url + ':' + lineNumber + ':' + columnNumber;
}
/**
* @param {string} fromURL
* @param {!Workspace.UISourceCode} toSourceCode
*/
copyBreakpoints(fromURL, toSourceCode) {
const breakpointItems = this._storage.breakpointItems(fromURL);
for (const item of breakpointItems) {
this.setBreakpoint(toSourceCode, item.lineNumber, item.columnNumber, item.condition, item.enabled);
}
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
_restoreBreakpoints(uiSourceCode) {
const url = uiSourceCode.url();
if (!url) {
return;
}
this._storage.mute();
const breakpointItems = this._storage.breakpointItems(url);
for (const item of breakpointItems) {
this._innerSetBreakpoint(uiSourceCode, item.lineNumber, item.columnNumber, item.condition, item.enabled);
}
this._storage.unmute();
}
/**
* @param {!Common.Event} event
*/
_uiSourceCodeAdded(event) {
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
this._restoreBreakpoints(uiSourceCode);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @param {string} condition
* @param {boolean} enabled
* @return {!Breakpoint}
*/
setBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled) {
let uiLocation = new Workspace.UILocation(uiSourceCode, lineNumber, columnNumber);
const normalizedLocation = this._debuggerWorkspaceBinding.normalizeUILocation(uiLocation);
if (normalizedLocation.id() !== uiLocation.id()) {
Common.Revealer.reveal(normalizedLocation);
uiLocation = normalizedLocation;
}
return this._innerSetBreakpoint(
uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, condition, enabled);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
* @param {string} condition
* @param {boolean} enabled
* @return {!Breakpoint}
*/
_innerSetBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled) {
const itemId = BreakpointManager._breakpointStorageId(uiSourceCode.url(), lineNumber, columnNumber);
let breakpoint = this._breakpointByStorageId.get(itemId);
if (breakpoint) {
breakpoint._updateState(condition, enabled);
breakpoint.setPrimaryUISourceCode(uiSourceCode);
breakpoint._updateBreakpoint();
return breakpoint;
}
breakpoint = new Breakpoint(this, uiSourceCode, uiSourceCode.url(), lineNumber, columnNumber, condition, enabled);
this._breakpointByStorageId.set(itemId, breakpoint);
return breakpoint;
}
/**
* @param {!Workspace.UILocation} uiLocation
* @return {?Bindings.BreakpointManager.BreakpointLocation}
*/
findBreakpoint(uiLocation) {
const breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
return breakpoints ? (breakpoints.get(uiLocation.id())) || null : null;
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {!TextUtils.TextRange} textRange
* @return {!Promise<!Array<!Workspace.UILocation>>}
*/
possibleBreakpoints(uiSourceCode, textRange) {
const startLocations = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocations(
uiSourceCode, textRange.startLine, textRange.startColumn);
const endLocations = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocations(
uiSourceCode, textRange.endLine, textRange.endColumn);
const endLocationByModel = new Map();
for (const location of endLocations) {
endLocationByModel.set(location.debuggerModel, location);
}
let startLocation = null;
let endLocation = null;
for (const location of startLocations) {
const endLocationCandidate = endLocationByModel.get(location.debuggerModel);
if (endLocationCandidate) {
startLocation = location;
endLocation = endLocationCandidate;
break;
}
}
if (!startLocation || !endLocation) {
return Promise.resolve([]);
}
return startLocation.debuggerModel
.getPossibleBreakpoints(startLocation, endLocation, /* restrictToFunction */ false)
.then(toUILocations.bind(this));
/**
* @this {!BreakpointManager}
* @param {!Array<!SDK.DebuggerModel.BreakLocation>} locations
* @return {!Array<!Workspace.UILocation>}
*/
function toUILocations(locations) {
let sortedLocations = locations.map(location => this._debuggerWorkspaceBinding.rawLocationToUILocation(location));
sortedLocations = sortedLocations.filter(location => location && location.uiSourceCode === uiSourceCode);
sortedLocations.sort(Workspace.UILocation.comparator);
if (!sortedLocations.length) {
return [];
}
const result = [sortedLocations[0]];
let lastLocation = sortedLocations[0];
for (let i = 1; i < sortedLocations.length; ++i) {
if (sortedLocations[i].id() === lastLocation.id()) {
continue;
}
result.push(sortedLocations[i]);
lastLocation = sortedLocations[i];
}
return result;
}
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {!Array<!Bindings.BreakpointManager.BreakpointLocation>}
*/
breakpointLocationsForUISourceCode(uiSourceCode) {
const breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
return breakpoints ? Array.from(breakpoints.values()) : [];
}
/**
* @return {!Array<!Bindings.BreakpointManager.BreakpointLocation>}
*/
allBreakpointLocations() {
let result = [];
for (const breakpoints of this._breakpointsForUISourceCode.values()) {
result = result.concat(Array.from(breakpoints.values()));
}
return result;
}
/**
* @param {!Breakpoint} breakpoint
* @param {boolean} removeFromStorage
*/
_removeBreakpoint(breakpoint, removeFromStorage) {
if (removeFromStorage) {
this._storage._removeBreakpoint(breakpoint);
}
this._breakpointByStorageId.delete(breakpoint._breakpointStorageId());
}
/**
* @param {!Breakpoint} breakpoint
* @param {!Workspace.UILocation} uiLocation
*/
_uiLocationAdded(breakpoint, uiLocation) {
let breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
if (!breakpoints) {
breakpoints = new Map();
this._breakpointsForUISourceCode.set(uiLocation.uiSourceCode, breakpoints);
}
const breakpointLocation = {breakpoint: breakpoint, uiLocation: uiLocation};
breakpoints.set(uiLocation.id(), breakpointLocation);
this.dispatchEventToListeners(Events.BreakpointAdded, breakpointLocation);
}
/**
* @param {!Breakpoint} breakpoint
* @param {!Workspace.UILocation} uiLocation
*/
_uiLocationRemoved(breakpoint, uiLocation) {
const breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
if (!breakpoints) {
return;
}
const breakpointLocation = breakpoints.get(uiLocation.id()) || null;
if (!breakpointLocation) {
return;
}
breakpoints.delete(uiLocation.id());
if (breakpoints.size === 0) {
this._breakpointsForUISourceCode.delete(uiLocation.uiSourceCode);
}
this.dispatchEventToListeners(Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
}
}
/** @enum {symbol} */
export const Events = {
BreakpointAdded: Symbol('breakpoint-added'),
BreakpointRemoved: Symbol('breakpoint-removed')
};
/**
* @unrestricted
* @implements {SDK.SDKModelObserver<!SDK.DebuggerModel>}
*/
export class Breakpoint {
/**
* @param {!BreakpointManager} breakpointManager
* @param {!Workspace.UISourceCode} primaryUISourceCode
* @param {string} url
* @param {number} lineNumber
* @param {number} columnNumber
* @param {string} condition
* @param {boolean} enabled
*/
constructor(breakpointManager, primaryUISourceCode, url, lineNumber, columnNumber, condition, enabled) {
this._breakpointManager = breakpointManager;
this._url = url;
this._lineNumber = lineNumber;
this._columnNumber = columnNumber;
/** @type {?Workspace.UILocation} */
this._defaultUILocation = null;
/** @type {!Set<!Workspace.UILocation>} */
this._uiLocations = new Set();
/** @type {string} */ this._condition;
/** @type {boolean} */ this._enabled;
/** @type {boolean} */ this._isRemoved;
this._currentState = null;
/** @type {!Map.<!SDK.DebuggerModel, !ModelBreakpoint>}*/
this._modelBreakpoints = new Map();
this._updateState(condition, enabled);
this.setPrimaryUISourceCode(primaryUISourceCode);
this._breakpointManager._targetManager.observeModels(SDK.DebuggerModel, this);
}
refreshInDebugger() {
if (this._isRemoved) {
return;
}
for (const breakpoint of this._modelBreakpoints.values()) {
breakpoint._refreshBreakpoint();
}
}
/**
* @override
* @param {!SDK.DebuggerModel} debuggerModel
*/
modelAdded(debuggerModel) {
const debuggerWorkspaceBinding = this._breakpointManager._debuggerWorkspaceBinding;
this._modelBreakpoints.set(debuggerModel, new ModelBreakpoint(debuggerModel, this, debuggerWorkspaceBinding));
}
/**
* @override
* @param {!SDK.DebuggerModel} debuggerModel
*/
modelRemoved(debuggerModel) {
const modelBreakpoint = this._modelBreakpoints.remove(debuggerModel);
modelBreakpoint._cleanUpAfterDebuggerIsGone();
modelBreakpoint._removeEventListeners();
}
/**
* @param {?Workspace.UISourceCode} primaryUISourceCode
*/
setPrimaryUISourceCode(primaryUISourceCode) {
if (this._uiLocations.size === 0 && this._defaultUILocation) {
this._breakpointManager._uiLocationRemoved(this, this._defaultUILocation);
}
if (primaryUISourceCode) {
this._defaultUILocation = primaryUISourceCode.uiLocation(this._lineNumber, this._columnNumber);
} else {
this._defaultUILocation = null;
}
if (this._uiLocations.size === 0 && this._defaultUILocation && !this._isRemoved) {
this._breakpointManager._uiLocationAdded(this, this._defaultUILocation);
}
}
/**
* @return {string}
*/
url() {
return this._url;
}
/**
* @return {number}
*/
lineNumber() {
return this._lineNumber;
}
/**
* @return {number}
*/
columnNumber() {
return this._columnNumber;
}
/**
* @param {!Workspace.UILocation} uiLocation
*/
_uiLocationAdded(uiLocation) {
if (this._isRemoved) {
return;
}
if (this._uiLocations.size === 0 && this._defaultUILocation) {
this._breakpointManager._uiLocationRemoved(this, this._defaultUILocation);
}
this._uiLocations.add(uiLocation);
this._breakpointManager._uiLocationAdded(this, uiLocation);
}
/**
* @param {!Workspace.UILocation} uiLocation
*/
_uiLocationRemoved(uiLocation) {
this._uiLocations.delete(uiLocation);
this._breakpointManager._uiLocationRemoved(this, uiLocation);
if (this._uiLocations.size === 0 && this._defaultUILocation && !this._isRemoved) {
this._breakpointManager._uiLocationAdded(this, this._defaultUILocation);
}
}
/**
* @return {boolean}
*/
enabled() {
return this._enabled;
}
/**
* @param {boolean} enabled
*/
setEnabled(enabled) {
this._updateState(this._condition, enabled);
}
/**
* @return {string}
*/
condition() {
return this._condition;
}
/**
* @param {string} condition
*/
setCondition(condition) {
this._updateState(condition, this._enabled);
}
/**
* @param {string} condition
* @param {boolean} enabled
*/
_updateState(condition, enabled) {
if (this._enabled === enabled && this._condition === condition) {
return;
}
this._enabled = enabled;
this._condition = condition;
this._breakpointManager._storage._updateBreakpoint(this);
this._updateBreakpoint();
}
_updateBreakpoint() {
if (this._uiLocations.size === 0 && this._defaultUILocation) {
this._breakpointManager._uiLocationRemoved(this, this._defaultUILocation);
}
if (this._uiLocations.size === 0 && this._defaultUILocation && !this._isRemoved) {
this._breakpointManager._uiLocationAdded(this, this._defaultUILocation);
}
const modelBreakpoints = this._modelBreakpoints.valuesArray();
for (let i = 0; i < modelBreakpoints.length; ++i) {
modelBreakpoints[i]._scheduleUpdateInDebugger();
}
}
/**
* @param {boolean} keepInStorage
*/
remove(keepInStorage) {
this._isRemoved = true;
const removeFromStorage = !keepInStorage;
const modelBreakpoints = this._modelBreakpoints.valuesArray();
for (let i = 0; i < modelBreakpoints.length; ++i) {
modelBreakpoints[i]._scheduleUpdateInDebugger();
modelBreakpoints[i]._removeEventListeners();
}
this._breakpointManager._removeBreakpoint(this, removeFromStorage);
this._breakpointManager._targetManager.unobserveModels(SDK.DebuggerModel, this);
this.setPrimaryUISourceCode(null);
}
/**
* @return {string}
*/
_breakpointStorageId() {
return BreakpointManager._breakpointStorageId(this._url, this._lineNumber, this._columnNumber);
}
_resetLocations() {
this.setPrimaryUISourceCode(null);
const modelBreakpoints = this._modelBreakpoints.valuesArray();
for (let i = 0; i < modelBreakpoints.length; ++i) {
modelBreakpoints[i]._resetLocations();
}
}
}
/**
* @unrestricted
*/
class ModelBreakpoint {
/**
* @param {!SDK.DebuggerModel} debuggerModel
* @param {!Breakpoint} breakpoint
* @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
*/
constructor(debuggerModel, breakpoint, debuggerWorkspaceBinding) {
this._debuggerModel = debuggerModel;
this._breakpoint = breakpoint;
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
this._liveLocations = new Bindings.LiveLocationPool();
/** @type {!Map<!Bindings.LiveLocation, !Workspace.UILocation>} */
this._uiLocations = new Map();
this._debuggerModel.addEventListener(
SDK.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this);
this._debuggerModel.addEventListener(
SDK.DebuggerModel.Events.DebuggerWasEnabled, this._scheduleUpdateInDebugger, this);
this._hasPendingUpdate = false;
this._isUpdating = false;
this._cancelCallback = false;
this._currentState = null;
if (this._debuggerModel.debuggerEnabled()) {
this._scheduleUpdateInDebugger();
}
}
_resetLocations() {
for (const uiLocation of this._uiLocations.values()) {
this._breakpoint._uiLocationRemoved(uiLocation);
}
this._uiLocations.clear();
this._liveLocations.disposeAll();
}
_scheduleUpdateInDebugger() {
if (this._isUpdating) {
this._hasPendingUpdate = true;
return;
}
this._isUpdating = true;
this._updateInDebugger(this._didUpdateInDebugger.bind(this));
}
_didUpdateInDebugger() {
this._isUpdating = false;
if (this._hasPendingUpdate) {
this._hasPendingUpdate = false;
this._scheduleUpdateInDebugger();
}
}
/**
* @return {boolean}
*/
_scriptDiverged() {
const uiLocation = this._breakpoint._defaultUILocation;
const uiSourceCode = uiLocation ? uiLocation.uiSourceCode : null;
if (!uiSourceCode) {
return false;
}
const scriptFile = this._debuggerWorkspaceBinding.scriptFile(uiSourceCode, this._debuggerModel);
return !!scriptFile && scriptFile.hasDivergedFromVM();
}
/**
* @param {function()} callback
* @return {!Promise}
*/
async _updateInDebugger(callback) {
if (this._debuggerModel.target().isDisposed()) {
this._cleanUpAfterDebuggerIsGone();
callback();
return;
}
const uiLocation = this._breakpoint._defaultUILocation;
const uiSourceCode = uiLocation ? uiLocation.uiSourceCode : null;
const lineNumber = this._breakpoint._lineNumber;
const columnNumber = this._breakpoint._columnNumber;
const condition = this._breakpoint.condition();
let debuggerLocation = null;
if (uiSourceCode) {
const locations =
Bindings.debuggerWorkspaceBinding.uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
debuggerLocation = locations.find(location => location.debuggerModel === this._debuggerModel);
}
let newState;
if (this._breakpoint._isRemoved || !this._breakpoint.enabled() || this._scriptDiverged()) {
newState = null;
} else if (debuggerLocation) {
const script = debuggerLocation.script();
if (script.sourceURL) {
newState = new Breakpoint.State(
script.sourceURL, null, null, debuggerLocation.lineNumber, debuggerLocation.columnNumber, condition);
} else {
newState = new Breakpoint.State(
null, script.scriptId, script.hash, debuggerLocation.lineNumber, debuggerLocation.columnNumber, condition);
}
} else if (this._breakpoint._currentState && this._breakpoint._currentState.url) {
const position = this._breakpoint._currentState;
newState = new Breakpoint.State(position.url, null, null, position.lineNumber, position.columnNumber, condition);
} else if (uiSourceCode) {
newState = new Breakpoint.State(uiSourceCode.url(), null, null, lineNumber, columnNumber, condition);
}
if (this._debuggerId && Breakpoint.State.equals(newState, this._currentState)) {
callback();
return;
}
this._breakpoint._currentState = newState;
if (this._debuggerId) {
await this._refreshBreakpoint();
callback();
return;
}
if (!newState) {
callback();
return;
}
let result;
this._currentState = newState;
if (newState.url) {
result = await this._debuggerModel.setBreakpointByURL(
newState.url, newState.lineNumber, newState.columnNumber, newState.condition);
} else if (newState.scriptId && newState.scriptHash) {
result = await this._debuggerModel.setBreakpointInAnonymousScript(
newState.scriptId, newState.scriptHash, newState.lineNumber, newState.columnNumber, newState.condition);
}
if (result && result.breakpointId) {
this._didSetBreakpointInDebugger(callback, result.breakpointId, result.locations);
} else {
this._didSetBreakpointInDebugger(callback, null, []);
}
}
async _refreshBreakpoint() {
if (!this._debuggerId) {
return;
}
this._resetLocations();
await this._debuggerModel.removeBreakpoint(this._debuggerId);
this._didRemoveFromDebugger();
this._currentState = null;
this._scheduleUpdateInDebugger();
}
/**
* @param {function()} callback
* @param {?Protocol.Debugger.BreakpointId} breakpointId
* @param {!Array.<!SDK.DebuggerModel.Location>} locations
*/
_didSetBreakpointInDebugger(callback, breakpointId, locations) {
if (this._cancelCallback) {
this._cancelCallback = false;
callback();
return;
}
if (!breakpointId) {
this._breakpoint.remove(true);
callback();
return;
}
this._debuggerId = breakpointId;
this._debuggerModel.addBreakpointListener(this._debuggerId, this._breakpointResolved, this);
for (let i = 0; i < locations.length; ++i) {
if (!this._addResolvedLocation(locations[i])) {
break;
}
}
callback();
}
_didRemoveFromDebugger() {
if (this._cancelCallback) {
this._cancelCallback = false;
return;
}
this._resetLocations();
this._debuggerModel.removeBreakpointListener(this._debuggerId, this._breakpointResolved, this);
delete this._debuggerId;
}
/**
* @param {!Common.Event} event
*/
_breakpointResolved(event) {
this._addResolvedLocation(/** @type {!SDK.DebuggerModel.Location}*/ (event.data));
}
/**
* @param {!Bindings.LiveLocation} liveLocation
*/
_locationUpdated(liveLocation) {
const oldUILocation = this._uiLocations.get(liveLocation);
if (oldUILocation) {
this._breakpoint._uiLocationRemoved(oldUILocation);
}
let uiLocation = liveLocation.uiLocation();
if (uiLocation) {
const breakpointLocation = this._breakpoint._breakpointManager.findBreakpoint(uiLocation);
if (breakpointLocation && breakpointLocation.uiLocation !== breakpointLocation.breakpoint._defaultUILocation) {
uiLocation = null;
}
}
if (uiLocation) {
this._uiLocations.set(liveLocation, uiLocation);
this._breakpoint._uiLocationAdded(uiLocation);
} else {
this._uiLocations.delete(liveLocation);
}
}
/**
* @param {!SDK.DebuggerModel.Location} location
* @return {boolean}
*/
_addResolvedLocation(location) {
const uiLocation = this._debuggerWorkspaceBinding.rawLocationToUILocation(location);
if (!uiLocation) {
return false;
}
const breakpointLocation = this._breakpoint._breakpointManager.findBreakpoint(uiLocation);
if (breakpointLocation && breakpointLocation.breakpoint !== this._breakpoint) {
// location clash
this._breakpoint.remove(false /* keepInStorage */);
return false;
}
this._debuggerWorkspaceBinding.createLiveLocation(location, this._locationUpdated.bind(this), this._liveLocations);
return true;
}
_cleanUpAfterDebuggerIsGone() {
if (this._isUpdating) {
this._cancelCallback = true;
}
this._resetLocations();
this._currentState = null;
if (this._debuggerId) {
this._didRemoveFromDebugger();
}
}
_removeEventListeners() {
this._debuggerModel.removeEventListener(
SDK.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this);
this._debuggerModel.removeEventListener(
SDK.DebuggerModel.Events.DebuggerWasEnabled, this._scheduleUpdateInDebugger, this);
}
}
Breakpoint.State = class {
/**
* @param {?string} url
* @param {?string} scriptId
* @param {?string} scriptHash
* @param {number} lineNumber
* @param {number} columnNumber
* @param {string} condition
*/
constructor(url, scriptId, scriptHash, lineNumber, columnNumber, condition) {
this.url = url;
this.scriptId = scriptId;
this.scriptHash = scriptHash;
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
this.condition = condition;
}
/**
* @param {?Breakpoint.State|undefined} stateA
* @param {?Breakpoint.State|undefined} stateB
* @return {boolean}
*/
static equals(stateA, stateB) {
if (!stateA || !stateB) {
return false;
}
return stateA.url === stateB.url && stateA.scriptId === stateB.scriptId &&
stateA.scriptHash === stateB.scriptHash && stateA.lineNumber === stateB.lineNumber &&
stateA.columnNumber === stateB.columnNumber && stateA.condition === stateB.condition;
}
};
class Storage {
constructor() {
this._setting = Common.settings.createLocalSetting('breakpoints', []);
/** @type {!Map<string, !Storage.Item>} */
this._breakpoints = new Map();
const items = /** @type {!Array<!Storage.Item>} */ (this._setting.get());
for (const item of items) {
item.columnNumber = item.columnNumber || 0;
this._breakpoints.set(BreakpointManager._breakpointStorageId(item.url, item.lineNumber, item.columnNumber), item);
}
/** @type {boolean|undefined} */ this._muted;
}
mute() {
this._muted = true;
}
unmute() {
delete this._muted;
}
/**
* @param {string} url
* @return {!Array<!Storage.Item>}
*/
breakpointItems(url) {
return Array.from(this._breakpoints.values()).filter(item => item.url === url);
}
/**
* @param {!Breakpoint} breakpoint
*/
_updateBreakpoint(breakpoint) {
if (this._muted || !breakpoint._breakpointStorageId()) {
return;
}
this._breakpoints.set(breakpoint._breakpointStorageId(), new Storage.Item(breakpoint));
this._save();
}
/**
* @param {!Breakpoint} breakpoint
*/
_removeBreakpoint(breakpoint) {
if (this._muted) {
return;
}
this._breakpoints.delete(breakpoint._breakpointStorageId());
this._save();
}
_save() {
this._setting.set(Array.from(this._breakpoints.values()));
}
}
/**
* @unrestricted
*/
Storage.Item = class {
/**
* @param {!Breakpoint} breakpoint
*/
constructor(breakpoint) {
this.url = breakpoint._url;
this.lineNumber = breakpoint.lineNumber();
this.columnNumber = breakpoint.columnNumber();
this.condition = breakpoint.condition();
this.enabled = breakpoint.enabled();
}
};
/* Legacy exported object */
self.Bindings = self.Bindings || {};
/* Legacy exported object */
Bindings = Bindings || {};
/** @constructor */
Bindings.BreakpointManager = BreakpointManager;
/** @enum {symbol} */
Bindings.BreakpointManager.Events = Events;
/** @constructor */
Bindings.BreakpointManager.Breakpoint = Breakpoint;
Bindings.BreakpointManager.ModelBreakpoint = ModelBreakpoint;
/** @typedef {{
* breakpoint: !Breakpoint,
* uiLocation: !Workspace.UILocation
* }}
*/
Bindings.BreakpointManager.BreakpointLocation;
/** @type {!Bindings.BreakpointManager} */
Bindings.breakpointManager;