mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 13:44:39 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
987 lines
33 KiB
JavaScript
987 lines
33 KiB
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.
|
|
*/
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
Bindings.BreakpointManager = class extends Common.Object {
|
|
/**
|
|
* @param {!Workspace.Workspace} workspace
|
|
* @param {!SDK.TargetManager} targetManager
|
|
* @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
|
|
*/
|
|
constructor(workspace, targetManager, debuggerWorkspaceBinding) {
|
|
super();
|
|
this._storage = new Bindings.BreakpointManager.Storage();
|
|
this._workspace = workspace;
|
|
this._targetManager = targetManager;
|
|
this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
|
|
/** @type {!Map<!Workspace.UISourceCode, !Map<number, !Map<number, !Array<!Bindings.BreakpointManager.Breakpoint>>>>} */
|
|
this._breakpointsForUISourceCode = new Map();
|
|
/** @type {!Map<string, !Bindings.BreakpointManager.Breakpoint>} */
|
|
this._breakpointByStorageId = new Map();
|
|
|
|
this._workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
|
|
this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
|
|
this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, 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 {!Common.Event} event
|
|
*/
|
|
_uiSourceCodeRemoved(event) {
|
|
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
|
|
this._removeUISourceCode(uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
*/
|
|
_removeUISourceCode(uiSourceCode) {
|
|
const breakpoints = uiSourceCode[Bindings.BreakpointManager._breakpointsSymbol] || new Set();
|
|
for (const breakpoint of breakpoints)
|
|
breakpoint._resetLocations();
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @param {string} condition
|
|
* @param {boolean} enabled
|
|
* @return {!Bindings.BreakpointManager.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 {!Bindings.BreakpointManager.Breakpoint}
|
|
*/
|
|
_innerSetBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled) {
|
|
const itemId = Bindings.BreakpointManager._breakpointStorageId(uiSourceCode.url(), lineNumber, columnNumber);
|
|
let breakpoint = this._breakpointByStorageId.get(itemId);
|
|
if (breakpoint) {
|
|
breakpoint.setPrimaryUISourceCode(uiSourceCode);
|
|
breakpoint._updateState(condition, enabled);
|
|
breakpoint._updateBreakpoint();
|
|
return breakpoint;
|
|
}
|
|
breakpoint = new Bindings.BreakpointManager.Breakpoint(
|
|
this, uiSourceCode, uiSourceCode.url(), lineNumber, columnNumber, condition, enabled);
|
|
this._breakpointByStorageId.set(itemId, breakpoint);
|
|
return breakpoint;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @return {!Array<!Bindings.BreakpointManager.Breakpoint>}
|
|
*/
|
|
findBreakpoints(uiSourceCode, lineNumber) {
|
|
const breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
|
|
const lineBreakpoints = breakpoints ? breakpoints.get(lineNumber) : null;
|
|
return lineBreakpoints ? lineBreakpoints.valuesArray()[0] : [];
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {?Bindings.BreakpointManager.Breakpoint}
|
|
*/
|
|
findBreakpoint(uiSourceCode, lineNumber, columnNumber) {
|
|
const breakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
|
|
const lineBreakpoints = breakpoints ? breakpoints.get(lineNumber) : null;
|
|
const columnBreakpoints = lineBreakpoints ? lineBreakpoints.get(columnNumber) : null;
|
|
return columnBreakpoints ? columnBreakpoints[0] : null;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {!TextUtils.TextRange} textRange
|
|
* @return {!Promise<!Array<!Workspace.UILocation>>}
|
|
*/
|
|
possibleBreakpoints(uiSourceCode, textRange) {
|
|
const startLocation = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(
|
|
uiSourceCode, textRange.startLine, textRange.startColumn);
|
|
const endLocation =
|
|
Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(uiSourceCode, textRange.endLine, textRange.endColumn);
|
|
if (!startLocation || !endLocation || startLocation.debuggerModel !== endLocation.debuggerModel)
|
|
return Promise.resolve([]);
|
|
|
|
return startLocation.debuggerModel
|
|
.getPossibleBreakpoints(startLocation, endLocation, /* restrictToFunction */ false)
|
|
.then(toUILocations.bind(this));
|
|
|
|
/**
|
|
* @this {!Bindings.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.Breakpoint>}
|
|
*/
|
|
breakpointsForUISourceCode(uiSourceCode) {
|
|
let result = [];
|
|
const uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
|
|
const breakpoints = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.valuesArray() : [];
|
|
for (let i = 0; i < breakpoints.length; ++i) {
|
|
const lineBreakpoints = breakpoints[i];
|
|
const columnBreakpointArrays = lineBreakpoints ? lineBreakpoints.valuesArray() : [];
|
|
result = result.concat.apply(result, columnBreakpointArrays);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return {!Array.<!Bindings.BreakpointManager.Breakpoint>}
|
|
*/
|
|
_allBreakpoints() {
|
|
let result = [];
|
|
const uiSourceCodes = this._breakpointsForUISourceCode.keysArray();
|
|
for (let i = 0; i < uiSourceCodes.length; ++i)
|
|
result = result.concat(this.breakpointsForUISourceCode(uiSourceCodes[i]));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {!Array.<!{breakpoint: !Bindings.BreakpointManager.Breakpoint, uiLocation: !Workspace.UILocation}>}
|
|
*/
|
|
breakpointLocationsForUISourceCode(uiSourceCode) {
|
|
const uiSourceCodeBreakpoints = this._breakpointsForUISourceCode.get(uiSourceCode);
|
|
const lineNumbers = uiSourceCodeBreakpoints ? uiSourceCodeBreakpoints.keysArray() : [];
|
|
const result = [];
|
|
for (let i = 0; i < lineNumbers.length; ++i) {
|
|
const lineBreakpoints = uiSourceCodeBreakpoints.get(lineNumbers[i]);
|
|
const columnNumbers = lineBreakpoints.keysArray();
|
|
for (let j = 0; j < columnNumbers.length; ++j) {
|
|
const columnBreakpoints = lineBreakpoints.get(columnNumbers[j]);
|
|
const lineNumber = parseInt(lineNumbers[i], 10);
|
|
const columnNumber = parseInt(columnNumbers[j], 10);
|
|
for (let k = 0; k < columnBreakpoints.length; ++k) {
|
|
const breakpoint = columnBreakpoints[k];
|
|
const uiLocation = uiSourceCode.uiLocation(lineNumber, columnNumber);
|
|
result.push({breakpoint: breakpoint, uiLocation: uiLocation});
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return {!Array.<!{breakpoint: !Bindings.BreakpointManager.Breakpoint, uiLocation: !Workspace.UILocation}>}
|
|
*/
|
|
allBreakpointLocations() {
|
|
let result = [];
|
|
const uiSourceCodes = this._breakpointsForUISourceCode.keysArray();
|
|
for (let i = 0; i < uiSourceCodes.length; ++i)
|
|
result = result.concat(this.breakpointLocationsForUISourceCode(uiSourceCodes[i]));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} toggleState
|
|
*/
|
|
toggleAllBreakpoints(toggleState) {
|
|
const breakpoints = this._allBreakpoints();
|
|
for (let i = 0; i < breakpoints.length; ++i)
|
|
breakpoints[i].setEnabled(toggleState);
|
|
}
|
|
|
|
removeAllBreakpoints() {
|
|
const breakpoints = this._allBreakpoints();
|
|
for (let i = 0; i < breakpoints.length; ++i)
|
|
breakpoints[i].remove(false /* keepInStorage */);
|
|
}
|
|
|
|
/**
|
|
* @param {!Set<!Bindings.BreakpointManager.Breakpoint>} selectedBreakpoints
|
|
*/
|
|
removeOtherBreakpoints(selectedBreakpoints) {
|
|
const allBreakpoints = this._allBreakpoints();
|
|
allBreakpoints.forEach(breakpoint => {
|
|
if (!selectedBreakpoints.has(breakpoint))
|
|
breakpoint.remove(false /* keepInStorage */);
|
|
});
|
|
}
|
|
|
|
_projectRemoved(event) {
|
|
const project = /** @type {!Workspace.Project} */ (event.data);
|
|
const uiSourceCodes = project.uiSourceCodes();
|
|
for (let i = 0; i < uiSourceCodes.length; ++i)
|
|
this._removeUISourceCode(uiSourceCodes[i]);
|
|
}
|
|
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
|
|
* @param {boolean} removeFromStorage
|
|
*/
|
|
_removeBreakpoint(breakpoint, removeFromStorage) {
|
|
if (removeFromStorage)
|
|
this._storage._removeBreakpoint(breakpoint);
|
|
this._breakpointByStorageId.delete(breakpoint._breakpointStorageId());
|
|
}
|
|
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.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);
|
|
}
|
|
let lineBreakpoints = breakpoints.get(uiLocation.lineNumber);
|
|
if (!lineBreakpoints) {
|
|
lineBreakpoints = new Map();
|
|
breakpoints.set(uiLocation.lineNumber, lineBreakpoints);
|
|
}
|
|
let columnBreakpoints = lineBreakpoints.get(uiLocation.columnNumber);
|
|
if (!columnBreakpoints) {
|
|
columnBreakpoints = [];
|
|
lineBreakpoints.set(uiLocation.columnNumber, columnBreakpoints);
|
|
}
|
|
columnBreakpoints.push(breakpoint);
|
|
this.dispatchEventToListeners(
|
|
Bindings.BreakpointManager.Events.BreakpointAdded, {breakpoint: breakpoint, uiLocation: uiLocation});
|
|
}
|
|
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
|
|
* @param {!Workspace.UILocation} uiLocation
|
|
*/
|
|
_uiLocationRemoved(breakpoint, uiLocation) {
|
|
const breakpoints = this._breakpointsForUISourceCode.get(uiLocation.uiSourceCode);
|
|
if (!breakpoints)
|
|
return;
|
|
|
|
const lineBreakpoints = breakpoints.get(uiLocation.lineNumber);
|
|
if (!lineBreakpoints)
|
|
return;
|
|
const columnBreakpoints = lineBreakpoints.get(uiLocation.columnNumber);
|
|
if (!columnBreakpoints)
|
|
return;
|
|
columnBreakpoints.remove(breakpoint);
|
|
if (!columnBreakpoints.length)
|
|
lineBreakpoints.remove(uiLocation.columnNumber);
|
|
if (!lineBreakpoints.size)
|
|
breakpoints.remove(uiLocation.lineNumber);
|
|
if (!breakpoints.size)
|
|
this._breakpointsForUISourceCode.remove(uiLocation.uiSourceCode);
|
|
this.dispatchEventToListeners(
|
|
Bindings.BreakpointManager.Events.BreakpointRemoved, {breakpoint: breakpoint, uiLocation: uiLocation});
|
|
}
|
|
};
|
|
|
|
/** @enum {symbol} */
|
|
Bindings.BreakpointManager.Events = {
|
|
BreakpointAdded: Symbol('breakpoint-added'),
|
|
BreakpointRemoved: Symbol('breakpoint-removed')
|
|
};
|
|
|
|
|
|
/**
|
|
* @unrestricted
|
|
* @implements {SDK.SDKModelObserver<!SDK.DebuggerModel>}
|
|
*/
|
|
Bindings.BreakpointManager.Breakpoint = class {
|
|
/**
|
|
* @param {!Bindings.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;
|
|
|
|
this.setPrimaryUISourceCode(primaryUISourceCode);
|
|
|
|
/** @type {!Map<string, number>} */
|
|
this._numberOfDebuggerLocationForUILocation = new Map();
|
|
|
|
/** @type {string} */ this._condition;
|
|
/** @type {boolean} */ this._enabled;
|
|
/** @type {boolean} */ this._isRemoved;
|
|
/** @type {!Workspace.UILocation|undefined} */ this._fakePrimaryLocation;
|
|
|
|
this._currentState = null;
|
|
/** @type {!Map.<!SDK.DebuggerModel, !Bindings.BreakpointManager.ModelBreakpoint>}*/
|
|
this._modelBreakpoints = new Map();
|
|
this._updateState(condition, enabled);
|
|
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 Bindings.BreakpointManager.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) {
|
|
const symbol = Bindings.BreakpointManager._breakpointsSymbol;
|
|
if (this._primaryUISourceCode)
|
|
this._primaryUISourceCode[symbol].delete(this);
|
|
this._primaryUISourceCode = primaryUISourceCode;
|
|
if (!primaryUISourceCode)
|
|
return;
|
|
if (!this._primaryUISourceCode[symbol])
|
|
this._primaryUISourceCode[symbol] = new Set();
|
|
this._primaryUISourceCode[symbol].add(this);
|
|
}
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
url() {
|
|
return this._url;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
lineNumber() {
|
|
return this._lineNumber;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
columnNumber() {
|
|
return this._columnNumber;
|
|
}
|
|
|
|
/**
|
|
* @param {?Workspace.UILocation} oldUILocation
|
|
* @param {!Workspace.UILocation} newUILocation
|
|
*/
|
|
_replaceUILocation(oldUILocation, newUILocation) {
|
|
if (this._isRemoved)
|
|
return;
|
|
|
|
this._removeUILocation(oldUILocation, true);
|
|
this._removeFakeBreakpointAtPrimaryLocation();
|
|
|
|
const current = (this._numberOfDebuggerLocationForUILocation.get(newUILocation.id()) || 0) + 1;
|
|
this._numberOfDebuggerLocationForUILocation.set(newUILocation.id(), current);
|
|
if (current === 1)
|
|
this._breakpointManager._uiLocationAdded(this, newUILocation);
|
|
}
|
|
|
|
/**
|
|
* @param {?Workspace.UILocation} uiLocation
|
|
* @param {boolean=} muteCreationFakeBreakpoint
|
|
*/
|
|
_removeUILocation(uiLocation, muteCreationFakeBreakpoint) {
|
|
if (!uiLocation || !this._numberOfDebuggerLocationForUILocation.has(uiLocation.id()))
|
|
return;
|
|
const current = (this._numberOfDebuggerLocationForUILocation.get(uiLocation.id()) || 0) - 1;
|
|
this._numberOfDebuggerLocationForUILocation.set(uiLocation.id(), current);
|
|
if (current !== 0)
|
|
return;
|
|
|
|
this._numberOfDebuggerLocationForUILocation.delete(uiLocation.id());
|
|
this._breakpointManager._uiLocationRemoved(this, uiLocation);
|
|
if (!muteCreationFakeBreakpoint)
|
|
this._fakeBreakpointAtPrimaryLocation();
|
|
}
|
|
|
|
/**
|
|
* @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() {
|
|
this._removeFakeBreakpointAtPrimaryLocation();
|
|
this._fakeBreakpointAtPrimaryLocation();
|
|
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;
|
|
this._removeFakeBreakpointAtPrimaryLocation();
|
|
const modelBreakpoints = this._modelBreakpoints.valuesArray();
|
|
for (let i = 0; i < modelBreakpoints.length; ++i) {
|
|
modelBreakpoints[i]._scheduleUpdateInDebugger();
|
|
modelBreakpoints[i]._removeEventListeners();
|
|
}
|
|
|
|
this.setPrimaryUISourceCode(null);
|
|
this._breakpointManager._removeBreakpoint(this, removeFromStorage);
|
|
this._breakpointManager._targetManager.unobserveModels(SDK.DebuggerModel, this);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
*/
|
|
_updateInDebuggerForModel(debuggerModel) {
|
|
this._modelBreakpoints.get(debuggerModel)._scheduleUpdateInDebugger();
|
|
}
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
_breakpointStorageId() {
|
|
return Bindings.BreakpointManager._breakpointStorageId(this._url, this._lineNumber, this._columnNumber);
|
|
}
|
|
|
|
_fakeBreakpointAtPrimaryLocation() {
|
|
if (this._isRemoved || this._numberOfDebuggerLocationForUILocation.size || this._fakePrimaryLocation)
|
|
return;
|
|
|
|
if (!this._primaryUISourceCode)
|
|
return;
|
|
|
|
this._fakePrimaryLocation = this._primaryUISourceCode.uiLocation(this._lineNumber, this._columnNumber);
|
|
if (this._fakePrimaryLocation)
|
|
this._breakpointManager._uiLocationAdded(this, this._fakePrimaryLocation);
|
|
}
|
|
|
|
_removeFakeBreakpointAtPrimaryLocation() {
|
|
if (this._fakePrimaryLocation) {
|
|
this._breakpointManager._uiLocationRemoved(this, this._fakePrimaryLocation);
|
|
delete this._fakePrimaryLocation;
|
|
}
|
|
}
|
|
|
|
_resetLocations() {
|
|
this.setPrimaryUISourceCode(null);
|
|
this._removeFakeBreakpointAtPrimaryLocation();
|
|
const modelBreakpoints = this._modelBreakpoints.valuesArray();
|
|
for (let i = 0; i < modelBreakpoints.length; ++i)
|
|
modelBreakpoints[i]._resetLocations();
|
|
}
|
|
};
|
|
|
|
Bindings.BreakpointManager._breakpointsSymbol = Symbol('breakpoints');
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
Bindings.BreakpointManager.ModelBreakpoint = class {
|
|
/**
|
|
* @param {!SDK.DebuggerModel} debuggerModel
|
|
* @param {!Bindings.BreakpointManager.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<string, !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._removeUILocation(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 uiSourceCode = this._breakpoint._primaryUISourceCode;
|
|
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 uiSourceCode = this._breakpoint._primaryUISourceCode;
|
|
const lineNumber = this._breakpoint._lineNumber;
|
|
const columnNumber = this._breakpoint._columnNumber;
|
|
const condition = this._breakpoint.condition();
|
|
|
|
let debuggerLocation = uiSourceCode &&
|
|
Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber);
|
|
if (debuggerLocation && debuggerLocation.debuggerModel !== this._debuggerModel)
|
|
debuggerLocation = null;
|
|
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 Bindings.BreakpointManager.Breakpoint.State(
|
|
script.sourceURL, null, null, debuggerLocation.lineNumber, debuggerLocation.columnNumber, condition);
|
|
} else {
|
|
newState = new Bindings.BreakpointManager.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 Bindings.BreakpointManager.Breakpoint.State(
|
|
position.url, null, null, position.lineNumber, position.columnNumber, condition);
|
|
} else if (uiSourceCode) {
|
|
newState = new Bindings.BreakpointManager.Breakpoint.State(
|
|
uiSourceCode.url(), null, null, lineNumber, columnNumber, condition);
|
|
}
|
|
if (this._debuggerId && Bindings.BreakpointManager.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 {!SDK.DebuggerModel.Location} location
|
|
* @param {!Bindings.LiveLocation} liveLocation
|
|
*/
|
|
_locationUpdated(location, liveLocation) {
|
|
const uiLocation = liveLocation.uiLocation();
|
|
if (!uiLocation)
|
|
return;
|
|
const oldUILocation = this._uiLocations.get(location.id()) || null;
|
|
this._uiLocations.set(location.id(), uiLocation);
|
|
this._breakpoint._replaceUILocation(oldUILocation, uiLocation);
|
|
}
|
|
|
|
/**
|
|
* @param {!SDK.DebuggerModel.Location} location
|
|
* @return {boolean}
|
|
*/
|
|
_addResolvedLocation(location) {
|
|
const uiLocation = this._debuggerWorkspaceBinding.rawLocationToUILocation(location);
|
|
if (!uiLocation)
|
|
return false;
|
|
const breakpoint = this._breakpoint._breakpointManager.findBreakpoint(
|
|
uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
|
|
if (breakpoint && breakpoint !== this._breakpoint) {
|
|
// location clash
|
|
this._breakpoint.remove(false /* keepInStorage */);
|
|
return false;
|
|
}
|
|
this._debuggerWorkspaceBinding.createLiveLocation(
|
|
location, this._locationUpdated.bind(this, location), 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);
|
|
}
|
|
};
|
|
|
|
Bindings.BreakpointManager.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 {?Bindings.BreakpointManager.Breakpoint.State|undefined} stateA
|
|
* @param {?Bindings.BreakpointManager.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;
|
|
}
|
|
};
|
|
|
|
|
|
Bindings.BreakpointManager.Storage = class {
|
|
constructor() {
|
|
this._setting = Common.settings.createLocalSetting('breakpoints', []);
|
|
/** @type {!Map<string, !Bindings.BreakpointManager.Storage.Item>} */
|
|
this._breakpoints = new Map();
|
|
const items = /** @type {!Array<!Bindings.BreakpointManager.Storage.Item>} */ (this._setting.get());
|
|
for (const item of items) {
|
|
item.columnNumber = item.columnNumber || 0;
|
|
this._breakpoints.set(
|
|
Bindings.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<!Bindings.BreakpointManager.Storage.Item>}
|
|
*/
|
|
breakpointItems(url) {
|
|
return Array.from(this._breakpoints.values()).filter(item => item.url === url);
|
|
}
|
|
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
|
|
*/
|
|
_updateBreakpoint(breakpoint) {
|
|
if (this._muted || !breakpoint._breakpointStorageId())
|
|
return;
|
|
this._breakpoints.set(breakpoint._breakpointStorageId(), new Bindings.BreakpointManager.Storage.Item(breakpoint));
|
|
this._save();
|
|
}
|
|
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.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
|
|
*/
|
|
Bindings.BreakpointManager.Storage.Item = class {
|
|
/**
|
|
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
|
|
*/
|
|
constructor(breakpoint) {
|
|
this.url = breakpoint._url;
|
|
this.lineNumber = breakpoint.lineNumber();
|
|
this.columnNumber = breakpoint.columnNumber();
|
|
this.condition = breakpoint.condition();
|
|
this.enabled = breakpoint.enabled();
|
|
}
|
|
};
|
|
|
|
/** @type {!Bindings.BreakpointManager} */
|
|
Bindings.breakpointManager;
|