1738 lines
68 KiB
JavaScript
1738 lines
68 KiB
JavaScript
/*
|
|
* Copyright (C) 2013-2016 Apple 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:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
|
|
*/
|
|
|
|
WI.DebuggerManager = class DebuggerManager extends WI.Object
|
|
{
|
|
constructor()
|
|
{
|
|
super();
|
|
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._breakpointDisabledStateDidChange, this);
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._breakpointEditablePropertyDidChange, this);
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._breakpointEditablePropertyDidChange, this);
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this);
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleBreakpointActionsDidChange, this);
|
|
WI.JavaScriptBreakpoint.addEventListener(WI.JavaScriptBreakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this);
|
|
|
|
WI.SymbolicBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleSymbolicBreakpointDisabledStateChanged, this);
|
|
WI.SymbolicBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._handleSymbolicBreakpointEditablePropertyChanged, this);
|
|
WI.SymbolicBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._handleSymbolicBreakpointEditablePropertyChanged, this);
|
|
WI.SymbolicBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._handleSymbolicBreakpointEditablePropertyChanged, this);
|
|
WI.SymbolicBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleSymbolicBreakpointActionsChanged, this);
|
|
|
|
WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
|
|
|
|
WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
|
|
WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
|
|
|
|
WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
|
|
|
|
WI.settings.blackboxBreakpointEvaluations.addEventListener(WI.Setting.Event.Changed, this._handleBlackboxBreakpointEvaluationsChange, this);
|
|
|
|
if (WI.engineeringSettingsAllowed()) {
|
|
WI.settings.engineeringShowInternalScripts.addEventListener(WI.Setting.Event.Changed, this._handleEngineeringShowInternalScriptsSettingChanged, this);
|
|
WI.settings.engineeringPauseForInternalScripts.addEventListener(WI.Setting.Event.Changed, this._handleEngineeringPauseForInternalScriptsSettingChanged, this);
|
|
}
|
|
|
|
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
|
|
|
|
this._breakpointsEnabledSetting = new WI.Setting("breakpoints-enabled", true);
|
|
this._asyncStackTraceDepthSetting = new WI.Setting("async-stack-trace-depth", 200);
|
|
|
|
this._debuggerStatementsBreakpointSetting = new WI.Setting("debugger-statements-breakpoint", {});
|
|
this._debuggerStatementsBreakpoint = null;
|
|
|
|
this._allExceptionsBreakpointSetting = new WI.Setting("all-exceptions-breakpoint", {disabled: true});
|
|
this._allExceptionsBreakpoint = null;
|
|
|
|
this._uncaughtExceptionsBreakpointSetting = new WI.Setting("uncaught-exceptions-breakpoint", {disabled: true});
|
|
this._uncaughtExceptionsBreakpoint = null;
|
|
|
|
this._assertionFailuresBreakpointSetting = new WI.Setting("assertion-failures-breakpoint", null);
|
|
if (WI.Setting.isFirstLaunch)
|
|
this._assertionFailuresBreakpointSetting.value = {disabled: true};
|
|
this._assertionFailuresBreakpoint = null;
|
|
|
|
this._allMicrotasksBreakpointSetting = new WI.Setting("all-microtasks-breakpoint", null);
|
|
this._allMicrotasksBreakpoint = null;
|
|
|
|
this._breakpoints = [];
|
|
this._breakpointContentIdentifierMap = new Multimap;
|
|
this._breakpointScriptIdentifierMap = new Multimap;
|
|
this._breakpointIdMap = new Map;
|
|
|
|
this._symbolicBreakpoints = [];
|
|
|
|
this._nextBreakpointActionIdentifier = 1;
|
|
|
|
this._blackboxedURLsSetting = new WI.Setting("debugger-blackboxed-urls", []);
|
|
this._blackboxedPatternsSetting = new WI.Setting("debugger-blackboxed-patterns", []);
|
|
this._blackboxedPatternDataMap = new Map;
|
|
this._blackboxedCallFrameGroupsToAutoExpand = [];
|
|
|
|
this._activeCallFrame = null;
|
|
|
|
this._internalWebKitScripts = [];
|
|
this._targetDebuggerDataMap = new Map;
|
|
|
|
// Used to detect deleted probe actions.
|
|
this._knownProbeIdentifiersForBreakpoint = new Map;
|
|
|
|
// Main lookup tables for probes and probe sets.
|
|
this._probesByIdentifier = new Map;
|
|
this._probeSetsByBreakpoint = new Map;
|
|
|
|
// Restore the correct breakpoints enabled setting if Web Inspector had
|
|
// previously been left in a state where breakpoints were temporarily disabled.
|
|
this._temporarilyDisabledBreakpointsRestoreSetting = new WI.Setting("temporarily-disabled-breakpoints-restore", null);
|
|
if (this._temporarilyDisabledBreakpointsRestoreSetting.value !== null) {
|
|
this._breakpointsEnabledSetting.value = this._temporarilyDisabledBreakpointsRestoreSetting.value;
|
|
this._temporarilyDisabledBreakpointsRestoreSetting.value = null;
|
|
}
|
|
this._temporarilyDisableBreakpointsRequestCount = 0;
|
|
|
|
this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
|
|
|
|
WI.Target.registerInitializationPromise((async () => {
|
|
let existingSerializedBreakpoints = WI.Setting.migrateValue("breakpoints");
|
|
if (existingSerializedBreakpoints) {
|
|
for (let existingSerializedBreakpoint of existingSerializedBreakpoints)
|
|
await WI.objectStores.breakpoints.putObject(WI.JavaScriptBreakpoint.fromJSON(existingSerializedBreakpoint));
|
|
}
|
|
|
|
let serializedBreakpoints = await WI.objectStores.breakpoints.getAll();
|
|
|
|
this._restoringBreakpoints = true;
|
|
for (let serializedBreakpoint of serializedBreakpoints) {
|
|
let breakpoint = WI.JavaScriptBreakpoint.fromJSON(serializedBreakpoint);
|
|
|
|
const key = null;
|
|
WI.objectStores.breakpoints.associateObject(breakpoint, key, serializedBreakpoint);
|
|
|
|
this.addBreakpoint(breakpoint);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
})());
|
|
|
|
if (WI.SymbolicBreakpoint.supported()) {
|
|
WI.Target.registerInitializationPromise((async () => {
|
|
let serializedSymbolicBreakpoints = await WI.objectStores.symbolicBreakpoints.getAll();
|
|
|
|
this._restoringBreakpoints = true;
|
|
for (let serializedSymbolicBreakpoint of serializedSymbolicBreakpoints) {
|
|
let symbolicBreakpoint = WI.SymbolicBreakpoint.fromJSON(serializedSymbolicBreakpoint);
|
|
|
|
const key = null;
|
|
WI.objectStores.symbolicBreakpoints.associateObject(symbolicBreakpoint, key, serializedSymbolicBreakpoint);
|
|
|
|
this.addSymbolicBreakpoint(symbolicBreakpoint);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
})());
|
|
}
|
|
|
|
WI.Target.registerInitializationPromise((async () => {
|
|
// Wait one microtask so that `WI.debuggerManager` can be initialized.
|
|
await new Promise((resolve, reject) => queueMicrotask(resolve));
|
|
|
|
let loadSpecialBreakpoint = (setting, enabledSettingsKey, shownSettingsKey) => {
|
|
let serializedBreakpoint = setting.value;
|
|
|
|
if (!serializedBreakpoint && (!shownSettingsKey || WI.Setting.migrateValue(shownSettingsKey))) {
|
|
serializedBreakpoint = setting.value = {};
|
|
setting.save();
|
|
}
|
|
|
|
if (WI.Setting.migrateValue(enabledSettingsKey)) {
|
|
if (!serializedBreakpoint)
|
|
serializedBreakpoint = setting.value = {};
|
|
serializedBreakpoint.disabled = false;
|
|
setting.save();
|
|
}
|
|
|
|
if (!serializedBreakpoint)
|
|
return null;
|
|
|
|
return this._createSpecialBreakpoint(serializedBreakpoint);
|
|
};
|
|
|
|
this._restoringBreakpoints = true;
|
|
|
|
if (WI.JavaScriptBreakpoint.supportsDebuggerStatements()) {
|
|
this._debuggerStatementsBreakpoint = loadSpecialBreakpoint(this._debuggerStatementsBreakpointSetting, "break-on-debugger-statements");
|
|
if (this._debuggerStatementsBreakpoint)
|
|
this.addBreakpoint(this._debuggerStatementsBreakpoint);
|
|
}
|
|
|
|
this._allExceptionsBreakpoint = loadSpecialBreakpoint(this._allExceptionsBreakpointSetting, "break-on-all-exceptions");
|
|
if (this._allExceptionsBreakpoint)
|
|
this.addBreakpoint(this._allExceptionsBreakpoint);
|
|
|
|
this._uncaughtExceptionsBreakpoint = loadSpecialBreakpoint(this._uncaughtExceptionsBreakpointSetting, "break-on-uncaught-exceptions");
|
|
if (this._uncaughtExceptionsBreakpoint)
|
|
this.addBreakpoint(this._uncaughtExceptionsBreakpoint);
|
|
|
|
this._assertionFailuresBreakpoint = loadSpecialBreakpoint(this._assertionFailuresBreakpointSetting, "break-on-assertion-failures", "show-assertion-failures-breakpoint");
|
|
if (this._assertionFailuresBreakpoint)
|
|
this.addBreakpoint(this._assertionFailuresBreakpoint);
|
|
|
|
if (WI.JavaScriptBreakpoint.supportsMicrotasks()) {
|
|
this._allMicrotasksBreakpoint = loadSpecialBreakpoint(this._allMicrotasksBreakpointSetting, "break-on-all-microtasks", "show-all-microtasks-breakpoint");
|
|
if (this._allMicrotasksBreakpoint)
|
|
this.addBreakpoint(this._allMicrotasksBreakpoint);
|
|
}
|
|
|
|
this._restoringBreakpoints = false;
|
|
})());
|
|
}
|
|
|
|
// Target
|
|
|
|
initializeTarget(target)
|
|
{
|
|
let targetData = this.dataForTarget(target);
|
|
|
|
// Initialize global state.
|
|
target.DebuggerAgent.enable();
|
|
target.DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value);
|
|
|
|
if (WI.SymbolicBreakpoint.supported(target)) {
|
|
for (let breakpoint of this._symbolicBreakpoints) {
|
|
if (!breakpoint.disabled)
|
|
this._setSymbolicBreakpoint(breakpoint, target);
|
|
}
|
|
}
|
|
|
|
if (this._debuggerStatementsBreakpoint)
|
|
this._updateSpecialBreakpoint(this._debuggerStatementsBreakpoint, target);
|
|
|
|
this._setPauseOnExceptions(target);
|
|
|
|
if (this._assertionFailuresBreakpoint)
|
|
this._updateSpecialBreakpoint(this._assertionFailuresBreakpoint, target);
|
|
|
|
if (this._allMicrotasksBreakpoint)
|
|
this._updateSpecialBreakpoint(this._allMicrotasksBreakpoint, target);
|
|
|
|
target.DebuggerAgent.setAsyncStackTraceDepth(this._asyncStackTraceDepthSetting.value);
|
|
|
|
// COMPATIBILITY (iOS 13): Debugger.setShouldBlackboxURL did not exist yet.
|
|
if (target.hasCommand("Debugger.setShouldBlackboxURL")) {
|
|
const shouldBlackbox = true;
|
|
|
|
{
|
|
const caseSensitive = true;
|
|
for (let url of this._blackboxedURLsSetting.value)
|
|
target.DebuggerAgent.setShouldBlackboxURL(url, shouldBlackbox, caseSensitive);
|
|
}
|
|
|
|
{
|
|
const isRegex = true;
|
|
for (let data of this._blackboxedPatternsSetting.value) {
|
|
this._blackboxedPatternDataMap.set(new RegExp(data.url, !data.caseSensitive ? "i" : ""), data);
|
|
target.DebuggerAgent.setShouldBlackboxURL(data.url, shouldBlackbox, data.caseSensitive, isRegex);
|
|
}
|
|
}
|
|
}
|
|
|
|
this._setBlackboxBreakpointEvaluations(target);
|
|
|
|
if (WI.engineeringSettingsAllowed()) {
|
|
// COMPATIBILITY (iOS 12): DebuggerAgent.setPauseForInternalScripts did not exist yet.
|
|
if (target.hasCommand("Debugger.setPauseForInternalScripts"))
|
|
target.DebuggerAgent.setPauseForInternalScripts(WI.settings.engineeringPauseForInternalScripts.value);
|
|
}
|
|
|
|
if (this.paused)
|
|
targetData.pauseIfNeeded();
|
|
|
|
// Initialize breakpoints.
|
|
this._restoringBreakpoints = true;
|
|
for (let breakpoint of this._breakpoints) {
|
|
if (breakpoint.disabled)
|
|
continue;
|
|
if (!breakpoint.contentIdentifier)
|
|
continue;
|
|
this._setBreakpoint(breakpoint, target);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
|
|
// Static
|
|
|
|
static supportsBlackboxingScripts()
|
|
{
|
|
// COMPATIBILITY (iOS 13): Debugger.setShouldBlackboxURL did not exist yet.
|
|
return InspectorBackend.hasCommand("Debugger.setShouldBlackboxURL");
|
|
}
|
|
|
|
static supportsBlackboxingBreakpointEvaluations()
|
|
{
|
|
// COMPATIBILITY (macOS 12.3, iOS 15.4): Debugger.setBlackboxBreakpointEvaluations did not exist yet.
|
|
return InspectorBackend.hasCommand("Debugger.setBlackboxBreakpointEvaluations");
|
|
}
|
|
|
|
static pauseReasonFromPayload(payload)
|
|
{
|
|
switch (payload) {
|
|
case InspectorBackend.Enum.Debugger.PausedReason.AnimationFrame:
|
|
return WI.DebuggerManager.PauseReason.AnimationFrame;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Assert:
|
|
return WI.DebuggerManager.PauseReason.Assertion;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.BlackboxedScript:
|
|
return WI.DebuggerManager.PauseReason.BlackboxedScript;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Breakpoint:
|
|
return WI.DebuggerManager.PauseReason.Breakpoint;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.CSPViolation:
|
|
return WI.DebuggerManager.PauseReason.CSPViolation;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.DOM:
|
|
return WI.DebuggerManager.PauseReason.DOM;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.DebuggerStatement:
|
|
return WI.DebuggerManager.PauseReason.DebuggerStatement;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.EventListener:
|
|
return WI.DebuggerManager.PauseReason.EventListener;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Exception:
|
|
return WI.DebuggerManager.PauseReason.Exception;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.FunctionCall:
|
|
return WI.DebuggerManager.PauseReason.FunctionCall;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Interval:
|
|
return WI.DebuggerManager.PauseReason.Interval;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Listener:
|
|
return WI.DebuggerManager.PauseReason.Listener;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Microtask:
|
|
return WI.DebuggerManager.PauseReason.Microtask;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.PauseOnNextStatement:
|
|
return WI.DebuggerManager.PauseReason.PauseOnNextStatement;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Timeout:
|
|
return WI.DebuggerManager.PauseReason.Timeout;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Timer:
|
|
return WI.DebuggerManager.PauseReason.Timer;
|
|
case InspectorBackend.Enum.Debugger.PausedReason.URL:
|
|
case InspectorBackend.Enum.Debugger.PausedReason.Fetch: // COMPATIBILITY (macOS 13.0, iOS 16.0): Debugger.paused.reason.Fetch was replaced by Debugger.paused.reason.URL
|
|
case InspectorBackend.Enum.Debugger.PausedReason.XHR: // COMPATIBILITY (macOS 13.0, iOS 16.0): Debugger.paused.reason.XHR was replaced by Debugger.paused.reason.URL
|
|
return WI.DebuggerManager.PauseReason.URL;
|
|
default:
|
|
return WI.DebuggerManager.PauseReason.Other;
|
|
}
|
|
}
|
|
|
|
// Public
|
|
|
|
get paused()
|
|
{
|
|
for (let [target, targetData] of this._targetDebuggerDataMap) {
|
|
if (targetData.paused)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
get activeCallFrame()
|
|
{
|
|
return this._activeCallFrame;
|
|
}
|
|
|
|
set activeCallFrame(callFrame)
|
|
{
|
|
if (callFrame === this._activeCallFrame)
|
|
return;
|
|
|
|
this._activeCallFrame = callFrame || null;
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
|
|
}
|
|
|
|
dataForTarget(target)
|
|
{
|
|
let targetData = this._targetDebuggerDataMap.get(target);
|
|
if (targetData)
|
|
return targetData;
|
|
|
|
targetData = new WI.DebuggerData(target);
|
|
this._targetDebuggerDataMap.set(target, targetData);
|
|
return targetData;
|
|
}
|
|
|
|
get debuggerStatementsBreakpoint() { return this._debuggerStatementsBreakpoint; }
|
|
get allExceptionsBreakpoint() { return this._allExceptionsBreakpoint; }
|
|
get uncaughtExceptionsBreakpoint() { return this._uncaughtExceptionsBreakpoint; }
|
|
get assertionFailuresBreakpoint() { return this._assertionFailuresBreakpoint; }
|
|
get allMicrotasksBreakpoint() { return this._allMicrotasksBreakpoint; }
|
|
get breakpoints() { return this._breakpoints; }
|
|
|
|
createAssertionFailuresBreakpoint(options = {})
|
|
{
|
|
console.assert(!this._assertionFailuresBreakpoint);
|
|
|
|
this._assertionFailuresBreakpoint = this._createSpecialBreakpoint(options);
|
|
this.addBreakpoint(this._assertionFailuresBreakpoint);
|
|
}
|
|
|
|
createAllMicrotasksBreakpoint(options = {})
|
|
{
|
|
console.assert(!this._allMicrotasksBreakpoint);
|
|
|
|
this._allMicrotasksBreakpoint = this._createSpecialBreakpoint(options);
|
|
this.addBreakpoint(this._allMicrotasksBreakpoint);
|
|
}
|
|
|
|
breakpointForIdentifier(id)
|
|
{
|
|
return this._breakpointIdMap.get(id) || null;
|
|
}
|
|
|
|
breakpointsForSourceCode(sourceCode)
|
|
{
|
|
console.assert(sourceCode instanceof WI.Resource || sourceCode instanceof WI.Script);
|
|
|
|
if (sourceCode instanceof WI.SourceMapResource)
|
|
return Array.from(this.breakpointsForSourceCode(sourceCode.sourceMap.originalSourceCode)).filter((breakpoint) => breakpoint.sourceCodeLocation.displaySourceCode === sourceCode);
|
|
|
|
let contentIdentifierBreakpoints = this._breakpointContentIdentifierMap.get(sourceCode.contentIdentifier);
|
|
if (contentIdentifierBreakpoints) {
|
|
this._associateBreakpointsWithSourceCode(contentIdentifierBreakpoints, sourceCode);
|
|
return contentIdentifierBreakpoints;
|
|
}
|
|
|
|
if (sourceCode instanceof WI.Script) {
|
|
let scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap.get(sourceCode.id);
|
|
if (scriptIdentifierBreakpoints) {
|
|
this._associateBreakpointsWithSourceCode(scriptIdentifierBreakpoints, sourceCode);
|
|
return scriptIdentifierBreakpoints;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
breakpointsForSourceCodeLocation(sourceCodeLocation)
|
|
{
|
|
console.assert(sourceCodeLocation instanceof WI.SourceCodeLocation, sourceCodeLocation);
|
|
|
|
return this.breakpointsForSourceCode(sourceCodeLocation.sourceCode)
|
|
.filter((breakpoint) => breakpoint.hasResolvedLocation(sourceCodeLocation));
|
|
}
|
|
|
|
breakpointForSourceCodeLocation(sourceCodeLocation)
|
|
{
|
|
console.assert(sourceCodeLocation instanceof WI.SourceCodeLocation);
|
|
|
|
for (let breakpoint of this.breakpointsForSourceCode(sourceCodeLocation.sourceCode)) {
|
|
if (breakpoint.sourceCodeLocation.isEqual(sourceCodeLocation))
|
|
return breakpoint;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
symbolicBreakpointsForSymbol(symbol)
|
|
{
|
|
console.assert(WI.SymbolicBreakpoint.supported());
|
|
|
|
// Order symbolic breakpoints based on how closely they match the given symbol. As an example,
|
|
// a regular expression is likely going to match more symbols than a case-insensitive string.
|
|
const rankFunctions = [
|
|
(breakpoint) => breakpoint.caseSensitive && !breakpoint.isRegex, // exact match
|
|
(breakpoint) => !breakpoint.caseSensitive && !breakpoint.isRegex, // case-insensitive
|
|
(breakpoint) => breakpoint.caseSensitive && breakpoint.isRegex, // case-sensitive regex
|
|
(breakpoint) => !breakpoint.caseSensitive && breakpoint.isRegex, // case-insensitive regex
|
|
];
|
|
return this._symbolicBreakpoints
|
|
.filter((breakpoint) => breakpoint.matches(symbol))
|
|
.sort((a, b) => {
|
|
let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
|
|
let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
|
|
return aRank - bRank;
|
|
});
|
|
}
|
|
|
|
get breakpointsEnabled()
|
|
{
|
|
return this._breakpointsEnabledSetting.value;
|
|
}
|
|
|
|
set breakpointsEnabled(enabled)
|
|
{
|
|
if (this._breakpointsEnabledSetting.value === enabled)
|
|
return;
|
|
|
|
console.assert(!(enabled && this.breakpointsDisabledTemporarily), "Should not enable breakpoints when we are temporarily disabling breakpoints.");
|
|
if (enabled && this.breakpointsDisabledTemporarily)
|
|
return;
|
|
|
|
this._breakpointsEnabledSetting.value = enabled;
|
|
|
|
for (let target of WI.targets) {
|
|
target.DebuggerAgent.setBreakpointsActive(enabled);
|
|
this._setPauseOnExceptions(target);
|
|
}
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointsEnabledDidChange);
|
|
}
|
|
|
|
get breakpointsDisabledTemporarily()
|
|
{
|
|
return this._temporarilyDisabledBreakpointsRestoreSetting.value !== null;
|
|
}
|
|
|
|
scriptForIdentifier(id, target)
|
|
{
|
|
console.assert(target instanceof WI.Target);
|
|
return this.dataForTarget(target).scriptForIdentifier(id);
|
|
}
|
|
|
|
scriptsForURL(url, target)
|
|
{
|
|
// FIXME: This may not be safe. A Resource's URL may differ from a Script's URL.
|
|
console.assert(target instanceof WI.Target);
|
|
return this.dataForTarget(target).scriptsForURL(url);
|
|
}
|
|
|
|
get searchableScripts()
|
|
{
|
|
return this.knownNonResourceScripts.filter((script) => !!script.contentIdentifier);
|
|
}
|
|
|
|
get knownNonResourceScripts()
|
|
{
|
|
let knownScripts = [];
|
|
|
|
for (let targetData of this._targetDebuggerDataMap.values()) {
|
|
for (let script of targetData.scripts) {
|
|
if (script.resource)
|
|
continue;
|
|
if (!WI.settings.debugShowConsoleEvaluations.value && isWebInspectorConsoleEvaluationScript(script.sourceURL))
|
|
continue;
|
|
if (!WI.settings.engineeringShowInternalScripts.value && isWebKitInternalScript(script.sourceURL))
|
|
continue;
|
|
knownScripts.push(script);
|
|
}
|
|
}
|
|
|
|
return knownScripts;
|
|
}
|
|
|
|
blackboxDataForSourceCode(sourceCode)
|
|
{
|
|
for (let regex of this._blackboxedPatternDataMap.keys()) {
|
|
if (regex.test(sourceCode.contentIdentifier))
|
|
return {type: DebuggerManager.BlackboxType.Pattern, regex};
|
|
}
|
|
|
|
if (this._blackboxedURLsSetting.value.includes(sourceCode.contentIdentifier))
|
|
return {type: DebuggerManager.BlackboxType.URL};
|
|
|
|
return null;
|
|
}
|
|
|
|
get blackboxPatterns()
|
|
{
|
|
return Array.from(this._blackboxedPatternDataMap.keys());
|
|
}
|
|
|
|
setShouldBlackboxScript(sourceCode, shouldBlackbox)
|
|
{
|
|
console.assert(DebuggerManager.supportsBlackboxingScripts());
|
|
console.assert(sourceCode instanceof WI.SourceCode);
|
|
console.assert(sourceCode.contentIdentifier);
|
|
console.assert(!isWebKitInjectedScript(sourceCode.contentIdentifier));
|
|
console.assert(shouldBlackbox !== ((this.blackboxDataForSourceCode(sourceCode) || {}).type === DebuggerManager.BlackboxType.URL));
|
|
|
|
this._blackboxedURLsSetting.value.toggleIncludes(sourceCode.contentIdentifier, shouldBlackbox);
|
|
this._blackboxedURLsSetting.save();
|
|
|
|
const caseSensitive = true;
|
|
for (let target of WI.targets) {
|
|
// COMPATIBILITY (iOS 13): Debugger.setShouldBlackboxURL did not exist yet.
|
|
if (target.hasCommand("Debugger.setShouldBlackboxURL"))
|
|
target.DebuggerAgent.setShouldBlackboxURL(sourceCode.contentIdentifier, !!shouldBlackbox, caseSensitive);
|
|
}
|
|
|
|
this.dispatchEventToListeners(DebuggerManager.Event.BlackboxChanged);
|
|
}
|
|
|
|
setShouldBlackboxPattern(regex, shouldBlackbox)
|
|
{
|
|
console.assert(DebuggerManager.supportsBlackboxingScripts());
|
|
console.assert(regex instanceof RegExp);
|
|
|
|
if (shouldBlackbox) {
|
|
console.assert(!this._blackboxedPatternDataMap.has(regex));
|
|
|
|
let data = {
|
|
url: regex.source,
|
|
caseSensitive: !regex.ignoreCase,
|
|
};
|
|
this._blackboxedPatternDataMap.set(regex, data);
|
|
this._blackboxedPatternsSetting.value.push(data);
|
|
} else {
|
|
console.assert(this._blackboxedPatternDataMap.has(regex));
|
|
this._blackboxedPatternsSetting.value.remove(this._blackboxedPatternDataMap.take(regex));
|
|
}
|
|
|
|
this._blackboxedPatternsSetting.save();
|
|
|
|
const isRegex = true;
|
|
for (let target of WI.targets) {
|
|
// COMPATIBILITY (iOS 13): Debugger.setShouldBlackboxURL did not exist yet.
|
|
if (target.hasCommand("Debugger.setShouldBlackboxURL"))
|
|
target.DebuggerAgent.setShouldBlackboxURL(regex.source, !!shouldBlackbox, !regex.ignoreCase, isRegex);
|
|
}
|
|
|
|
this.dispatchEventToListeners(DebuggerManager.Event.BlackboxChanged);
|
|
}
|
|
|
|
rememberBlackboxedCallFrameGroupToAutoExpand(blackboxedCallFrameGroup)
|
|
{
|
|
console.assert(!this.shouldAutoExpandBlackboxedCallFrameGroup(blackboxedCallFrameGroup), blackboxedCallFrameGroup);
|
|
|
|
this._blackboxedCallFrameGroupsToAutoExpand.push(blackboxedCallFrameGroup);
|
|
}
|
|
|
|
shouldAutoExpandBlackboxedCallFrameGroup(blackboxedCallFrameGroup)
|
|
{
|
|
console.assert(Array.isArray(blackboxedCallFrameGroup) && blackboxedCallFrameGroup.length && blackboxedCallFrameGroup.every((callFrame) => callFrame instanceof WI.CallFrame && callFrame.blackboxed), blackboxedCallFrameGroup);
|
|
|
|
return this._blackboxedCallFrameGroupsToAutoExpand.some((blackboxedCallFrameGroupToAutoExpand) => {
|
|
if (blackboxedCallFrameGroupToAutoExpand.length !== blackboxedCallFrameGroup.length)
|
|
return false;
|
|
|
|
return blackboxedCallFrameGroupToAutoExpand.every((item, i) => item.isEqual(blackboxedCallFrameGroup[i]));
|
|
});
|
|
}
|
|
|
|
get asyncStackTraceDepth()
|
|
{
|
|
return this._asyncStackTraceDepthSetting.value;
|
|
}
|
|
|
|
set asyncStackTraceDepth(x)
|
|
{
|
|
if (this._asyncStackTraceDepthSetting.value === x)
|
|
return;
|
|
|
|
this._asyncStackTraceDepthSetting.value = x;
|
|
|
|
for (let target of WI.targets)
|
|
target.DebuggerAgent.setAsyncStackTraceDepth(this._asyncStackTraceDepthSetting.value);
|
|
}
|
|
|
|
get probeSets()
|
|
{
|
|
return [...this._probeSetsByBreakpoint.values()];
|
|
}
|
|
|
|
probeForIdentifier(identifier)
|
|
{
|
|
return this._probesByIdentifier.get(identifier);
|
|
}
|
|
|
|
pause()
|
|
{
|
|
if (this.paused)
|
|
return Promise.resolve();
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.WaitingToPause);
|
|
|
|
let promises = [this.awaitEvent(WI.DebuggerManager.Event.Paused, this)];
|
|
for (let targetData of this._targetDebuggerDataMap.values())
|
|
promises.push(targetData.pauseIfNeeded());
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
resume()
|
|
{
|
|
if (!this.paused)
|
|
return Promise.resolve();
|
|
|
|
let promises = [this.awaitEvent(WI.DebuggerManager.Event.Resumed, this)];
|
|
for (let targetData of this._targetDebuggerDataMap.values())
|
|
promises.push(targetData.resumeIfNeeded());
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
stepNext()
|
|
{
|
|
if (!this.paused)
|
|
return Promise.reject(new Error("Cannot step next because debugger is not paused."));
|
|
|
|
return Promise.all([
|
|
this.awaitEvent(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this),
|
|
this._activeCallFrame.target.DebuggerAgent.stepNext(),
|
|
]);
|
|
}
|
|
|
|
stepOver()
|
|
{
|
|
if (!this.paused)
|
|
return Promise.reject(new Error("Cannot step over because debugger is not paused."));
|
|
|
|
return Promise.all([
|
|
this.awaitEvent(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this),
|
|
this._activeCallFrame.target.DebuggerAgent.stepOver(),
|
|
]);
|
|
}
|
|
|
|
stepInto()
|
|
{
|
|
if (!this.paused)
|
|
return Promise.reject(new Error("Cannot step into because debugger is not paused."));
|
|
|
|
return Promise.all([
|
|
this.awaitEvent(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this),
|
|
this._activeCallFrame.target.DebuggerAgent.stepInto(),
|
|
]);
|
|
}
|
|
|
|
stepOut()
|
|
{
|
|
if (!this.paused)
|
|
return Promise.reject(new Error("Cannot step out because debugger is not paused."));
|
|
|
|
return Promise.all([
|
|
this.awaitEvent(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this),
|
|
this._activeCallFrame.target.DebuggerAgent.stepOut(),
|
|
]);
|
|
}
|
|
|
|
continueUntilNextRunLoop(target)
|
|
{
|
|
return this.dataForTarget(target).continueUntilNextRunLoop();
|
|
}
|
|
|
|
continueToLocation(script, lineNumber, columnNumber)
|
|
{
|
|
return script.target.DebuggerAgent.continueToLocation({scriptId: script.id, lineNumber, columnNumber});
|
|
}
|
|
|
|
addBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.JavaScriptBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return;
|
|
|
|
if (breakpoint.special)
|
|
this._updateSpecialBreakpoint(breakpoint);
|
|
else {
|
|
if (breakpoint.contentIdentifier)
|
|
this._breakpointContentIdentifierMap.add(breakpoint.contentIdentifier, breakpoint);
|
|
|
|
if (breakpoint.scriptIdentifier)
|
|
this._breakpointScriptIdentifierMap.add(breakpoint.scriptIdentifier, breakpoint);
|
|
|
|
this._breakpoints.push(breakpoint);
|
|
|
|
if (!breakpoint.disabled)
|
|
this._setBreakpoint(breakpoint);
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.breakpoints.putObject(breakpoint);
|
|
}
|
|
|
|
this.addProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointAdded, {breakpoint});
|
|
}
|
|
|
|
removeBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.JavaScriptBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return;
|
|
|
|
console.assert(breakpoint.removable, breakpoint);
|
|
if (!breakpoint.removable)
|
|
return;
|
|
|
|
let special = breakpoint.special;
|
|
|
|
if (!special) {
|
|
this._breakpoints.remove(breakpoint);
|
|
|
|
if (breakpoint.identifier)
|
|
this._removeBreakpoint(breakpoint);
|
|
|
|
if (breakpoint.contentIdentifier)
|
|
this._breakpointContentIdentifierMap.delete(breakpoint.contentIdentifier, breakpoint);
|
|
|
|
if (breakpoint.scriptIdentifier)
|
|
this._breakpointScriptIdentifierMap.delete(breakpoint.scriptIdentifier, breakpoint);
|
|
}
|
|
|
|
// Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
|
|
breakpoint.disabled = true;
|
|
breakpoint.clearActions();
|
|
|
|
if (special) {
|
|
switch (breakpoint) {
|
|
case this._assertionFailuresBreakpoint:
|
|
this._assertionFailuresBreakpointSetting.reset();
|
|
this._assertionFailuresBreakpoint = null;
|
|
break;
|
|
|
|
case this._allMicrotasksBreakpoint:
|
|
this._allMicrotasksBreakpointSetting.reset();
|
|
this._allMicrotasksBreakpoint = null;
|
|
break;
|
|
}
|
|
} else if (!this._restoringBreakpoints)
|
|
WI.objectStores.breakpoints.deleteObject(breakpoint);
|
|
|
|
this.removeProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointRemoved, {breakpoint});
|
|
}
|
|
|
|
addSymbolicBreakpoint(breakpoint)
|
|
{
|
|
console.assert(WI.SymbolicBreakpoint.supported());
|
|
console.assert(breakpoint instanceof WI.SymbolicBreakpoint, breakpoint);
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
|
|
if (this._symbolicBreakpoints.some((existingBreakpoint) => existingBreakpoint.equals(breakpoint)))
|
|
return false;
|
|
|
|
this._symbolicBreakpoints.push(breakpoint);
|
|
|
|
if (!breakpoint.disabled) {
|
|
for (let target of WI.targets)
|
|
this._setSymbolicBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.symbolicBreakpoints.putObject(breakpoint);
|
|
|
|
this.addProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.SymbolicBreakpointAdded, {breakpoint});
|
|
|
|
return true;
|
|
}
|
|
|
|
removeSymbolicBreakpoint(breakpoint)
|
|
{
|
|
console.assert(WI.SymbolicBreakpoint.supported());
|
|
console.assert(breakpoint instanceof WI.SymbolicBreakpoint, breakpoint);
|
|
console.assert(breakpoint.removable, breakpoint);
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
|
|
// Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
|
|
breakpoint.disabled = true;
|
|
breakpoint.clearActions();
|
|
|
|
this._symbolicBreakpoints.remove(breakpoint);
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.symbolicBreakpoints.deleteObject(breakpoint);
|
|
|
|
this.removeProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.SymbolicBreakpointRemoved, {breakpoint});
|
|
}
|
|
|
|
nextBreakpointActionIdentifier()
|
|
{
|
|
return this._nextBreakpointActionIdentifier++;
|
|
}
|
|
|
|
addProbesForBreakpoint(breakpoint)
|
|
{
|
|
if (this._knownProbeIdentifiersForBreakpoint.has(breakpoint))
|
|
return;
|
|
|
|
this._knownProbeIdentifiersForBreakpoint.set(breakpoint, new Set);
|
|
|
|
this.updateProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
removeProbesForBreakpoint(breakpoint)
|
|
{
|
|
console.assert(this._knownProbeIdentifiersForBreakpoint.has(breakpoint));
|
|
|
|
this.updateProbesForBreakpoint(breakpoint);
|
|
this._knownProbeIdentifiersForBreakpoint.delete(breakpoint);
|
|
}
|
|
|
|
updateProbesForBreakpoint(breakpoint)
|
|
{
|
|
let knownProbeIdentifiers = this._knownProbeIdentifiersForBreakpoint.get(breakpoint);
|
|
if (!knownProbeIdentifiers) {
|
|
// Sometimes actions change before the added breakpoint is fully dispatched.
|
|
this.addProbesForBreakpoint(breakpoint);
|
|
return;
|
|
}
|
|
|
|
let seenProbeIdentifiers = new Set;
|
|
|
|
for (let probeAction of breakpoint.probeActions) {
|
|
let probeIdentifier = probeAction.id;
|
|
console.assert(probeIdentifier, "Probe added without breakpoint action identifier: ", breakpoint);
|
|
|
|
seenProbeIdentifiers.add(probeIdentifier);
|
|
if (!knownProbeIdentifiers.has(probeIdentifier)) {
|
|
// New probe; find or create relevant probe set.
|
|
knownProbeIdentifiers.add(probeIdentifier);
|
|
let probeSet = this._probeSetForBreakpoint(breakpoint);
|
|
let newProbe = new WI.Probe(probeIdentifier, breakpoint, probeAction.data);
|
|
this._probesByIdentifier.set(probeIdentifier, newProbe);
|
|
probeSet.addProbe(newProbe);
|
|
break;
|
|
}
|
|
|
|
let probe = this._probesByIdentifier.get(probeIdentifier);
|
|
console.assert(probe, "Probe known but couldn't be found by identifier: ", probeIdentifier);
|
|
// Update probe expression; if it differed, change events will fire.
|
|
probe.expression = probeAction.data;
|
|
}
|
|
|
|
// Look for missing probes based on what we saw last.
|
|
for (let probeIdentifier of knownProbeIdentifiers) {
|
|
if (seenProbeIdentifiers.has(probeIdentifier))
|
|
break;
|
|
|
|
// The probe has gone missing, remove it.
|
|
let probeSet = this._probeSetForBreakpoint(breakpoint);
|
|
let probe = this._probesByIdentifier.get(probeIdentifier);
|
|
this._probesByIdentifier.delete(probeIdentifier);
|
|
knownProbeIdentifiers.delete(probeIdentifier);
|
|
probeSet.removeProbe(probe);
|
|
|
|
// Remove the probe set if it has become empty.
|
|
if (!probeSet.probes.length) {
|
|
this._probeSetsByBreakpoint.delete(probeSet.breakpoint);
|
|
probeSet.willRemove();
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ProbeSetRemoved, {probeSet});
|
|
}
|
|
}
|
|
}
|
|
|
|
// DebuggerObserver
|
|
|
|
breakpointResolved(target, breakpointIdentifier, location)
|
|
{
|
|
let breakpoint = this._breakpointIdMap.get(breakpointIdentifier);
|
|
console.assert(breakpoint);
|
|
if (!breakpoint)
|
|
return;
|
|
|
|
console.assert(breakpoint.identifier === breakpointIdentifier);
|
|
|
|
let sourceCodeLocation = this._sourceCodeLocationFromPayload(target, location);
|
|
|
|
if (!breakpoint.sourceCodeLocation.sourceCode)
|
|
breakpoint.sourceCodeLocation.sourceCode = sourceCodeLocation.sourceCode;
|
|
|
|
breakpoint.addResolvedLocation(sourceCodeLocation);
|
|
}
|
|
|
|
globalObjectCleared(target)
|
|
{
|
|
let wasPaused = this.paused;
|
|
|
|
WI.Script.resetUniqueDisplayNameNumbers(target);
|
|
|
|
this._internalWebKitScripts = [];
|
|
this._targetDebuggerDataMap.clear();
|
|
|
|
this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
|
|
|
|
// Mark all the breakpoints as unresolved. They will be reported as resolved when
|
|
// breakpointResolved is called as the page loads.
|
|
for (let breakpoint of this._breakpoints) {
|
|
breakpoint.clearResolvedLocations();
|
|
|
|
if (breakpoint.sourceCodeLocation.sourceCode)
|
|
breakpoint.sourceCodeLocation.sourceCode = null;
|
|
}
|
|
|
|
this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ScriptsCleared);
|
|
|
|
if (wasPaused)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
|
|
}
|
|
|
|
debuggerDidPause(target, callFramesPayload, reason, data, asyncStackTracePayload)
|
|
{
|
|
if (this._delayedResumeTimeout) {
|
|
clearTimeout(this._delayedResumeTimeout);
|
|
this._delayedResumeTimeout = undefined;
|
|
}
|
|
|
|
let wasPaused = this.paused;
|
|
let targetData = this._targetDebuggerDataMap.get(target);
|
|
|
|
let callFrames = [];
|
|
let pauseReason = DebuggerManager.pauseReasonFromPayload(reason);
|
|
let pauseData = data || null;
|
|
|
|
for (var i = 0; i < callFramesPayload.length; ++i) {
|
|
var callFramePayload = callFramesPayload[i];
|
|
var sourceCodeLocation = this._sourceCodeLocationFromPayload(target, callFramePayload.location);
|
|
// FIXME: There may be useful call frames without a source code location (native callframes), should we include them?
|
|
if (!sourceCodeLocation)
|
|
continue;
|
|
if (!sourceCodeLocation.sourceCode)
|
|
continue;
|
|
|
|
// Exclude the case where the call frame is in the inspector code.
|
|
if (!WI.settings.engineeringShowInternalScripts.value && isWebKitInternalScript(sourceCodeLocation.sourceCode.sourceURL))
|
|
continue;
|
|
|
|
let scopeChain = this._scopeChainFromPayload(target, callFramePayload.scopeChain);
|
|
let callFrame = WI.CallFrame.fromDebuggerPayload(target, callFramePayload, scopeChain, sourceCodeLocation);
|
|
callFrames.push(callFrame);
|
|
}
|
|
|
|
let activeCallFrame = callFrames[0];
|
|
|
|
if (!activeCallFrame) {
|
|
// FIXME: This may not be safe for multiple threads/targets.
|
|
// This indicates we were pausing in internal scripts only (Injected Scripts).
|
|
// Just resume and skip past this pause. We should be fixing the backend to
|
|
// not send such pauses.
|
|
if (wasPaused)
|
|
target.DebuggerAgent.continueUntilNextRunLoop();
|
|
else
|
|
target.DebuggerAgent.resume();
|
|
this._didResumeInternal(target);
|
|
return;
|
|
}
|
|
|
|
let stackTrace = new WI.StackTrace(callFrames, {
|
|
parentStackTrace: WI.StackTrace.fromPayload(target, asyncStackTracePayload),
|
|
});
|
|
targetData.updateForPause(stackTrace, pauseReason, pauseData);
|
|
|
|
// Pause other targets because at least one target has paused.
|
|
// FIXME: Should this be done on the backend?
|
|
for (let [otherTarget, otherTargetData] of this._targetDebuggerDataMap)
|
|
otherTargetData.pauseIfNeeded();
|
|
|
|
let activeCallFrameDidChange = this._activeCallFrame && this._activeCallFrame.target === target;
|
|
if (activeCallFrameDidChange)
|
|
this._activeCallFrame = activeCallFrame;
|
|
else if (!wasPaused) {
|
|
this._activeCallFrame = activeCallFrame;
|
|
activeCallFrameDidChange = true;
|
|
}
|
|
|
|
if (!wasPaused)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.Paused);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.CallFramesDidChange, {target});
|
|
|
|
if (activeCallFrameDidChange)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
|
|
}
|
|
|
|
debuggerDidResume(target)
|
|
{
|
|
this._didResumeInternal(target);
|
|
}
|
|
|
|
playBreakpointActionSound(breakpointActionIdentifier)
|
|
{
|
|
InspectorFrontendHost.beep();
|
|
}
|
|
|
|
scriptDidParse(target, scriptIdentifier, url, startLine, startColumn, endLine, endColumn, isModule, isContentScript, sourceURL, sourceMapURL)
|
|
{
|
|
// Don't add the script again if it is already known.
|
|
let targetData = this.dataForTarget(target);
|
|
let existingScript = targetData.scriptForIdentifier(scriptIdentifier);
|
|
if (existingScript) {
|
|
console.assert(existingScript.url === (url || null));
|
|
console.assert(existingScript.range.startLine === startLine);
|
|
console.assert(existingScript.range.startColumn === startColumn);
|
|
console.assert(existingScript.range.endLine === endLine);
|
|
console.assert(existingScript.range.endColumn === endColumn);
|
|
return;
|
|
}
|
|
|
|
if (!WI.settings.engineeringShowInternalScripts.value && isWebKitInternalScript(sourceURL))
|
|
return;
|
|
|
|
let range = new WI.TextRange(startLine, startColumn, endLine, endColumn);
|
|
let sourceType = isModule ? WI.Script.SourceType.Module : WI.Script.SourceType.Program;
|
|
let script = new WI.Script(target, scriptIdentifier, range, url, sourceType, isContentScript, sourceURL, sourceMapURL);
|
|
|
|
targetData.addScript(script);
|
|
|
|
// FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
|
|
// We make the main resource of a WorkerTarget the Script instead of the Resource
|
|
// because the frontend may not be informed of the Resource. We should guarantee
|
|
// the frontend is informed of the Resource.
|
|
if (WI.sharedApp.debuggableType === WI.DebuggableType.ServiceWorker) {
|
|
// A ServiceWorker starts with a LocalScript for the main resource but we can replace it during initialization.
|
|
if (target.mainResource instanceof WI.LocalScript) {
|
|
if (script.url === target.name)
|
|
target.mainResource = script;
|
|
}
|
|
} else if (!target.mainResource && target !== WI.mainTarget) {
|
|
// A Worker starts without a main resource and we insert one.
|
|
if (script.url === target.name) {
|
|
target.mainResource = script;
|
|
if (script.resource)
|
|
target.resourceCollection.remove(script.resource);
|
|
}
|
|
}
|
|
|
|
if (isWebKitInternalScript(script.sourceURL)) {
|
|
this._internalWebKitScripts.push(script);
|
|
if (!WI.settings.engineeringShowInternalScripts.value)
|
|
return;
|
|
}
|
|
|
|
if (!WI.settings.debugShowConsoleEvaluations.value && isWebInspectorConsoleEvaluationScript(script.sourceURL))
|
|
return;
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ScriptAdded, {script});
|
|
|
|
if ((target !== WI.mainTarget || WI.sharedApp.debuggableType === WI.DebuggableType.ServiceWorker) && !script.isMainResource() && !script.resource)
|
|
target.addScript(script);
|
|
}
|
|
|
|
scriptDidFail(target, url, scriptSource)
|
|
{
|
|
const sourceURL = null;
|
|
const sourceType = WI.Script.SourceType.Program;
|
|
let script = new WI.LocalScript(target, url, sourceURL, sourceType, scriptSource);
|
|
|
|
// If there is already a resource we don't need to have the script anymore,
|
|
// we only need a script to use for parser error location links.
|
|
if (script.resource)
|
|
return;
|
|
|
|
let targetData = this.dataForTarget(target);
|
|
targetData.addScript(script);
|
|
target.addScript(script);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ScriptAdded, {script});
|
|
}
|
|
|
|
didSampleProbe(target, sample)
|
|
{
|
|
console.assert(this._probesByIdentifier.has(sample.probeId), "Unknown probe identifier specified for sample: ", sample);
|
|
let probe = this._probesByIdentifier.get(sample.probeId);
|
|
let elapsedTime = WI.timelineManager.computeElapsedTime(sample.timestamp);
|
|
let object = WI.RemoteObject.fromPayload(sample.payload, target);
|
|
probe.addSample(new WI.ProbeSample(sample.sampleId, sample.batchId, elapsedTime, object));
|
|
}
|
|
|
|
// Private
|
|
|
|
_sourceCodeLocationFromPayload(target, payload)
|
|
{
|
|
let targetData = this.dataForTarget(target);
|
|
let script = targetData.scriptForIdentifier(payload.scriptId);
|
|
if (!script)
|
|
return null;
|
|
|
|
return script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
|
|
}
|
|
|
|
_scopeChainFromPayload(target, payload)
|
|
{
|
|
let scopeChain = [];
|
|
for (let i = 0; i < payload.length; ++i)
|
|
scopeChain.push(this._scopeChainNodeFromPayload(target, payload[i]));
|
|
return scopeChain;
|
|
}
|
|
|
|
_scopeChainNodeFromPayload(target, payload)
|
|
{
|
|
var type = null;
|
|
switch (payload.type) {
|
|
case InspectorBackend.Enum.Debugger.ScopeType.Global:
|
|
type = WI.ScopeChainNode.Type.Global;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.With:
|
|
type = WI.ScopeChainNode.Type.With;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.Closure:
|
|
type = WI.ScopeChainNode.Type.Closure;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.Catch:
|
|
type = WI.ScopeChainNode.Type.Catch;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.FunctionName:
|
|
type = WI.ScopeChainNode.Type.FunctionName;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.NestedLexical:
|
|
type = WI.ScopeChainNode.Type.Block;
|
|
break;
|
|
case InspectorBackend.Enum.Debugger.ScopeType.GlobalLexicalEnvironment:
|
|
type = WI.ScopeChainNode.Type.GlobalLexicalEnvironment;
|
|
break;
|
|
default:
|
|
console.error("Unknown type: " + payload.type);
|
|
break;
|
|
}
|
|
|
|
let object = WI.RemoteObject.fromPayload(payload.object, target);
|
|
return new WI.ScopeChainNode(type, [object], payload.name, payload.location, payload.empty);
|
|
}
|
|
|
|
_setBreakpoint(breakpoint, specificTarget)
|
|
{
|
|
console.assert(!breakpoint.disabled);
|
|
|
|
if (breakpoint.disabled)
|
|
return;
|
|
|
|
if (!this._restoringBreakpoints && !this.breakpointsDisabledTemporarily) {
|
|
// Enable breakpoints since a breakpoint is being set. This eliminates
|
|
// a multi-step process for the user that can be confusing.
|
|
this.breakpointsEnabled = true;
|
|
}
|
|
|
|
function didSetBreakpoint(target, error, breakpointIdentifier, locations) {
|
|
if (error) {
|
|
WI.reportInternalError(error);
|
|
return;
|
|
}
|
|
|
|
this._breakpointIdMap.set(breakpointIdentifier, breakpoint);
|
|
|
|
breakpoint.identifier = breakpointIdentifier;
|
|
|
|
// Debugger.setBreakpoint returns a single location.
|
|
if (!(locations instanceof Array))
|
|
locations = [locations];
|
|
|
|
for (let location of locations)
|
|
this.breakpointResolved(target, breakpointIdentifier, location);
|
|
}
|
|
|
|
// The breakpoint will be resolved again by calling DebuggerAgent, so mark it as unresolved.
|
|
// If something goes wrong it will stay unresolved and show up as such in the user interface.
|
|
// When setting for a new target, don't change the resolved target.
|
|
if (!specificTarget)
|
|
breakpoint.clearResolvedLocations();
|
|
|
|
if (breakpoint.contentIdentifier) {
|
|
let targets = specificTarget ? [specificTarget] : WI.targets;
|
|
for (let target of targets) {
|
|
target.DebuggerAgent.setBreakpointByUrl.invoke({
|
|
lineNumber: breakpoint.sourceCodeLocation.lineNumber,
|
|
url: breakpoint.contentIdentifier,
|
|
urlRegex: undefined,
|
|
columnNumber: breakpoint.sourceCodeLocation.columnNumber,
|
|
options: breakpoint.optionsToProtocol(),
|
|
}, didSetBreakpoint.bind(this, target));
|
|
}
|
|
} else if (breakpoint.scriptIdentifier) {
|
|
let target = breakpoint.target;
|
|
target.DebuggerAgent.setBreakpoint.invoke({
|
|
location: {scriptId: breakpoint.scriptIdentifier, lineNumber: breakpoint.sourceCodeLocation.lineNumber, columnNumber: breakpoint.sourceCodeLocation.columnNumber},
|
|
options: breakpoint.optionsToProtocol(),
|
|
}, didSetBreakpoint.bind(this, target));
|
|
} else
|
|
WI.reportInternalError("Unknown source for breakpoint.");
|
|
}
|
|
|
|
_removeBreakpoint(breakpoint, callback)
|
|
{
|
|
if (!breakpoint.identifier)
|
|
return;
|
|
|
|
function didRemoveBreakpoint(target, error)
|
|
{
|
|
if (error) {
|
|
WI.reportInternalError(error);
|
|
return;
|
|
}
|
|
|
|
this._breakpointIdMap.delete(breakpoint.identifier);
|
|
|
|
breakpoint.identifier = null;
|
|
|
|
// Don't reset resolved here since we want to keep disabled breakpoints looking like they
|
|
// are resolved in the user interface. They will get marked as unresolved in reset.
|
|
|
|
if (callback)
|
|
callback(target);
|
|
}
|
|
|
|
if (breakpoint.contentIdentifier) {
|
|
for (let target of WI.targets)
|
|
target.DebuggerAgent.removeBreakpoint(breakpoint.identifier, didRemoveBreakpoint.bind(this, target));
|
|
} else if (breakpoint.scriptIdentifier) {
|
|
let target = breakpoint.target;
|
|
target.DebuggerAgent.removeBreakpoint(breakpoint.identifier, didRemoveBreakpoint.bind(this, target));
|
|
}
|
|
}
|
|
|
|
_setSymbolicBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(breakpoint instanceof WI.SymbolicBreakpoint, breakpoint);
|
|
console.assert(!breakpoint.disabled, breakpoint);
|
|
console.assert(WI.SymbolicBreakpoint.supported(target), target);
|
|
|
|
if (!this._restoringBreakpoints && !this.breakpointsDisabledTemporarily)
|
|
this.breakpointsEnabled = true;
|
|
|
|
target.DebuggerAgent.addSymbolicBreakpoint.invoke({
|
|
symbol: breakpoint.symbol,
|
|
caseSensitive: breakpoint.caseSensitive,
|
|
isRegex: breakpoint.isRegex,
|
|
options: breakpoint.optionsToProtocol(),
|
|
});
|
|
}
|
|
|
|
_removeSymbolicBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(breakpoint instanceof WI.SymbolicBreakpoint, breakpoint);
|
|
console.assert(WI.SymbolicBreakpoint.supported(target), target);
|
|
|
|
target.DebuggerAgent.removeSymbolicBreakpoint.invoke({
|
|
symbol: breakpoint.symbol,
|
|
caseSensitive: breakpoint.caseSensitive,
|
|
isRegex: breakpoint.isRegex,
|
|
});
|
|
}
|
|
|
|
_setPauseOnExceptions(target)
|
|
{
|
|
let commandArguments = {
|
|
state: "none",
|
|
};
|
|
|
|
if (this._breakpointsEnabledSetting.value) {
|
|
if (this._allExceptionsBreakpoint && !this._allExceptionsBreakpoint.disabled) {
|
|
commandArguments.state = "all";
|
|
commandArguments.options = this._allExceptionsBreakpoint.optionsToProtocol();
|
|
} else if (this._uncaughtExceptionsBreakpoint && !this._uncaughtExceptionsBreakpoint.disabled) {
|
|
commandArguments.state = "uncaught";
|
|
commandArguments.options = this._uncaughtExceptionsBreakpoint.optionsToProtocol();
|
|
}
|
|
|
|
// Mark the uncaught breakpoint as unresolved if "all" as it includes "uncaught".
|
|
// That way it is clear in the user interface that the breakpoint is ignored.
|
|
if (this._uncaughtExceptionsBreakpoint)
|
|
this._uncaughtExceptionsBreakpoint.resolved = commandArguments.state !== "all";
|
|
}
|
|
|
|
target.DebuggerAgent.setPauseOnExceptions.invoke(commandArguments);
|
|
}
|
|
|
|
_createSpecialBreakpoint(serializedBreakpoint = {})
|
|
{
|
|
let location = WI.SourceCodeLocation.specialBreakpointLocation;
|
|
serializedBreakpoint.lineNumber = location.lineNumber;
|
|
serializedBreakpoint.columnNumber = location.columnNumber;
|
|
|
|
serializedBreakpoint.resolved = true;
|
|
|
|
return WI.JavaScriptBreakpoint.fromJSON(serializedBreakpoint);
|
|
}
|
|
|
|
_updateSpecialBreakpoint(breakpoint, specificTarget)
|
|
{
|
|
console.assert(breakpoint.special, breakpoint);
|
|
|
|
if (!breakpoint.disabled && !this._restoringBreakpoints && !this.breakpointsDisabledTemporarily)
|
|
this.breakpointsEnabled = true;
|
|
|
|
let targets = specificTarget ? [specificTarget] : WI.targets;
|
|
|
|
let setting = null;
|
|
let command = null;
|
|
switch (breakpoint) {
|
|
case this._debuggerStatementsBreakpoint:
|
|
setting = this._debuggerStatementsBreakpointSetting;
|
|
command = "setPauseOnDebuggerStatements";
|
|
break;
|
|
|
|
case this._allExceptionsBreakpoint:
|
|
setting = this._allExceptionsBreakpointSetting;
|
|
break;
|
|
|
|
case this._uncaughtExceptionsBreakpoint:
|
|
setting = this._uncaughtExceptionsBreakpointSetting;
|
|
break;
|
|
|
|
case this._assertionFailuresBreakpoint:
|
|
setting = this._assertionFailuresBreakpointSetting;
|
|
command = "setPauseOnAssertions";
|
|
break;
|
|
|
|
case this._allMicrotasksBreakpoint:
|
|
setting = this._allMicrotasksBreakpointSetting;
|
|
command = "setPauseOnMicrotasks";
|
|
break;
|
|
}
|
|
console.assert(setting);
|
|
|
|
if (!specificTarget)
|
|
setting.value = breakpoint.toJSON();
|
|
|
|
if (command) {
|
|
let commandArguments = {
|
|
enabled: !breakpoint.disabled,
|
|
};
|
|
if (!breakpoint.disabled)
|
|
commandArguments.options = breakpoint.optionsToProtocol();
|
|
|
|
for (let target of targets)
|
|
target.DebuggerAgent[command].invoke(commandArguments);
|
|
} else {
|
|
console.assert(breakpoint === this._allExceptionsBreakpoint || breakpoint === this._uncaughtExceptionsBreakpoint, breakpoint);
|
|
for (let target of targets)
|
|
this._setPauseOnExceptions(target);
|
|
}
|
|
}
|
|
|
|
_setBlackboxBreakpointEvaluations(target)
|
|
{
|
|
// COMPATIBILITY (macOS 12.3, iOS 15.4): Debugger.setBlackboxBreakpointEvaluations did not exist yet.
|
|
if (target.hasCommand("Debugger.setBlackboxBreakpointEvaluations"))
|
|
target.DebuggerAgent.setBlackboxBreakpointEvaluations(WI.settings.blackboxBreakpointEvaluations.value);
|
|
}
|
|
|
|
_breakpointDisplayLocationDidChange(event)
|
|
{
|
|
if (this._ignoreBreakpointDisplayLocationDidChangeEvent)
|
|
return;
|
|
|
|
let breakpoint = event.target;
|
|
if (!breakpoint.identifier || breakpoint.disabled)
|
|
return;
|
|
|
|
// Remove the breakpoint with its old id.
|
|
this._removeBreakpoint(breakpoint, (target) => {
|
|
// Add the breakpoint at its new lineNumber and get a new id.
|
|
this._restoringBreakpoints = true;
|
|
this._setBreakpoint(breakpoint, target);
|
|
this._restoringBreakpoints = false;
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointMoved, {breakpoint});
|
|
});
|
|
}
|
|
|
|
_breakpointDisabledStateDidChange(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (breakpoint.special) {
|
|
this._updateSpecialBreakpoint(breakpoint);
|
|
return;
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.breakpoints.putObject(breakpoint);
|
|
|
|
if (breakpoint.disabled)
|
|
this._removeBreakpoint(breakpoint);
|
|
else
|
|
this._setBreakpoint(breakpoint);
|
|
}
|
|
|
|
_breakpointEditablePropertyDidChange(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (breakpoint.special) {
|
|
this._restoringBreakpoints = true;
|
|
this._updateSpecialBreakpoint(breakpoint);
|
|
this._restoringBreakpoints = false;
|
|
return;
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.breakpoints.putObject(breakpoint);
|
|
|
|
if (breakpoint.disabled)
|
|
return;
|
|
|
|
console.assert(breakpoint.editable);
|
|
if (!breakpoint.editable)
|
|
return;
|
|
|
|
// Remove the breakpoint with its old id.
|
|
this._removeBreakpoint(breakpoint, (target) => {
|
|
// Add the breakpoint with its new properties and get a new id.
|
|
this._restoringBreakpoints = true;
|
|
this._setBreakpoint(breakpoint, target);
|
|
this._restoringBreakpoints = false;
|
|
});
|
|
}
|
|
|
|
_handleBreakpointActionsDidChange(event)
|
|
{
|
|
this._breakpointEditablePropertyDidChange(event);
|
|
|
|
this.updateProbesForBreakpoint(event.target);
|
|
}
|
|
|
|
_handleSymbolicBreakpointDisabledStateChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
for (let target of WI.targets) {
|
|
if (breakpoint.disabled)
|
|
this._removeSymbolicBreakpoint(breakpoint, target);
|
|
else
|
|
this._setSymbolicBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.symbolicBreakpoints.putObject(breakpoint);
|
|
}
|
|
|
|
_handleSymbolicBreakpointEditablePropertyChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.symbolicBreakpoints.putObject(breakpoint);
|
|
|
|
if (breakpoint.disabled)
|
|
return;
|
|
|
|
this._restoringBreakpoints = true;
|
|
for (let target of WI.targets) {
|
|
// Clear the old breakpoint from the backend before setting the new one.
|
|
this._removeSymbolicBreakpoint(breakpoint, target);
|
|
this._setSymbolicBreakpoint(breakpoint, target);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
|
|
_handleSymbolicBreakpointActionsChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
this._handleSymbolicBreakpointEditablePropertyChanged(event);
|
|
|
|
this.updateProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_startDisablingBreakpointsTemporarily()
|
|
{
|
|
if (++this._temporarilyDisableBreakpointsRequestCount > 1)
|
|
return;
|
|
|
|
console.assert(!this.breakpointsDisabledTemporarily, "Already temporarily disabling breakpoints.");
|
|
if (this.breakpointsDisabledTemporarily)
|
|
return;
|
|
|
|
|
|
this._temporarilyDisabledBreakpointsRestoreSetting.value = this._breakpointsEnabledSetting.value;
|
|
|
|
this.breakpointsEnabled = false;
|
|
}
|
|
|
|
_stopDisablingBreakpointsTemporarily()
|
|
{
|
|
this._temporarilyDisableBreakpointsRequestCount = Math.max(0, this._temporarilyDisableBreakpointsRequestCount - 1);
|
|
if (this._temporarilyDisableBreakpointsRequestCount > 0)
|
|
return;
|
|
|
|
console.assert(this.breakpointsDisabledTemporarily, "Was not temporarily disabling breakpoints.");
|
|
if (!this.breakpointsDisabledTemporarily)
|
|
return;
|
|
|
|
let restoreState = this._temporarilyDisabledBreakpointsRestoreSetting.value;
|
|
this._temporarilyDisabledBreakpointsRestoreSetting.value = null;
|
|
|
|
this.breakpointsEnabled = restoreState;
|
|
}
|
|
|
|
_handleTimelineCapturingStateChanged(event)
|
|
{
|
|
switch (WI.timelineManager.capturingState) {
|
|
case WI.TimelineManager.CapturingState.Starting:
|
|
this._startDisablingBreakpointsTemporarily();
|
|
if (this.paused)
|
|
this.resume();
|
|
break;
|
|
|
|
case WI.TimelineManager.CapturingState.Inactive:
|
|
this._stopDisablingBreakpointsTemporarily();
|
|
break;
|
|
}
|
|
}
|
|
|
|
_handleAuditManagerTestScheduled(event)
|
|
{
|
|
this._startDisablingBreakpointsTemporarily();
|
|
|
|
if (this.paused)
|
|
this.resume();
|
|
}
|
|
|
|
_handleAuditManagerTestCompleted(event)
|
|
{
|
|
this._stopDisablingBreakpointsTemporarily();
|
|
}
|
|
|
|
_targetRemoved(event)
|
|
{
|
|
let wasPaused = this.paused;
|
|
|
|
this._targetDebuggerDataMap.delete(event.data.target);
|
|
|
|
if (!this.paused && wasPaused)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
|
|
}
|
|
|
|
_handleBlackboxBreakpointEvaluationsChange(event)
|
|
{
|
|
for (let target of WI.targets)
|
|
this._setBlackboxBreakpointEvaluations(target);
|
|
}
|
|
|
|
_handleEngineeringShowInternalScriptsSettingChanged(event)
|
|
{
|
|
let eventType = WI.settings.engineeringShowInternalScripts.value ? WI.DebuggerManager.Event.ScriptAdded : WI.DebuggerManager.Event.ScriptRemoved;
|
|
for (let script of this._internalWebKitScripts)
|
|
this.dispatchEventToListeners(eventType, {script});
|
|
}
|
|
|
|
_handleEngineeringPauseForInternalScriptsSettingChanged(event)
|
|
{
|
|
for (let target of WI.targets) {
|
|
if (target.hasCommand("Debugger.setPauseForInternalScripts"))
|
|
target.DebuggerAgent.setPauseForInternalScripts(WI.settings.engineeringPauseForInternalScripts.value);
|
|
}
|
|
}
|
|
|
|
_mainResourceDidChange(event)
|
|
{
|
|
if (!event.target.isMainFrame())
|
|
return;
|
|
|
|
this._didResumeInternal(WI.mainTarget);
|
|
}
|
|
|
|
_didResumeInternal(target)
|
|
{
|
|
if (!this.paused)
|
|
return;
|
|
|
|
if (this._delayedResumeTimeout) {
|
|
clearTimeout(this._delayedResumeTimeout);
|
|
this._delayedResumeTimeout = undefined;
|
|
}
|
|
|
|
let activeCallFrameDidChange = false;
|
|
if (this._activeCallFrame && this._activeCallFrame.target === target) {
|
|
this._activeCallFrame = null;
|
|
activeCallFrameDidChange = true;
|
|
}
|
|
|
|
this._blackboxedCallFrameGroupsToAutoExpand = [];
|
|
|
|
this.dataForTarget(target).updateForResume();
|
|
|
|
if (!this.paused)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
|
|
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.CallFramesDidChange, {target});
|
|
|
|
if (activeCallFrameDidChange)
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
|
|
}
|
|
|
|
_associateBreakpointsWithSourceCode(breakpoints, sourceCode)
|
|
{
|
|
this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
|
|
|
|
for (let breakpoint of breakpoints) {
|
|
if (!breakpoint.sourceCodeLocation.sourceCode)
|
|
breakpoint.sourceCodeLocation.sourceCode = sourceCode;
|
|
// SourceCodes can be unequal if the SourceCodeLocation is associated with a Script and we are looking at the Resource.
|
|
console.assert(breakpoint.sourceCodeLocation.sourceCode === sourceCode || breakpoint.sourceCodeLocation.sourceCode.contentIdentifier === sourceCode.contentIdentifier);
|
|
}
|
|
|
|
this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
|
|
}
|
|
|
|
_probeSetForBreakpoint(breakpoint)
|
|
{
|
|
let probeSet = this._probeSetsByBreakpoint.get(breakpoint);
|
|
if (!probeSet) {
|
|
probeSet = new WI.ProbeSet(breakpoint);
|
|
this._probeSetsByBreakpoint.set(breakpoint, probeSet);
|
|
this.dispatchEventToListeners(WI.DebuggerManager.Event.ProbeSetAdded, {probeSet});
|
|
}
|
|
return probeSet;
|
|
}
|
|
};
|
|
|
|
WI.DebuggerManager.Event = {
|
|
BreakpointAdded: "debugger-manager-breakpoint-added",
|
|
BreakpointRemoved: "debugger-manager-breakpoint-removed",
|
|
BreakpointMoved: "debugger-manager-breakpoint-moved",
|
|
SymbolicBreakpointAdded: "debugger-manager-symbolic-breakpoint-added",
|
|
SymbolicBreakpointRemoved: "debugger-manager-symbolic-breakpoint-removed",
|
|
WaitingToPause: "debugger-manager-waiting-to-pause",
|
|
Paused: "debugger-manager-paused",
|
|
Resumed: "debugger-manager-resumed",
|
|
CallFramesDidChange: "debugger-manager-call-frames-did-change",
|
|
ActiveCallFrameDidChange: "debugger-manager-active-call-frame-did-change",
|
|
ScriptAdded: "debugger-manager-script-added",
|
|
ScriptRemoved: "debugger-manager-script-removed",
|
|
ScriptsCleared: "debugger-manager-scripts-cleared",
|
|
BreakpointsEnabledDidChange: "debugger-manager-breakpoints-enabled-did-change",
|
|
ProbeSetAdded: "debugger-manager-probe-set-added",
|
|
ProbeSetRemoved: "debugger-manager-probe-set-removed",
|
|
BlackboxChanged: "blackboxed-urls-changed",
|
|
};
|
|
|
|
WI.DebuggerManager.PauseReason = {
|
|
AnimationFrame: "animation-frame",
|
|
Assertion: "assertion",
|
|
BlackboxedScript: "blackboxed-script",
|
|
Breakpoint: "breakpoint",
|
|
CSPViolation: "CSP-violation",
|
|
DebuggerStatement: "debugger-statement",
|
|
DOM: "DOM",
|
|
Exception: "exception",
|
|
FunctionCall: "function-call",
|
|
Interval: "interval",
|
|
Listener: "listener",
|
|
Microtask: "microtask",
|
|
PauseOnNextStatement: "pause-on-next-statement",
|
|
Timeout: "timeout",
|
|
URL: "url",
|
|
Other: "other",
|
|
|
|
// COMPATIBILITY (iOS 13): DOMDebugger.EventBreakpointType.Timer was replaced by DOMDebugger.EventBreakpointType.Interval and DOMDebugger.EventBreakpointType.Timeout.
|
|
Timer: "timer",
|
|
|
|
// COMPATIBILITY (iOS 13): DOMDebugger.EventBreakpointType.EventListener was replaced by DOMDebugger.EventBreakpointType.Listener.
|
|
EventListener: "event-listener",
|
|
};
|
|
|
|
WI.DebuggerManager.BlackboxType = {
|
|
Pattern: "pattern",
|
|
URL: "url",
|
|
};
|