1080 lines
42 KiB
JavaScript
1080 lines
42 KiB
JavaScript
/*
|
|
* Copyright (C) 2017 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.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
|
|
{
|
|
constructor()
|
|
{
|
|
super();
|
|
|
|
this._domBreakpointURLMap = new Multimap;
|
|
this._domBreakpointFrameIdentifierMap = new Map;
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = false;
|
|
|
|
this._listenerBreakpoints = [];
|
|
this._allAnimationFramesBreakpoint = null;
|
|
this._allIntervalsBreakpoint = null;
|
|
this._allListenersBreakpoint = null;
|
|
this._allTimeoutsBreakpoint = null;
|
|
|
|
this._urlBreakpoints = [];
|
|
this._allRequestsBreakpoint = null;
|
|
|
|
WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleDOMBreakpointDisabledStateChanged, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleDOMBreakpointActionsChanged, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeWillChange, this._handleDOMBreakpointDOMNodeWillChange, this);
|
|
WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeDidChange, this._handleDOMBreakpointDOMNodeDidChange, this);
|
|
|
|
WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleEventBreakpointDisabledStateChanged, this);
|
|
WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._handleEventBreakpointEditablePropertyChanged, this);
|
|
WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._handleEventBreakpointEditablePropertyChanged, this);
|
|
WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._handleEventBreakpointEditablePropertyChanged, this);
|
|
WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleEventBreakpointActionsChanged, this);
|
|
|
|
WI.URLBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleURLBreakpointDisabledStateChanged, this);
|
|
WI.URLBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._handleURLBreakpointEditablePropertyChanged, this);
|
|
WI.URLBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._handleURLBreakpointEditablePropertyChanged, this);
|
|
WI.URLBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._handleURLBreakpointEditablePropertyChanged, this);
|
|
WI.URLBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleURLBreakpointActionsChanged, this);
|
|
|
|
WI.domManager.addEventListener(WI.DOMManager.Event.NodeRemoved, this._nodeRemoved, this);
|
|
WI.domManager.addEventListener(WI.DOMManager.Event.NodeInserted, this._nodeInserted, this);
|
|
|
|
WI.networkManager.addEventListener(WI.NetworkManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
|
|
|
|
WI.Frame.addEventListener(WI.Frame.Event.ChildFrameWasRemoved, this._childFrameWasRemoved, this);
|
|
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
|
|
|
|
let loadBreakpoints = (constructor, objectStore, oldSettings, callback) => {
|
|
WI.Target.registerInitializationPromise((async () => {
|
|
for (let key of oldSettings) {
|
|
let existingSerializedBreakpoints = WI.Setting.migrateValue(key);
|
|
if (existingSerializedBreakpoints) {
|
|
for (let existingSerializedBreakpoint of existingSerializedBreakpoints)
|
|
await objectStore.putObject(constructor.fromJSON(existingSerializedBreakpoint));
|
|
}
|
|
}
|
|
|
|
let serializedBreakpoints = await objectStore.getAll();
|
|
|
|
this._restoringBreakpoints = true;
|
|
for (let serializedBreakpoint of serializedBreakpoints) {
|
|
let breakpoint = constructor.fromJSON(serializedBreakpoint);
|
|
|
|
const key = null;
|
|
objectStore.associateObject(breakpoint, key, serializedBreakpoint);
|
|
|
|
callback(breakpoint);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
})());
|
|
};
|
|
|
|
function loadLegacySpecialBreakpoint(shownSettingsKey, enabledSettingsKey, callback) {
|
|
if (!WI.Setting.migrateValue(shownSettingsKey))
|
|
return;
|
|
|
|
return callback({
|
|
disabled: !WI.Setting.migrateValue(enabledSettingsKey),
|
|
});
|
|
}
|
|
|
|
loadBreakpoints(WI.DOMBreakpoint, WI.objectStores.domBreakpoints, ["dom-breakpoints"], (breakpoint) => {
|
|
this.addDOMBreakpoint(breakpoint);
|
|
});
|
|
|
|
if (DOMDebuggerManager.supportsEventBreakpoints() || DOMDebuggerManager.supportsEventListenerBreakpoints()) {
|
|
loadBreakpoints(WI.EventBreakpoint, WI.objectStores.eventBreakpoints, ["event-breakpoints"], (breakpoint) => {
|
|
this.addEventBreakpoint(breakpoint);
|
|
});
|
|
|
|
this._allAnimationFramesBreakpoint ??= loadLegacySpecialBreakpoint("show-all-animation-frames-breakpoint", "break-on-all-animation-frames", (options) => new WI.EventBreakpoint(WI.EventBreakpoint.Type.AnimationFrame, options));
|
|
this._allIntervalsBreakpoint ??= loadLegacySpecialBreakpoint("show-all-inteverals-breakpoint", "break-on-all-intervals", (options) => new WI.EventBreakpoint(WI.EventBreakpoint.Type.Interval, options));
|
|
this._allListenersBreakpoint ??= loadLegacySpecialBreakpoint("show-all-listeners-breakpoint", "break-on-all-listeners", (options) => new WI.EventBreakpoint(WI.EventBreakpoint.Type.Listener, options));
|
|
this._allTimeoutsBreakpoint ??= loadLegacySpecialBreakpoint("show-all-timeouts-breakpoint", "break-on-all-timeouts", (options) => new WI.EventBreakpoint(WI.EventBreakpoint.Type.Timeout, options));
|
|
}
|
|
|
|
if (DOMDebuggerManager.supportsURLBreakpoints() || DOMDebuggerManager.supportsXHRBreakpoints()) {
|
|
loadBreakpoints(WI.URLBreakpoint, WI.objectStores.urlBreakpoints, ["xhr-breakpoints", "url-breakpoints"], (breakpoint) => {
|
|
this.addURLBreakpoint(breakpoint);
|
|
});
|
|
|
|
this._allRequestsBreakpoint ??= loadLegacySpecialBreakpoint("show-all-requests-breakpoint", "break-on-all-requests", (options) => new WI.URLBreakpoint(WI.URLBreakpoint.Type.Text, "", options));
|
|
}
|
|
}
|
|
|
|
// Target
|
|
|
|
initializeTarget(target)
|
|
{
|
|
if (target.hasDomain("DOMDebugger")) {
|
|
this._restoringBreakpoints = true;
|
|
|
|
if (target === WI.assumingMainTarget() && target.mainResource)
|
|
this._speculativelyResolveDOMBreakpointsForURL(target.mainResource.url);
|
|
|
|
if (this._allAnimationFramesBreakpoint && !this._allAnimationFramesBreakpoint.disabled)
|
|
this._setEventBreakpoint(this._allAnimationFramesBreakpoint, target);
|
|
|
|
if (this._allIntervalsBreakpoint && !this._allIntervalsBreakpoint.disabled)
|
|
this._setEventBreakpoint(this._allIntervalsBreakpoint, target);
|
|
|
|
if (this._allListenersBreakpoint && !this._allListenersBreakpoint.disabled)
|
|
this._setEventBreakpoint(this._allListenersBreakpoint, target);
|
|
|
|
if (this._allTimeoutsBreakpoint && !this._allTimeoutsBreakpoint.disabled)
|
|
this._setEventBreakpoint(this._allTimeoutsBreakpoint, target);
|
|
|
|
if (this._allRequestsBreakpoint)
|
|
this._setURLBreakpoint(this._allRequestsBreakpoint, target);
|
|
|
|
for (let breakpoint of this._listenerBreakpoints) {
|
|
if (!breakpoint.disabled)
|
|
this._setEventBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
for (let breakpoint of this._urlBreakpoints) {
|
|
if (!breakpoint.disabled)
|
|
this._setURLBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
}
|
|
|
|
// Static
|
|
|
|
static supportsEventBreakpoints()
|
|
{
|
|
// COMPATIBILITY (iOS 13): DOMDebugger.setEventBreakpoint and DOMDebugger.removeEventBreakpoint did not exist yet.
|
|
return InspectorBackend.hasCommand("DOMDebugger.setEventBreakpoint")
|
|
&& InspectorBackend.hasCommand("DOMDebugger.removeEventBreakpoint");
|
|
}
|
|
|
|
static supportsEventListenerBreakpoints()
|
|
{
|
|
// COMPATIBILITY (iOS 12.2): Replaced by DOMDebugger.setEventBreakpoint and DOMDebugger.removeEventBreakpoint.
|
|
return InspectorBackend.hasCommand("DOMDebugger.setEventListenerBreakpoint")
|
|
&& InspectorBackend.hasCommand("DOMDebugger.removeEventListenerBreakpoint");
|
|
}
|
|
|
|
static supportsURLBreakpoints()
|
|
{
|
|
// COMPATIBILITY (iOS 13): DOMDebugger.setURLBreakpoint and DOMDebugger.removeURLBreakpoint did not exist yet.
|
|
return InspectorBackend.hasCommand("DOMDebugger.setURLBreakpoint")
|
|
&& InspectorBackend.hasCommand("DOMDebugger.removeURLBreakpoint");
|
|
}
|
|
|
|
static supportsXHRBreakpoints()
|
|
{
|
|
// COMPATIBILITY (iOS 13): Replaced by DOMDebugger.setURLBreakpoint and DOMDebugger.removeURLBreakpoint.
|
|
return InspectorBackend.hasCommand("DOMDebugger.setXHRBreakpoint")
|
|
&& InspectorBackend.hasCommand("DOMDebugger.removeXHRBreakpoint");
|
|
}
|
|
|
|
static supportsAllListenersBreakpoint()
|
|
{
|
|
// COMPATIBILITY (iOS 13): DOMDebugger.EventBreakpointType.Interval and DOMDebugger.EventBreakpointType.Timeout did not exist yet.
|
|
return DOMDebuggerManager.supportsEventBreakpoints()
|
|
&& InspectorBackend.Enum.DOMDebugger.EventBreakpointType.Interval
|
|
&& InspectorBackend.Enum.DOMDebugger.EventBreakpointType.Timeout;
|
|
}
|
|
|
|
// Public
|
|
|
|
get supported()
|
|
{
|
|
return InspectorBackend.hasDomain("DOMDebugger");
|
|
}
|
|
|
|
get allAnimationFramesBreakpoint() { return this._allAnimationFramesBreakpoint; }
|
|
get allIntervalsBreakpoint() { return this._allIntervalsBreakpoint; }
|
|
get allListenersBreakpoint() { return this._allListenersBreakpoint; }
|
|
get allTimeoutsBreakpoint() { return this._allTimeoutsBreakpoint; }
|
|
get allRequestsBreakpoint() { return this._allRequestsBreakpoint; }
|
|
|
|
get domBreakpoints()
|
|
{
|
|
let mainFrame = WI.networkManager.mainFrame;
|
|
if (!mainFrame)
|
|
return [];
|
|
|
|
let resolvedBreakpoints = [];
|
|
let frames = [mainFrame];
|
|
while (frames.length) {
|
|
let frame = frames.shift();
|
|
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame);
|
|
if (domBreakpointNodeIdentifierMap)
|
|
resolvedBreakpoints.pushAll(domBreakpointNodeIdentifierMap.values());
|
|
|
|
frames.pushAll(frame.childFrameCollection);
|
|
}
|
|
|
|
return resolvedBreakpoints;
|
|
}
|
|
|
|
get listenerBreakpoints() { return this._listenerBreakpoints; }
|
|
get urlBreakpoints() { return this._urlBreakpoints; }
|
|
|
|
domBreakpointsForNode(node)
|
|
{
|
|
console.assert(node instanceof WI.DOMNode);
|
|
|
|
if (!node || !node.frame)
|
|
return [];
|
|
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
|
|
if (!domBreakpointNodeIdentifierMap)
|
|
return [];
|
|
|
|
let breakpoints = domBreakpointNodeIdentifierMap.get(node);
|
|
return breakpoints ? Array.from(breakpoints) : [];
|
|
}
|
|
|
|
domBreakpointsInSubtree(node)
|
|
{
|
|
console.assert(node instanceof WI.DOMNode);
|
|
|
|
let breakpoints = [];
|
|
|
|
if (node.children) {
|
|
let children = Array.from(node.children);
|
|
while (children.length) {
|
|
let child = children.pop();
|
|
if (child.children)
|
|
children.pushAll(child.children);
|
|
breakpoints.pushAll(this.domBreakpointsForNode(child));
|
|
}
|
|
}
|
|
|
|
return breakpoints;
|
|
}
|
|
|
|
addDOMBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.DOMBreakpoint, breakpoint);
|
|
console.assert(breakpoint.url, breakpoint);
|
|
if (!breakpoint || !breakpoint.url)
|
|
return;
|
|
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
|
|
this._domBreakpointURLMap.add(breakpoint.url, breakpoint);
|
|
|
|
if (breakpoint.domNode) {
|
|
this._resolveDOMBreakpoint(breakpoint, breakpoint.domNode);
|
|
|
|
if (!breakpoint.disabled) {
|
|
// We should get the target associated with the nodeIdentifier of this breakpoint.
|
|
let target = WI.assumingMainTarget();
|
|
if (target)
|
|
this._setDOMBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
|
|
|
|
WI.debuggerManager.addProbesForBreakpoint(breakpoint);
|
|
} else
|
|
this._speculativelyResolveDOMBreakpoint(breakpoint);
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.domBreakpoints.putObject(breakpoint);
|
|
}
|
|
|
|
removeDOMBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.DOMBreakpoint, breakpoint);
|
|
console.assert(breakpoint.url, breakpoint);
|
|
if (!breakpoint || !breakpoint.url)
|
|
return;
|
|
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
|
|
// Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
|
|
breakpoint.disabled = true;
|
|
breakpoint.clearActions();
|
|
|
|
this._domBreakpointURLMap.delete(breakpoint.url);
|
|
|
|
if (breakpoint.domNode) {
|
|
if (breakpoint.domNode.frame) {
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(breakpoint.domNode.frame);
|
|
domBreakpointNodeIdentifierMap.delete(breakpoint.domNode, breakpoint);
|
|
if (!domBreakpointNodeIdentifierMap.size)
|
|
this._domBreakpointFrameIdentifierMap.delete(breakpoint.domNode.frame);
|
|
}
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
|
|
|
|
breakpoint.domNode = null;
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.domBreakpoints.deleteObject(breakpoint);
|
|
}
|
|
|
|
removeDOMBreakpointsForNode(node)
|
|
{
|
|
this.domBreakpointsForNode(node).forEach(this.removeDOMBreakpoint, this);
|
|
}
|
|
|
|
listenerBreakpointsForEventName(eventName)
|
|
{
|
|
if (DOMDebuggerManager.supportsAllListenersBreakpoint() && this._allListenersBreakpoint && !this._allListenersBreakpoint.disabled)
|
|
return this._allListenersBreakpoint;
|
|
|
|
// Order event breakpoints based on how closely they match the given symbol. As an example,
|
|
// a regular expression is likely going to match more events 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._listenerBreakpoints
|
|
.filter((breakpoint) => breakpoint.matches(eventName))
|
|
.sort((a, b) => {
|
|
let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
|
|
let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
|
|
return aRank - bRank;
|
|
});
|
|
}
|
|
|
|
addEventBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.EventBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return false;
|
|
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
|
|
switch (breakpoint.type) {
|
|
case WI.EventBreakpoint.Type.AnimationFrame:
|
|
console.assert(!this._allAnimationFramesBreakpoint, this._allAnimationFramesBreakpoint, breakpoint);
|
|
this._allAnimationFramesBreakpoint = breakpoint;
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Interval:
|
|
console.assert(!this._allIntervalsBreakpoint, this._allIntervalsBreakpoint, breakpoint);
|
|
this._allIntervalsBreakpoint = breakpoint;
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Listener:
|
|
if (breakpoint.eventName) {
|
|
if (this._listenerBreakpoints.some((existing) => existing.equals(breakpoint)))
|
|
return false;
|
|
|
|
this._listenerBreakpoints.push(breakpoint);
|
|
} else {
|
|
console.assert(!this._allListenersBreakpoint, this._allListenersBreakpoint, breakpoint);
|
|
this._allListenersBreakpoint = breakpoint;
|
|
}
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Timeout:
|
|
console.assert(!this._allTimeoutsBreakpoint, this._allTimeoutsBreakpoint, breakpoint);
|
|
this._allTimeoutsBreakpoint = breakpoint;
|
|
break;
|
|
}
|
|
|
|
WI.debuggerManager.addProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointAdded, {breakpoint});
|
|
|
|
if (!breakpoint.disabled) {
|
|
for (let target of WI.targets)
|
|
this._setEventBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.eventBreakpoints.putObject(breakpoint);
|
|
|
|
return true;
|
|
}
|
|
|
|
removeEventBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.EventBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return;
|
|
|
|
// Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
|
|
breakpoint.disabled = true;
|
|
breakpoint.clearActions();
|
|
|
|
switch (breakpoint.type) {
|
|
case WI.EventBreakpoint.Type.AnimationFrame:
|
|
console.assert(this._allAnimationFramesBreakpoint, this._allAnimationFramesBreakpoint);
|
|
this._allAnimationFramesBreakpoint = null;
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Interval:
|
|
console.assert(this._allIntervalsBreakpoint, this._allIntervalsBreakpoint);
|
|
this._allIntervalsBreakpoint = null;
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Listener:
|
|
if (breakpoint.eventName) {
|
|
console.assert(this._listenerBreakpoints.includes(breakpoint), breakpoint);
|
|
if (!this._listenerBreakpoints.includes(breakpoint))
|
|
return;
|
|
|
|
this._listenerBreakpoints.remove(breakpoint);
|
|
} else {
|
|
console.assert(this._allListenersBreakpoint, this._allListenersBreakpoint);
|
|
this._allListenersBreakpoint = null;
|
|
}
|
|
break;
|
|
|
|
case WI.EventBreakpoint.Type.Timeout:
|
|
console.assert(this._allTimeoutsBreakpoint, this._allTimeoutsBreakpoint);
|
|
this._allTimeoutsBreakpoint = null;
|
|
break;
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.eventBreakpoints.deleteObject(breakpoint);
|
|
|
|
WI.debuggerManager.removeProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, {breakpoint});
|
|
}
|
|
|
|
urlBreakpointForURL(url)
|
|
{
|
|
return this._urlBreakpoints.find((breakpoint) => breakpoint.url === url) || null;
|
|
}
|
|
|
|
urlBreakpointsMatchingURL(url)
|
|
{
|
|
return this._urlBreakpoints
|
|
.filter((urlBreakpoint) => {
|
|
switch (urlBreakpoint.type) {
|
|
case WI.URLBreakpoint.Type.Text:
|
|
return urlBreakpoint.url.toLowerCase() === url.toLowerCase();
|
|
|
|
case WI.URLBreakpoint.Type.RegularExpression:
|
|
return (new RegExp(urlBreakpoint.url, "i")).test(url);
|
|
}
|
|
|
|
return false;
|
|
})
|
|
.sort((a, b) => {
|
|
// Order URL breakpoints based on how closely they match the given URL.
|
|
const typeRankings = [
|
|
WI.URLBreakpoint.Type.Text,
|
|
WI.URLBreakpoint.Type.RegularExpression,
|
|
];
|
|
return typeRankings.indexOf(a.type) - typeRankings.indexOf(b.type);
|
|
});
|
|
}
|
|
|
|
addURLBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.URLBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return false;
|
|
|
|
console.assert(!breakpoint.special, breakpoint);
|
|
if (breakpoint.url) {
|
|
if (this._urlBreakpoints.some((entry) => entry.type === breakpoint.type && entry.url === breakpoint.url))
|
|
return false;
|
|
|
|
this._urlBreakpoints.push(breakpoint);
|
|
} else {
|
|
console.assert(!this._allRequestsBreakpoint, this._allRequestsBreakpoint, breakpoint);
|
|
this._allRequestsBreakpoint = breakpoint;
|
|
}
|
|
|
|
WI.debuggerManager.addProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointAdded, {breakpoint});
|
|
|
|
if (!breakpoint.disabled) {
|
|
for (let target of WI.targets)
|
|
this._setURLBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.urlBreakpoints.putObject(breakpoint);
|
|
|
|
return true;
|
|
}
|
|
|
|
removeURLBreakpoint(breakpoint)
|
|
{
|
|
console.assert(breakpoint instanceof WI.URLBreakpoint, breakpoint);
|
|
if (!breakpoint)
|
|
return;
|
|
|
|
// Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
|
|
breakpoint.disabled = true;
|
|
breakpoint.clearActions();
|
|
|
|
if (breakpoint.url) {
|
|
console.assert(this._urlBreakpoints.includes(breakpoint), breakpoint);
|
|
if (!this._urlBreakpoints.includes(breakpoint))
|
|
return;
|
|
|
|
this._urlBreakpoints.remove(breakpoint);
|
|
} else {
|
|
console.assert(this._allRequestsBreakpoint, this._allRequestsBreakpoint);
|
|
this._allRequestsBreakpoint = null;
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.urlBreakpoints.deleteObject(breakpoint);
|
|
|
|
WI.debuggerManager.removeProbesForBreakpoint(breakpoint);
|
|
|
|
this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, {breakpoint});
|
|
}
|
|
|
|
// Private
|
|
|
|
_detachDOMBreakpointsForFrame(frame)
|
|
{
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame);
|
|
if (domBreakpointNodeIdentifierMap) {
|
|
this._domBreakpointFrameIdentifierMap.delete(frame);
|
|
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = true;
|
|
for (let breakpoint of domBreakpointNodeIdentifierMap.values())
|
|
breakpoint.domNode = null;
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = false;
|
|
}
|
|
|
|
for (let childFrame of frame.childFrameCollection)
|
|
this._detachDOMBreakpointsForFrame(childFrame);
|
|
}
|
|
|
|
_speculativelyResolveDOMBreakpointsForURL(url)
|
|
{
|
|
let domBreakpoints = this._domBreakpointURLMap.get(url);
|
|
if (!domBreakpoints)
|
|
return;
|
|
|
|
for (let breakpoint of domBreakpoints)
|
|
this._speculativelyResolveDOMBreakpoint(breakpoint);
|
|
}
|
|
|
|
_speculativelyResolveDOMBreakpoint(breakpoint)
|
|
{
|
|
if (breakpoint.domNode)
|
|
return;
|
|
|
|
WI.domManager.pushNodeByPathToFrontend(breakpoint.path, (nodeIdentifier) => {
|
|
if (!nodeIdentifier)
|
|
return;
|
|
|
|
if (breakpoint.domNode) {
|
|
// This breakpoint may have been resolved by a node being inserted before this
|
|
// callback is invoked. If so, the `nodeIdentifier` should match, so don't try
|
|
// to resolve it again as it would've already been resolved.
|
|
console.assert(breakpoint.domNode.id === nodeIdentifier);
|
|
return;
|
|
}
|
|
|
|
this._restoringBreakpoints = true;
|
|
this._resolveDOMBreakpoint(breakpoint, WI.domManager.nodeForId(nodeIdentifier));
|
|
this._restoringBreakpoints = false;
|
|
});
|
|
}
|
|
|
|
_resolveDOMBreakpoint(breakpoint, node)
|
|
{
|
|
console.assert(node instanceof WI.DOMNode, node);
|
|
|
|
if (!node.frame)
|
|
return;
|
|
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
|
|
if (!domBreakpointNodeIdentifierMap) {
|
|
domBreakpointNodeIdentifierMap = new Multimap;
|
|
this._domBreakpointFrameIdentifierMap.set(node.frame, domBreakpointNodeIdentifierMap);
|
|
}
|
|
|
|
domBreakpointNodeIdentifierMap.add(node, breakpoint);
|
|
|
|
breakpoint.domNode = node;
|
|
}
|
|
|
|
_setDOMBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(!breakpoint.disabled, breakpoint);
|
|
console.assert(breakpoint.domNode instanceof WI.DOMNode, breakpoint);
|
|
console.assert(target.type !== WI.TargetType.Worker, "Worker targets do not support DOM breakpoints", target);
|
|
|
|
if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
|
|
WI.debuggerManager.breakpointsEnabled = true;
|
|
|
|
target.DOMDebuggerAgent.setDOMBreakpoint.invoke({
|
|
nodeId: breakpoint.domNode.id,
|
|
type: breakpoint.type,
|
|
options: breakpoint.optionsToProtocol(),
|
|
});
|
|
}
|
|
|
|
_removeDOMBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(breakpoint.domNode instanceof WI.DOMNode, breakpoint);
|
|
console.assert(target.type !== WI.TargetType.Worker, "Worker targets do not support DOM breakpoints", target);
|
|
|
|
target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNode.id, breakpoint.type);
|
|
}
|
|
|
|
_commandArgumentsForEventBreakpoint(breakpoint)
|
|
{
|
|
let commandArguments = {};
|
|
|
|
switch (breakpoint) {
|
|
case this._allAnimationFramesBreakpoint:
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.AnimationFrame;
|
|
if (!DOMDebuggerManager.supportsAllListenersBreakpoint())
|
|
commandArguments.eventName = "requestAnimationFrame";
|
|
break;
|
|
|
|
case this._allIntervalsBreakpoint:
|
|
if (DOMDebuggerManager.supportsAllListenersBreakpoint())
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.Interval;
|
|
else {
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.Timer;
|
|
commandArguments.eventName = "setInterval";
|
|
}
|
|
break;
|
|
|
|
case this._allListenersBreakpoint:
|
|
if (!DOMDebuggerManager.supportsAllListenersBreakpoint())
|
|
return;
|
|
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.Listener;
|
|
break;
|
|
|
|
case this._allTimeoutsBreakpoint:
|
|
if (DOMDebuggerManager.supportsAllListenersBreakpoint())
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.Timeout;
|
|
else {
|
|
commandArguments.breakpointType = WI.EventBreakpoint.Type.Timer;
|
|
commandArguments.eventName = "setTimeout";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener, breakpoint.type);
|
|
console.assert(breakpoint.eventName, breakpoint.eventName);
|
|
commandArguments.breakpointType = breakpoint.type;
|
|
commandArguments.eventName = breakpoint.eventName;
|
|
commandArguments.caseSensitive = breakpoint.caseSensitive;
|
|
commandArguments.isRegex = breakpoint.isRegex;
|
|
break;
|
|
}
|
|
|
|
return commandArguments;
|
|
}
|
|
|
|
_setEventBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(!breakpoint.disabled, breakpoint);
|
|
|
|
// Worker targets do not support `requestAnimationFrame` breakpoints.
|
|
if (breakpoint === this._allAnimationFramesBreakpoint && target.type === WI.TargetType.Worker)
|
|
return;
|
|
|
|
// COMPATIBILITY (iOS 12.0): DOMDebugger.setEventListenerBreakpoint was replaced by DOMDebugger.setEventBreakpoint.
|
|
if (!target.hasCommand("DOMDebugger.setEventBreakpoint")) {
|
|
console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
|
|
|
|
if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
|
|
WI.debuggerManager.breakpointsEnabled = true;
|
|
|
|
target.DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName);
|
|
return;
|
|
}
|
|
|
|
let commandArguments = this._commandArgumentsForEventBreakpoint(breakpoint);
|
|
|
|
if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
|
|
WI.debuggerManager.breakpointsEnabled = true;
|
|
|
|
commandArguments.options = breakpoint.optionsToProtocol();
|
|
|
|
target.DOMDebuggerAgent.setEventBreakpoint.invoke(commandArguments);
|
|
}
|
|
|
|
_removeEventBreakpoint(breakpoint, target)
|
|
{
|
|
// Worker targets do not support `requestAnimationFrame` breakpoints.
|
|
if (breakpoint === this._allAnimationFramesBreakpoint && target.type === WI.TargetType.Worker)
|
|
return;
|
|
|
|
// COMPATIBILITY (iOS 12.0): DOMDebugger.removeEventListenerBreakpoint was replaced by DOMDebugger.removeEventBreakpoint.
|
|
if (!target.hasCommand("DOMDebugger.removeEventBreakpoint")) {
|
|
console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
|
|
target.DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName);
|
|
return;
|
|
}
|
|
|
|
let commandArguments = this._commandArgumentsForEventBreakpoint(breakpoint);
|
|
|
|
target.DOMDebuggerAgent.removeEventBreakpoint.invoke(commandArguments);
|
|
}
|
|
|
|
_setURLBreakpoint(breakpoint, target)
|
|
{
|
|
console.assert(!breakpoint.disabled, breakpoint);
|
|
|
|
if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
|
|
WI.debuggerManager.breakpointsEnabled = true;
|
|
|
|
// COMPATIBILITY (iOS 12.2): DOMDebugger.setXHRBreakpoint was replaced by DOMDebugger.setURLBreakpoint.
|
|
if (!target.hasCommand("DOMDebugger.setURLBreakpoint")) {
|
|
let isRegex = breakpoint.type === WI.URLBreakpoint.Type.RegularExpression;
|
|
target.DOMDebuggerAgent.setXHRBreakpoint(breakpoint.url, isRegex);
|
|
return;
|
|
}
|
|
|
|
|
|
target.DOMDebuggerAgent.setURLBreakpoint.invoke({
|
|
url: breakpoint.url,
|
|
isRegex: breakpoint.type === WI.URLBreakpoint.Type.RegularExpression,
|
|
options: breakpoint.optionsToProtocol(),
|
|
});
|
|
}
|
|
|
|
_removeURLBreakpoint(breakpoint, target)
|
|
{
|
|
// COMPATIBILITY (iOS 12.2): DOMDebugger.removeXHRBreakpoint was replaced by DOMDebugger.removeURLBreakpoint.
|
|
if (!target.hasCommand("DOMDebugger.removeURLBreakpoint")) {
|
|
target.DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url);
|
|
return;
|
|
}
|
|
|
|
target.DOMDebuggerAgent.removeURLBreakpoint.invoke({
|
|
url: breakpoint.url,
|
|
isRegex: breakpoint.type === WI.URLBreakpoint.Type.RegularExpression,
|
|
});
|
|
}
|
|
|
|
_handleDOMBreakpointDisabledStateChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.domBreakpoints.putObject(breakpoint);
|
|
|
|
if (!breakpoint.domNode)
|
|
return;
|
|
|
|
// We should get the target associated with the nodeIdentifier of this breakpoint.
|
|
let target = WI.assumingMainTarget();
|
|
if (target) {
|
|
if (breakpoint.disabled)
|
|
this._removeDOMBreakpoint(breakpoint, target);
|
|
else
|
|
this._setDOMBreakpoint(breakpoint, target);
|
|
}
|
|
}
|
|
|
|
_handleDOMBreakpointEditablePropertyChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.domBreakpoints.putObject(breakpoint);
|
|
|
|
if (!breakpoint.domNode)
|
|
return;
|
|
|
|
if (breakpoint.disabled)
|
|
return;
|
|
|
|
this._restoringBreakpoints = true;
|
|
// We should get the target associated with the nodeIdentifier of this breakpoint.
|
|
let target = WI.assumingMainTarget();
|
|
if (target) {
|
|
// Clear the old breakpoint from the backend before setting the new one.
|
|
this._removeDOMBreakpoint(breakpoint, target);
|
|
this._setDOMBreakpoint(breakpoint, target);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
|
|
_handleDOMBreakpointActionsChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
this._handleDOMBreakpointEditablePropertyChanged(event);
|
|
|
|
if (!breakpoint.domNode)
|
|
return;
|
|
|
|
WI.debuggerManager.updateProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_handleDOMBreakpointDOMNodeWillChange(event)
|
|
{
|
|
if (this._clearingDOMBreakpointsForRemovedDOMNode)
|
|
return;
|
|
|
|
let breakpoint = event.target;
|
|
|
|
if (!breakpoint.domNode)
|
|
return;
|
|
|
|
if (!breakpoint.disabled) {
|
|
// We should get the target associated with the nodeIdentifier of this breakpoint.
|
|
let target = WI.assumingMainTarget();
|
|
if (target)
|
|
this._removeDOMBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
WI.debuggerManager.removeProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_handleDOMBreakpointDOMNodeDidChange(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (!breakpoint.domNode)
|
|
return;
|
|
|
|
if (!breakpoint.disabled) {
|
|
// We should get the target associated with the nodeIdentifier of this breakpoint.
|
|
let target = WI.assumingMainTarget();
|
|
if (target)
|
|
this._setDOMBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
WI.debuggerManager.addProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_handleEventBreakpointDisabledStateChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
// Specific event listener breakpoints are handled by `DOMManager`.
|
|
if (breakpoint.eventListener)
|
|
return;
|
|
|
|
for (let target of WI.targets) {
|
|
if (breakpoint.disabled)
|
|
this._removeEventBreakpoint(breakpoint, target);
|
|
else
|
|
this._setEventBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.eventBreakpoints.putObject(breakpoint);
|
|
}
|
|
|
|
_handleEventBreakpointEditablePropertyChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
// Specific event listener breakpoints are handled by `DOMManager`.
|
|
if (breakpoint.eventListener)
|
|
return;
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.eventBreakpoints.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._removeEventBreakpoint(breakpoint, target);
|
|
this._setEventBreakpoint(breakpoint, target);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
|
|
_handleEventBreakpointActionsChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
// Specific event listener breakpoints are handled by `DOMManager`.
|
|
if (breakpoint.eventListener)
|
|
return;
|
|
|
|
this._handleEventBreakpointEditablePropertyChanged(event);
|
|
|
|
WI.debuggerManager.updateProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_handleURLBreakpointDisabledStateChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
for (let target of WI.targets) {
|
|
if (breakpoint.disabled)
|
|
this._removeURLBreakpoint(breakpoint, target);
|
|
else
|
|
this._setURLBreakpoint(breakpoint, target);
|
|
}
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.urlBreakpoints.putObject(breakpoint);
|
|
}
|
|
|
|
_handleURLBreakpointEditablePropertyChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
if (!this._restoringBreakpoints)
|
|
WI.objectStores.urlBreakpoints.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._removeURLBreakpoint(breakpoint, target)
|
|
this._setURLBreakpoint(breakpoint, target);
|
|
}
|
|
this._restoringBreakpoints = false;
|
|
}
|
|
|
|
_handleURLBreakpointActionsChanged(event)
|
|
{
|
|
let breakpoint = event.target;
|
|
|
|
this._handleURLBreakpointEditablePropertyChanged(event);
|
|
|
|
WI.debuggerManager.updateProbesForBreakpoint(breakpoint);
|
|
}
|
|
|
|
_childFrameWasRemoved(event)
|
|
{
|
|
let frame = event.data.childFrame;
|
|
this._detachDOMBreakpointsForFrame(frame);
|
|
}
|
|
|
|
_mainFrameDidChange(event)
|
|
{
|
|
this._speculativelyResolveDOMBreakpointsForURL(WI.networkManager.mainFrame.url);
|
|
}
|
|
|
|
_mainResourceDidChange(event)
|
|
{
|
|
let frame = event.target;
|
|
if (frame.isMainFrame()) {
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = true;
|
|
for (let breakpoint of this._domBreakpointURLMap.values())
|
|
breakpoint.domNode = null;
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = false;
|
|
|
|
this._domBreakpointFrameIdentifierMap.clear();
|
|
} else
|
|
this._detachDOMBreakpointsForFrame(frame);
|
|
|
|
this._speculativelyResolveDOMBreakpointsForURL(frame.url);
|
|
}
|
|
|
|
_nodeInserted(event)
|
|
{
|
|
let node = event.data.node;
|
|
if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
|
|
return;
|
|
|
|
let url = node.frame.url;
|
|
let breakpoints = this._domBreakpointURLMap.get(url);
|
|
if (!breakpoints)
|
|
return;
|
|
|
|
let resolvableBreakpoints = [];
|
|
for (let breakpoint of breakpoints) {
|
|
if (!breakpoint.domNode)
|
|
resolvableBreakpoints.push(breakpoint);
|
|
}
|
|
if (!resolvableBreakpoints.length)
|
|
return;
|
|
|
|
// This is not very expensive because `WI.DOMNode` children are lazily populated, so it's
|
|
// unlikely that there will be a deep subtree to walk.
|
|
let stack = [node];
|
|
while (stack.length) {
|
|
let child = stack.pop();
|
|
let path = child.path();
|
|
|
|
for (let i = resolvableBreakpoints.length - 1; i >= 0; --i) {
|
|
if (resolvableBreakpoints[i].path === path) {
|
|
this._restoringBreakpoints = true;
|
|
this._resolveDOMBreakpoint(resolvableBreakpoints[i], child);
|
|
this._restoringBreakpoints = false;
|
|
|
|
resolvableBreakpoints.splice(i, 1);
|
|
}
|
|
}
|
|
if (!resolvableBreakpoints.length)
|
|
break;
|
|
|
|
if (child.children?.length)
|
|
stack.pushAll(child.children);
|
|
}
|
|
}
|
|
|
|
_nodeRemoved(event)
|
|
{
|
|
let node = event.data.node;
|
|
if (node.nodeType() !== Node.ELEMENT_NODE || !node.frame)
|
|
return;
|
|
|
|
let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
|
|
if (!domBreakpointNodeIdentifierMap)
|
|
return;
|
|
|
|
for (let [breakpointOwner, breakpoints] of domBreakpointNodeIdentifierMap.sets()) {
|
|
if (breakpointOwner == node || node.isAncestor(breakpointOwner)) {
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = true;
|
|
for (let breakpoint of breakpoints)
|
|
breakpoint.domNode = null;
|
|
this._clearingDOMBreakpointsForRemovedDOMNode = false;
|
|
|
|
domBreakpointNodeIdentifierMap.delete(breakpointOwner);
|
|
if (!domBreakpointNodeIdentifierMap.size) {
|
|
this._domBreakpointFrameIdentifierMap.delete(node.frame);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
WI.DOMDebuggerManager.Event = {
|
|
DOMBreakpointAdded: "dom-debugger-manager-dom-breakpoint-added",
|
|
DOMBreakpointRemoved: "dom-debugger-manager-dom-breakpoint-removed",
|
|
EventBreakpointAdded: "dom-debugger-manager-event-breakpoint-added",
|
|
EventBreakpointRemoved: "dom-debugger-manager-event-breakpoint-removed",
|
|
URLBreakpointAdded: "dom-debugger-manager-url-breakpoint-added",
|
|
URLBreakpointRemoved: "dom-debugger-manager-url-breakpoint-removed",
|
|
};
|