debug-server-next
Version:
Dev server for hippy-core.
740 lines (739 loc) • 30.9 kB
JavaScript
/*
* 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.
*/
/* eslint-disable rulesdir/no_underscored_properties */
import * as Common from '../../core/common/common.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Workspace from '../workspace/workspace.js';
import { DebuggerWorkspaceBinding } from './DebuggerWorkspaceBinding.js';
import { LiveLocationPool } from './LiveLocation.js';
let breakpointManagerInstance;
export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper {
_storage;
_workspace;
_targetManager;
_debuggerWorkspaceBinding;
_breakpointsForUISourceCode;
_breakpointByStorageId;
constructor(targetManager, workspace, debuggerWorkspaceBinding) {
super();
this._storage = new Storage();
this._workspace = workspace;
this._targetManager = targetManager;
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
this._breakpointsForUISourceCode = new Map();
this._breakpointByStorageId = new Map();
this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
this._workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
}
static instance(opts = { forceNew: null, targetManager: null, workspace: null, debuggerWorkspaceBinding: null }) {
const { forceNew, targetManager, workspace, debuggerWorkspaceBinding } = opts;
if (!breakpointManagerInstance || forceNew) {
if (!targetManager || !workspace || !debuggerWorkspaceBinding) {
throw new Error(`Unable to create settings: targetManager, workspace, and debuggerWorkspaceBinding must be provided: ${new Error().stack}`);
}
breakpointManagerInstance = new BreakpointManager(targetManager, workspace, debuggerWorkspaceBinding);
}
return breakpointManagerInstance;
}
static _breakpointStorageId(url, lineNumber, columnNumber) {
if (!url) {
return '';
}
return `${url}:${lineNumber}` + (typeof columnNumber === 'number' ? `:${columnNumber}` : '');
}
async copyBreakpoints(fromURL, toSourceCode) {
const breakpointItems = this._storage.breakpointItems(fromURL);
for (const item of breakpointItems) {
await this.setBreakpoint(toSourceCode, item.lineNumber, item.columnNumber, item.condition, item.enabled);
}
}
_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();
}
_uiSourceCodeAdded(event) {
const uiSourceCode = event.data;
this._restoreBreakpoints(uiSourceCode);
}
_uiSourceCodeRemoved(event) {
const uiSourceCode = event.data;
this._removeUISourceCode(uiSourceCode);
}
_projectRemoved(event) {
const project = event.data;
for (const uiSourceCode of project.uiSourceCodes()) {
this._removeUISourceCode(uiSourceCode);
}
}
_removeUISourceCode(uiSourceCode) {
const breakpoints = this.breakpointLocationsForUISourceCode(uiSourceCode);
breakpoints.forEach(bp => bp.breakpoint.removeUISourceCode(uiSourceCode));
}
async setBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled) {
let uiLocation = new Workspace.UISourceCode.UILocation(uiSourceCode, lineNumber, columnNumber);
const normalizedLocation = await 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);
}
_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.addUISourceCode(uiSourceCode);
breakpoint._updateBreakpoint();
return breakpoint;
}
breakpoint = new Breakpoint(this, uiSourceCode, uiSourceCode.url(), lineNumber, columnNumber, condition, enabled);
this._breakpointByStorageId.set(itemId, breakpoint);
return breakpoint;
}
findBreakpoint(uiLocation) {
const breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
return breakpoints ? (breakpoints.get(uiLocation.id())) || null : null;
}
async possibleBreakpoints(uiSourceCode, textRange) {
const { pluginManager } = this._debuggerWorkspaceBinding;
if (pluginManager) {
// TODO(bmeurer): Refactor this logic, as for DWARF and sourcemaps, it doesn't make sense
// to even ask V8 for possible break locations, since these are determined
// from the debugging information.
const rawLocations = await pluginManager.uiLocationToRawLocations(uiSourceCode, textRange.startLine);
if (rawLocations) {
const uiLocations = [];
for (const rawLocation of rawLocations) {
const uiLocation = await this._debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation);
if (uiLocation) {
uiLocations.push(uiLocation);
}
}
return uiLocations;
}
}
const startLocationsPromise = DebuggerWorkspaceBinding.instance().uiLocationToRawLocations(uiSourceCode, textRange.startLine, textRange.startColumn);
const endLocationsPromise = DebuggerWorkspaceBinding.instance().uiLocationToRawLocations(uiSourceCode, textRange.endLine, textRange.endColumn);
const [startLocations, endLocations] = await Promise.all([startLocationsPromise, endLocationsPromise]);
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 [];
}
return startLocation.debuggerModel
.getPossibleBreakpoints(startLocation, endLocation, /* restrictToFunction */ false)
.then(toUILocations.bind(this));
async function toUILocations(locations) {
const sortedLocationsPromises = locations.map(location => this._debuggerWorkspaceBinding.rawLocationToUILocation(location));
const nullableLocations = await Promise.all(sortedLocationsPromises);
const sortedLocations = nullableLocations.filter(location => location && location.uiSourceCode === uiSourceCode);
if (!sortedLocations.length) {
return [];
}
sortedLocations.sort(Workspace.UISourceCode.UILocation.comparator);
let lastLocation = sortedLocations[0];
const result = [lastLocation];
for (const location of sortedLocations) {
if (location.id() !== lastLocation.id()) {
result.push(location);
lastLocation = location;
}
}
return result;
}
}
breakpointLocationsForUISourceCode(uiSourceCode) {
const breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
return breakpoints ? Array.from(breakpoints.values()) : [];
}
allBreakpointLocations() {
const result = [];
for (const breakpoints of this._breakpointsForUISourceCode.values()) {
result.push(...breakpoints.values());
}
return result;
}
_removeBreakpoint(breakpoint, removeFromStorage) {
if (removeFromStorage) {
this._storage._removeBreakpoint(breakpoint);
}
this._breakpointByStorageId.delete(breakpoint._breakpointStorageId());
}
_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);
}
_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 });
}
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
Events["BreakpointAdded"] = "breakpoint-added";
Events["BreakpointRemoved"] = "breakpoint-removed";
})(Events || (Events = {}));
export class Breakpoint {
_breakpointManager;
_url;
_lineNumber;
_columnNumber;
_uiLocations;
_uiSourceCodes;
_condition;
_enabled;
_isRemoved;
_currentState;
_modelBreakpoints;
constructor(breakpointManager, primaryUISourceCode, url, lineNumber, columnNumber, condition, enabled) {
this._breakpointManager = breakpointManager;
this._url = url;
this._lineNumber = lineNumber;
this._columnNumber = columnNumber;
this._uiLocations = new Set(); // Bound locations
this._uiSourceCodes = new Set(); // All known UISourceCodes with this url
this._currentState = null;
this._modelBreakpoints = new Map();
this._updateState(condition, enabled);
this.addUISourceCode(primaryUISourceCode);
this._breakpointManager._targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
}
async refreshInDebugger() {
if (!this._isRemoved) {
const breakpoints = Array.from(this._modelBreakpoints.values());
await Promise.all(breakpoints.map(breakpoint => breakpoint._refreshBreakpoint()));
}
}
modelAdded(debuggerModel) {
const debuggerWorkspaceBinding = this._breakpointManager._debuggerWorkspaceBinding;
this._modelBreakpoints.set(debuggerModel, new ModelBreakpoint(debuggerModel, this, debuggerWorkspaceBinding));
}
modelRemoved(debuggerModel) {
const modelBreakpoint = this._modelBreakpoints.get(debuggerModel);
this._modelBreakpoints.delete(debuggerModel);
if (!modelBreakpoint) {
return;
}
modelBreakpoint._cleanUpAfterDebuggerIsGone();
modelBreakpoint._removeEventListeners();
}
addUISourceCode(uiSourceCode) {
if (!this._uiSourceCodes.has(uiSourceCode)) {
this._uiSourceCodes.add(uiSourceCode);
if (!this.bound()) {
this._breakpointManager._uiLocationAdded(this, this._defaultUILocation(uiSourceCode));
}
}
}
clearUISourceCodes() {
if (!this.bound()) {
this._removeAllUnboundLocations();
}
this._uiSourceCodes.clear();
}
removeUISourceCode(uiSourceCode) {
if (this._uiSourceCodes.has(uiSourceCode)) {
this._uiSourceCodes.delete(uiSourceCode);
if (!this.bound()) {
this._breakpointManager._uiLocationRemoved(this, this._defaultUILocation(uiSourceCode));
}
}
// Do we need to do this? Not sure if bound locations will leak...
if (this.bound()) {
for (const uiLocation of this._uiLocations) {
if (uiLocation.uiSourceCode === uiSourceCode) {
this._uiLocations.delete(uiLocation);
this._breakpointManager._uiLocationRemoved(this, uiLocation);
}
}
if (!this.bound() && !this._isRemoved) {
// Switch to unbound locations
this._addAllUnboundLocations();
}
}
}
url() {
return this._url;
}
lineNumber() {
return this._lineNumber;
}
columnNumber() {
return this._columnNumber;
}
_uiLocationAdded(uiLocation) {
if (this._isRemoved) {
return;
}
if (!this.bound()) {
// This is our first bound location; remove all unbound locations
this._removeAllUnboundLocations();
}
this._uiLocations.add(uiLocation);
this._breakpointManager._uiLocationAdded(this, uiLocation);
}
_uiLocationRemoved(uiLocation) {
if (this._uiLocations.has(uiLocation)) {
this._uiLocations.delete(uiLocation);
this._breakpointManager._uiLocationRemoved(this, uiLocation);
if (!this.bound() && !this._isRemoved) {
this._addAllUnboundLocations();
}
}
}
enabled() {
return this._enabled;
}
bound() {
return this._uiLocations.size !== 0;
}
hasBoundScript() {
for (const uiSourceCode of this._uiSourceCodes) {
if (uiSourceCode.project().type() === Workspace.Workspace.projectTypes.Network) {
return true;
}
}
return false;
}
setEnabled(enabled) {
this._updateState(this._condition, enabled);
}
condition() {
return this._condition;
}
setCondition(condition) {
this._updateState(condition, this._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.bound()) {
this._removeAllUnboundLocations();
if (!this._isRemoved) {
this._addAllUnboundLocations();
}
}
for (const modelBreakpoint of this._modelBreakpoints.values()) {
modelBreakpoint._scheduleUpdateInDebugger();
}
}
remove(keepInStorage) {
this._isRemoved = true;
const removeFromStorage = !keepInStorage;
for (const modelBreakpoint of this._modelBreakpoints.values()) {
modelBreakpoint._scheduleUpdateInDebugger();
modelBreakpoint._removeEventListeners();
}
this._breakpointManager._removeBreakpoint(this, removeFromStorage);
this._breakpointManager._targetManager.unobserveModels(SDK.DebuggerModel.DebuggerModel, this);
this.clearUISourceCodes();
}
_breakpointStorageId() {
return BreakpointManager._breakpointStorageId(this._url, this._lineNumber, this._columnNumber);
}
_resetLocations() {
this.clearUISourceCodes();
for (const modelBreakpoint of this._modelBreakpoints.values()) {
modelBreakpoint._resetLocations();
}
}
_defaultUILocation(uiSourceCode) {
return uiSourceCode.uiLocation(this._lineNumber, this._columnNumber);
}
_removeAllUnboundLocations() {
for (const uiSourceCode of this._uiSourceCodes) {
this._breakpointManager._uiLocationRemoved(this, this._defaultUILocation(uiSourceCode));
}
}
_addAllUnboundLocations() {
for (const uiSourceCode of this._uiSourceCodes) {
this._breakpointManager._uiLocationAdded(this, this._defaultUILocation(uiSourceCode));
}
}
}
export class ModelBreakpoint {
_debuggerModel;
_breakpoint;
_debuggerWorkspaceBinding;
_liveLocations;
_uiLocations;
_hasPendingUpdate;
_isUpdating;
_cancelCallback;
_currentState;
_breakpointIds;
constructor(debuggerModel, breakpoint, debuggerWorkspaceBinding) {
this._debuggerModel = debuggerModel;
this._breakpoint = breakpoint;
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
this._liveLocations = new LiveLocationPool();
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;
this._breakpointIds = [];
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().then(() => {
this._isUpdating = false;
if (this._hasPendingUpdate) {
this._hasPendingUpdate = false;
this._scheduleUpdateInDebugger();
}
});
}
_scriptDiverged() {
for (const uiSourceCode of this._breakpoint._uiSourceCodes) {
const scriptFile = this._debuggerWorkspaceBinding.scriptFile(uiSourceCode, this._debuggerModel);
if (scriptFile && scriptFile.hasDivergedFromVM()) {
return true;
}
}
return false;
}
async _updateInDebugger() {
if (this._debuggerModel.target().isDisposed()) {
this._cleanUpAfterDebuggerIsGone();
return;
}
const lineNumber = this._breakpoint.lineNumber();
const columnNumber = this._breakpoint.columnNumber();
const condition = this._breakpoint.condition();
let newState = null;
if (!this._breakpoint._isRemoved && this._breakpoint.enabled() && !this._scriptDiverged()) {
let debuggerLocations = [];
for (const uiSourceCode of this._breakpoint._uiSourceCodes) {
const locations = await DebuggerWorkspaceBinding.instance().uiLocationToRawLocations(uiSourceCode, lineNumber, columnNumber);
debuggerLocations = locations.filter(location => location.debuggerModel === this._debuggerModel);
if (debuggerLocations.length) {
break;
}
}
if (debuggerLocations.length && debuggerLocations.every(loc => loc.script())) {
const positions = debuggerLocations.map(loc => {
const script = loc.script();
return {
url: script.sourceURL,
scriptId: script.scriptId,
scriptHash: script.hash,
lineNumber: loc.lineNumber,
columnNumber: loc.columnNumber,
};
});
newState = new Breakpoint.State(positions, condition);
}
else if (this._breakpoint._currentState) {
newState = new Breakpoint.State(this._breakpoint._currentState.positions, condition);
}
else {
// TODO(bmeurer): This fallback doesn't make a whole lot of sense, we should
// at least signal a warning to the developer that this breakpoint wasn't
// really resolved.
const position = { url: this._breakpoint.url(), scriptId: '', scriptHash: '', lineNumber, columnNumber };
newState = new Breakpoint.State([position], condition);
}
}
if (this._breakpointIds.length && Breakpoint.State.equals(newState, this._currentState)) {
return;
}
this._breakpoint._currentState = newState;
if (this._breakpointIds.length) {
await this._refreshBreakpoint();
return;
}
if (!newState) {
return;
}
const results = await Promise.all(newState.positions.map(pos => {
if (pos.url) {
return this._debuggerModel.setBreakpointByURL(pos.url, pos.lineNumber, pos.columnNumber, condition);
}
return this._debuggerModel.setBreakpointInAnonymousScript(pos.scriptId, pos.scriptHash, pos.lineNumber, pos.columnNumber, condition);
}));
const breakpointIds = [];
let locations = [];
let maybeRescheduleUpdate = false;
for (const result of results) {
if (result.breakpointId) {
breakpointIds.push(result.breakpointId);
locations = locations.concat(result.locations);
}
else if (this._debuggerModel.debuggerEnabled() && !this._debuggerModel.isReadyToPause()) {
maybeRescheduleUpdate = true;
}
}
if (!breakpointIds.length && maybeRescheduleUpdate) {
// TODO(crbug.com/1229541): This is a quickfix to prevent breakpoints from
// disappearing if the Debugger is actually not enabled
// yet. This quickfix should be removed as soon as we have a solution
// to correctly synchronize the front-end with the inspector back-end.
this._scheduleUpdateInDebugger();
return;
}
this._currentState = newState;
if (this._cancelCallback) {
this._cancelCallback = false;
return;
}
if (!breakpointIds.length) {
this._breakpoint.remove(true);
return;
}
this._breakpointIds = breakpointIds;
this._breakpointIds.forEach(breakpointId => this._debuggerModel.addBreakpointListener(breakpointId, this._breakpointResolved, this));
await Promise.all(locations.map(location => this._addResolvedLocation(location)));
}
async _refreshBreakpoint() {
if (!this._breakpointIds.length) {
return;
}
this._resetLocations();
await Promise.all(this._breakpointIds.map(id => this._debuggerModel.removeBreakpoint(id)));
this._didRemoveFromDebugger();
this._currentState = null;
this._scheduleUpdateInDebugger();
}
_didRemoveFromDebugger() {
if (this._cancelCallback) {
this._cancelCallback = false;
return;
}
this._resetLocations();
this._breakpointIds.forEach(breakpointId => this._debuggerModel.removeBreakpointListener(breakpointId, this._breakpointResolved, this));
this._breakpointIds = [];
}
async _breakpointResolved(event) {
await this._addResolvedLocation(event.data);
}
async _locationUpdated(liveLocation) {
const oldUILocation = this._uiLocations.get(liveLocation);
const uiLocation = await liveLocation.uiLocation();
if (oldUILocation) {
this._breakpoint._uiLocationRemoved(oldUILocation);
}
if (uiLocation) {
this._uiLocations.set(liveLocation, uiLocation);
this._breakpoint._uiLocationAdded(uiLocation);
}
else {
this._uiLocations.delete(liveLocation);
}
}
async _addResolvedLocation(location) {
const uiLocation = await this._debuggerWorkspaceBinding.rawLocationToUILocation(location);
if (!uiLocation) {
return;
}
const breakpointLocation = this._breakpoint._breakpointManager.findBreakpoint(uiLocation);
if (breakpointLocation && breakpointLocation.breakpoint !== this._breakpoint) {
// location clash
this._breakpoint.remove(false /* keepInStorage */);
return;
}
await this._debuggerWorkspaceBinding.createLiveLocation(location, this._locationUpdated.bind(this), this._liveLocations);
}
_cleanUpAfterDebuggerIsGone() {
if (this._isUpdating) {
this._cancelCallback = true;
}
this._resetLocations();
this._currentState = null;
if (this._breakpointIds.length) {
this._didRemoveFromDebugger();
}
}
_removeEventListeners() {
this._debuggerModel.removeEventListener(SDK.DebuggerModel.Events.DebuggerWasDisabled, this._cleanUpAfterDebuggerIsGone, this);
this._debuggerModel.removeEventListener(SDK.DebuggerModel.Events.DebuggerWasEnabled, this._scheduleUpdateInDebugger, this);
}
}
(function (Breakpoint) {
class State {
positions;
condition;
constructor(positions, condition) {
this.positions = positions;
this.condition = condition;
}
static equals(stateA, stateB) {
if (!stateA || !stateB) {
return false;
}
if (stateA.condition !== stateB.condition) {
return false;
}
if (stateA.positions.length !== stateB.positions.length) {
return false;
}
for (let i = 0; i < stateA.positions.length; i++) {
const positionA = stateA.positions[i];
const positionB = stateB.positions[i];
if (positionA.url !== positionB.url) {
return false;
}
if (positionA.scriptId !== positionB.scriptId) {
return false;
}
if (positionA.scriptHash !== positionB.scriptHash) {
return false;
}
if (positionA.lineNumber !== positionB.lineNumber) {
return false;
}
if (positionA.columnNumber !== positionB.columnNumber) {
return false;
}
}
return true;
}
}
Breakpoint.State = State;
})(Breakpoint || (Breakpoint = {}));
class Storage {
_setting;
_breakpoints;
_muted;
constructor() {
this._setting = Common.Settings.Settings.instance().createLocalSetting('breakpoints', []);
this._breakpoints = new Map();
const items = this._setting.get();
for (const item of items) {
this._breakpoints.set(BreakpointManager._breakpointStorageId(item.url, item.lineNumber, item.columnNumber), item);
}
}
mute() {
this._muted = true;
}
unmute() {
delete this._muted;
}
breakpointItems(url) {
return Array.from(this._breakpoints.values()).filter(item => item.url === url);
}
_updateBreakpoint(breakpoint) {
if (this._muted || !breakpoint._breakpointStorageId()) {
return;
}
this._breakpoints.set(breakpoint._breakpointStorageId(), new Storage.Item(breakpoint));
this._save();
}
_removeBreakpoint(breakpoint) {
if (!this._muted) {
this._breakpoints.delete(breakpoint._breakpointStorageId());
this._save();
}
}
_save() {
this._setting.set(Array.from(this._breakpoints.values()));
}
}
(function (Storage) {
class Item {
url;
lineNumber;
columnNumber;
condition;
enabled;
constructor(breakpoint) {
this.url = breakpoint._url;
this.lineNumber = breakpoint.lineNumber();
this.columnNumber = breakpoint.columnNumber();
this.condition = breakpoint.condition();
this.enabled = breakpoint.enabled();
}
}
Storage.Item = Item;
})(Storage || (Storage = {}));