Changeset 266669 in webkit


Ignore:
Timestamp:
Sep 5, 2020 1:06:22 PM (4 years ago)
Author:
Devin Rousso
Message:

Web Inspector: allow DOM breakpoints to be configured
https://bugs.webkit.org/show_bug.cgi?id=215795

Reviewed by Brian Burg.

Source/JavaScriptCore:

  • inspector/protocol/DOMDebugger.json:

Add an options parameter to DOMDebugger.setDOMBreakpoint to allow configuration.

Source/WebCore:

Tests: inspector/dom-debugger/attribute-modified-style.html

inspector/dom-debugger/dom-breakpoints.html
inspector/dom-debugger/dom-breakpoint-attribute-modified.html
inspector/dom-debugger/dom-breakpoint-node-removed-ancestor.html
inspector/dom-debugger/dom-breakpoint-node-removed-direct.html
inspector/dom-debugger/dom-breakpoint-subtree-modified-add.html
inspector/dom-debugger/dom-breakpoint-subtree-modified-remove.html

  • inspector/agents/page/PageDOMDebuggerAgent.h:
  • inspector/agents/page/PageDOMDebuggerAgent.cpp:

(WebCore::PageDOMDebuggerAgent::disable):
(WebCore::PageDOMDebuggerAgent::setDOMBreakpoint):
(WebCore::PageDOMDebuggerAgent::removeDOMBreakpoint):
(WebCore::PageDOMDebuggerAgent::frameDocumentUpdated):
(WebCore::calculateDistance): Added.
(WebCore::PageDOMDebuggerAgent::willInsertDOMNode):
(WebCore::PageDOMDebuggerAgent::willRemoveDOMNode):
(WebCore::PageDOMDebuggerAgent::didRemoveDOMNode):
(WebCore::PageDOMDebuggerAgent::willModifyDOMAttr):
(WebCore::PageDOMDebuggerAgent::willInvalidateStyleAttr):
(WebCore::PageDOMDebuggerAgent::buildPauseDataForDOMBreakpoint): Added.
(WebCore::domTypeForName): Deleted.
(WebCore::domTypeName): Deleted.
(WebCore::PageDOMDebuggerAgent::didInsertDOMNode): Deleted.
(WebCore::PageDOMDebuggerAgent::descriptionForDOMEvent): Deleted.
(WebCore::PageDOMDebuggerAgent::updateSubtreeBreakpoints): Deleted.
(WebCore::PageDOMDebuggerAgent::hasBreakpoint): Deleted.
Replace the bitmask with separate HashMap for each type of DOM breakpoint. Instead of
propagating the SubtreeModified bit to the entire subtree when new nodes are added (which
means there's an entry in the HashMap for every descendant) and removing them all when
that node is removed, only keep nodes in each HashMap if it directly has a DOM breakpoint.
Walk up the ancestor chain when nodes are added/removed to determine if there is a nearby
breakpoint to pause. When a node is removed, remove any existing DOM breakpoint if it's
owner is part of the removed subtree.

  • inspector/agents/worker/WorkerDOMDebuggerAgent.h:
  • inspector/agents/worker/WorkerDOMDebuggerAgent.cpp:

(WebCore::WorkerDOMDebuggerAgent::setDOMBreakpoint):

  • inspector/InspectorInstrumentation.cpp:

(WebCore::InspectorInstrumentation::didInsertDOMNodeImpl):

Source/WebInspectorUI:

  • UserInterface/Controllers/DOMManager.js:

(WI.DOMManager.prototype._setChildNodes):
Dispatch events for each new child node added and each existing child node removed so that
any listeners can also know when new nodes are added via a full children payload update.

  • UserInterface/Controllers/DOMDebuggerManager.js:

(WI.DOMDebuggerManager):
(WI.DOMDebuggerManager.prototype.addDOMBreakpoint):
(WI.DOMDebuggerManager.prototype.removeDOMBreakpoint):
(WI.DOMDebuggerManager.prototype._detachDOMBreakpointsForFrame): Added.
(WI.DOMDebuggerManager.prototype._speculativelyResolveDOMBreakpointsForURL):
(WI.DOMDebuggerManager.prototype._speculativelyResolveDOMBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._resolveDOMBreakpoint):
(WI.DOMDebuggerManager.prototype._setDOMBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._removeDOMBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._handleDOMBreakpointDisabledStateChanged):
(WI.DOMDebuggerManager.prototype._handleDOMBreakpointEditablePropertyChanged): Added.
(WI.DOMDebuggerManager.prototype._handleDOMBreakpointActionsChanged): Added.
(WI.DOMDebuggerManager.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
(WI.DOMDebuggerManager.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
(WI.DOMDebuggerManager.prototype._childFrameWasRemoved):
(WI.DOMDebuggerManager.prototype._mainResourceDidChange):
(WI.DOMDebuggerManager.prototype._nodeInserted):
(WI.DOMDebuggerManager.prototype._nodeRemoved):
(WI.DOMDebuggerManager.prototype._detachDOMBreakpoint): Deleted.
(WI.DOMDebuggerManager.prototype._detachBreakpointsForFrame): Deleted.
(WI.DOMDebuggerManager.prototype._updateDOMBreakpoint): Deleted.
Recursively walk any added node's subtree to resolve and set any matching DOM breakpoints.
Iterate all existing DOM breakpoints to see if the owner node is part of the subtree of any
removed nodes and remove ot if so.

  • UserInterface/Models/DOMBreakpoint.js:

(WI.DOMBreakpoint):
(WI.DOMBreakpoint.displayNameForType):
(WI.DOMBreakpoint.fromJSON):
(WI.DOMBreakpoint.prototype.get editable): Added.
(WI.DOMBreakpoint.prototype.get domNode): Added.
(WI.DOMBreakpoint.prototype.set domNode): Added.
(WI.DOMBreakpoint.prototype.get domNodeIdentifier): Deleted.
(WI.DOMBreakpoint.prototype.set domNodeIdentifier): Deleted.

  • UserInterface/Views/DOMTreeContentView.js:

(WI.DOMTreeContentView):
(WI.DOMTreeContentView.prototype.closed):
(WI.DOMTreeContentView.prototype._domTreeElementAdded):
(WI.DOMTreeContentView.prototype._domBreakpointAddedOrRemoved):
(WI.DOMTreeContentView.prototype._handleDOMBreakpointDisabledStateChanged):
(WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
(WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
(WI.DOMTreeContentView.prototype._updateBreakpointStatus):
(WI.DOMTreeContentView.prototype._restoreBreakpointsAfterUpdate):
(WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeChanged): Deleted.

  • UserInterface/Views/SourcesNavigationSidebarPanel.js:

(WI.SourcesNavigationSidebarPanel):
(WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
(WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
(WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
(WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeChanged): Deleted.
Use a WI.DOMNode instead of an identifier instead of fetching the node for the identifier
each time and checking if is valid.

LayoutTests:

  • inspector/debugger/resources/breakpoint-options-utilities.js:

(TestPage.registerInitializer.InspectorTest.BreakpointOptions.addTestCases):

  • inspector/dom-debugger/resources/dom-breakpoint-utilities.js: Added.

(TestPage.registerInitializer.InspectorTest.DOMBreakpoint.teardown):
(TestPage.registerInitializer.InspectorTest.DOMBreakpoint.createBreakpoint):
(TestPage.registerInitializer.InspectorTest.DOMBreakpoint.addBreakpoint):
(TestPage.registerInitializer.InspectorTest.DOMBreakpoint.awaitQuerySelector):

  • inspector/dom-debugger/attribute-modified-style.html:
  • inspector/dom-debugger/attribute-modified-style-expected.txt:
  • inspector/dom-debugger/dom-breakpoints.html:
  • inspector/dom-debugger/dom-breakpoints-expected.txt:
  • inspector/dom-debugger/dom-breakpoint-attribute-modified.html: Added.
  • inspector/dom-debugger/dom-breakpoint-attribute-modified-expected.txt: Added.
  • inspector/dom-debugger/dom-breakpoint-node-removed-ancestor.html: Added.
  • inspector/dom-debugger/dom-breakpoint-node-removed-ancestor-expected.txt: Added.
  • inspector/dom-debugger/dom-breakpoint-node-removed-direct.html: Added.
  • inspector/dom-debugger/dom-breakpoint-node-removed-direct-expected.txt: Added.
  • inspector/dom-debugger/dom-breakpoint-subtree-modified-add.html: Added.
  • inspector/dom-debugger/dom-breakpoint-subtree-modified-remove-expected.txt: Added.
  • inspector/dom-debugger/dom-breakpoint-subtree-modified-remove.html: Added.
  • inspector/dom-debugger/dom-breakpoint-subtree-modified-add-expected.txt: Added.
Location:
trunk
Files:
11 added
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r266665 r266669  
     12020-09-05  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: allow DOM breakpoints to be configured
     4        https://bugs.webkit.org/show_bug.cgi?id=215795
     5
     6        Reviewed by Brian Burg.
     7
     8        * inspector/debugger/resources/breakpoint-options-utilities.js:
     9        (TestPage.registerInitializer.InspectorTest.BreakpointOptions.addTestCases):
     10
     11        * inspector/dom-debugger/resources/dom-breakpoint-utilities.js: Added.
     12        (TestPage.registerInitializer.InspectorTest.DOMBreakpoint.teardown):
     13        (TestPage.registerInitializer.InspectorTest.DOMBreakpoint.createBreakpoint):
     14        (TestPage.registerInitializer.InspectorTest.DOMBreakpoint.addBreakpoint):
     15        (TestPage.registerInitializer.InspectorTest.DOMBreakpoint.awaitQuerySelector):
     16
     17        * inspector/dom-debugger/attribute-modified-style.html:
     18        * inspector/dom-debugger/attribute-modified-style-expected.txt:
     19        * inspector/dom-debugger/dom-breakpoints.html:
     20        * inspector/dom-debugger/dom-breakpoints-expected.txt:
     21        * inspector/dom-debugger/dom-breakpoint-attribute-modified.html: Added.
     22        * inspector/dom-debugger/dom-breakpoint-attribute-modified-expected.txt: Added.
     23        * inspector/dom-debugger/dom-breakpoint-node-removed-ancestor.html: Added.
     24        * inspector/dom-debugger/dom-breakpoint-node-removed-ancestor-expected.txt: Added.
     25        * inspector/dom-debugger/dom-breakpoint-node-removed-direct.html: Added.
     26        * inspector/dom-debugger/dom-breakpoint-node-removed-direct-expected.txt: Added.
     27        * inspector/dom-debugger/dom-breakpoint-subtree-modified-add.html: Added.
     28        * inspector/dom-debugger/dom-breakpoint-subtree-modified-remove-expected.txt: Added.
     29        * inspector/dom-debugger/dom-breakpoint-subtree-modified-remove.html: Added.
     30        * inspector/dom-debugger/dom-breakpoint-subtree-modified-add-expected.txt: Added.
     31
    1322020-09-05  Myles C. Maxfield  <mmaxfield@apple.com>
    233
  • trunk/LayoutTests/inspector/debugger/resources/breakpoint-options-utilities.js

    r266538 r266669  
    22    InspectorTest.BreakpointOptions = {};
    33
    4     InspectorTest.BreakpointOptions.addTestCases = function(suite, {testCaseNamePrefix, createBreakpoint, triggerBreakpoint}) {
     4    InspectorTest.BreakpointOptions.addTestCases = function(suite, {testCaseNamePrefix, createBreakpoint, triggerBreakpoint, skip}) {
    55        testCaseNamePrefix ??= "";
    66
     
    4343
    4444                    InspectorTest.log("Triggering breakpoint...");
    45                     await triggerBreakpoint();
     45                    await triggerBreakpoint(breakpoint);
    4646
    4747                    if (i <= 2)
     
    5757        });
    5858
    59         suite.addTestCase({
    60             name: suite.name + "." + testCaseNamePrefix + "Options.IgnoreCount",
    61             description: "Check that the debugger will not pause unless the breakpoint is hit at least as many times as it's `ignoreCount`.",
    62             async test() {
    63                 let pauseCount = 0;
    64 
    65                 let pausedListener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
    66                     ++pauseCount;
    67                     WI.debuggerManager.resume();
    68                 });
    69 
    70                 let breakpoint = await createBreakpoint();
    71 
    72                 InspectorTest.newline();
    73 
    74                 InspectorTest.log("Setting ignoreCount to '2'...");
    75                 breakpoint.ignoreCount = 2;
    76 
    77                 for (let i = 1; i <=4; ++i) {
    78                     InspectorTest.newline();
    79 
    80                     InspectorTest.log("Triggering breakpoint...");
    81                     await triggerBreakpoint();
    82 
    83                     if (i <= 2)
    84                         InspectorTest.expectEqual(pauseCount, 0, "Should not pause.");
    85                     else
    86                         InspectorTest.expectEqual(pauseCount, i - 2, "Should pause.");
    87                 }
    88 
    89                 removeBreakpoint(breakpoint);
    90 
    91                 WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, pausedListener);
    92             },
    93         });
     59        if (!skip?.ignoreCount) {
     60            suite.addTestCase({
     61                name: suite.name + "." + testCaseNamePrefix + "Options.IgnoreCount",
     62                description: "Check that the debugger will not pause unless the breakpoint is hit at least as many times as it's `ignoreCount`.",
     63                async test() {
     64                    let pauseCount = 0;
     65
     66                    let pausedListener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
     67                        ++pauseCount;
     68                        WI.debuggerManager.resume();
     69                    });
     70
     71                    let breakpoint = await createBreakpoint();
     72
     73                    InspectorTest.newline();
     74
     75                    InspectorTest.log("Setting ignoreCount to '2'...");
     76                    breakpoint.ignoreCount = 2;
     77
     78                    for (let i = 1; i <=4; ++i) {
     79                        InspectorTest.newline();
     80
     81                        InspectorTest.log("Triggering breakpoint...");
     82                        await triggerBreakpoint(breakpoint);
     83
     84                        if (i <= 2)
     85                            InspectorTest.expectEqual(pauseCount, 0, "Should not pause.");
     86                        else
     87                            InspectorTest.expectEqual(pauseCount, i - 2, "Should pause.");
     88                    }
     89
     90                    removeBreakpoint(breakpoint);
     91
     92                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, pausedListener);
     93                },
     94            });
     95        }
    9496
    9597        suite.addTestCase({
     
    134136
    135137                    InspectorTest.log("Triggering breakpoint...");
    136                     await triggerBreakpoint();
     138                    await triggerBreakpoint(breakpoint);
    137139
    138140                    WI.consoleManager.removeEventListener(WI.ConsoleManager.Event.MessageAdded, messageAddedListener);
     
    187189
    188190                    InspectorTest.log("Triggering breakpoint...");
    189                     await triggerBreakpoint();
     191                    await triggerBreakpoint(breakpoint);
    190192
    191193                    let breakpointActionEvaluateResult = await InspectorTest.evaluateInPage(`window.BREAKPOINT_ACTION_EVALUATE`);
  • trunk/LayoutTests/inspector/dom-debugger/attribute-modified-style-expected.txt

    r244269 r266669  
     1CONSOLE MESSAGE: BREAKPOINT ACTION LOG 1
     2CONSOLE MESSAGE: BREAKPOINT ACTION LOG 2
     3CONSOLE MESSAGE: BREAKPOINT ACTION LOG 3
     4CONSOLE MESSAGE: BREAKPOINT ACTION LOG 4
    15Tests functionality of attribute modified DOM breakpoints when modifying the inline style attribute.
    26
    3 Adding attribute modified breakpoint to #test...
    4 PASS: Added attribute modified breakpoint to #test.
    57
    6 == Running test suite: DOMBreakpoints.AttributeModified.Style
    7 -- Running test case: DOMBreakpoints.AttributeModified.Style.Add
     8== Running test suite: DOMBreakpoint.AttributeModified
     9-- Running test case: DOMBreakpoint.AttributeModified.Style.Add
     10Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
    811Getting 'display'...
    912PASS: 'display' should have the value ''.
     
    1518Getting 'display'...
    1619PASS: 'display' should have the value 'none'.
     20-- Running test teardown.
    1721
    18 -- Running test case: DOMBreakpoints.AttributeModified.Style.ReplaceSame
     22-- Running test case: DOMBreakpoint.AttributeModified.Style.ReplaceSame
     23Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
    1924Getting 'display'...
    2025PASS: 'display' should have the value 'none'.
     
    2631Getting 'display'...
    2732PASS: 'display' should have the value 'none'.
     33-- Running test teardown.
    2834
    29 -- Running test case: DOMBreakpoints.AttributeModified.Style.ReplaceDifferent
     35-- Running test case: DOMBreakpoint.AttributeModified.Style.ReplaceDifferent
     36Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
    3037Getting 'display'...
    3138PASS: 'display' should have the value 'none'.
     
    3744Getting 'display'...
    3845PASS: 'display' should have the value 'block'.
     46-- Running test teardown.
    3947
    40 -- Running test case: DOMBreakpoints.AttributeModified.Style.Remove
     48-- Running test case: DOMBreakpoint.AttributeModified.Style.Remove
     49Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
    4150Getting 'display'...
    4251PASS: 'display' should have the value 'block'.
     
    4857Getting 'display'...
    4958PASS: 'display' should have the value ''.
     59-- Running test teardown.
    5060
    51 -- Running test case: DOMBreakpoints.AttributeModified.Style.Text
     61-- Running test case: DOMBreakpoint.AttributeModified.Style.Text
     62Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
    5263Getting 'display'...
    5364PASS: 'display' should have the value ''.
     
    5970Getting 'display'...
    6071PASS: 'display' should have the value 'none'.
     72-- Running test teardown.
    6173
     74-- Running test case: DOMBreakpoint.AttributeModified.Style.Options.Condition
     75Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
     76
     77Setting condition to 'false'...
     78
     79Triggering breakpoint...
     80PASS: Should not pause.
     81
     82Triggering breakpoint...
     83PASS: Should not pause.
     84
     85Setting condition to 'true'...
     86
     87Triggering breakpoint...
     88PASS: Should pause.
     89
     90Triggering breakpoint...
     91PASS: Should pause.
     92
     93-- Running test case: DOMBreakpoint.AttributeModified.Style.Options.IgnoreCount
     94Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
     95
     96Setting ignoreCount to '2'...
     97
     98Triggering breakpoint...
     99PASS: Should not pause.
     100
     101Triggering breakpoint...
     102PASS: Should not pause.
     103
     104Triggering breakpoint...
     105PASS: Should pause.
     106
     107Triggering breakpoint...
     108PASS: Should pause.
     109
     110-- Running test case: DOMBreakpoint.AttributeModified.Style.Options.Action.Log
     111Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
     112
     113Adding log action...
     114
     115Triggering breakpoint...
     116PASS: Should execute breakpoint action.
     117PASS: Should pause.
     118
     119Editing log action...
     120
     121Triggering breakpoint...
     122PASS: Should execute breakpoint action.
     123PASS: Should pause.
     124
     125Editing log action...
     126Enabling auto-continue...
     127
     128Triggering breakpoint...
     129PASS: Should execute breakpoint action.
     130PASS: Should not pause.
     131
     132Editing log action...
     133
     134Triggering breakpoint...
     135PASS: Should execute breakpoint action.
     136PASS: Should not pause.
     137
     138-- Running test case: DOMBreakpoint.AttributeModified.Style.Options.Actions.Evaluate
     139Adding "attribute-modified:1,HTML,1,BODY,1,DIV" DOM Breakpoint...
     140
     141Adding evaluate action...
     142
     143Triggering breakpoint...
     144PASS: Should execute breakpoint action.
     145PASS: Should pause.
     146
     147Editing evaluate action...
     148
     149Triggering breakpoint...
     150PASS: Should execute breakpoint action.
     151PASS: Should pause.
     152
     153Editing evaluate action...
     154Enabling auto-continue...
     155
     156Triggering breakpoint...
     157PASS: Should execute breakpoint action.
     158PASS: Should not pause.
     159
     160Editing evaluate action...
     161
     162Triggering breakpoint...
     163PASS: Should execute breakpoint action.
     164PASS: Should not pause.
     165
  • trunk/LayoutTests/inspector/dom-debugger/attribute-modified-style.html

    r253242 r266669  
    33<head>
    44<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
     5<script src="../debugger/resources/breakpoint-options-utilities.js"></script>
    56<script src="../debugger/resources/log-active-stack-trace.js"></script>
     7<script src="resources/dom-breakpoint-utilities.js"></script>
    68<script>
    79
     
    2123function test()
    2224{
    23     InspectorTest.debug();
    24 
    25     let suite = InspectorTest.createAsyncSuite("DOMBreakpoints.AttributeModified.Style");
     25    let suite = InspectorTest.createAsyncSuite("DOMBreakpoint.AttributeModified");
    2626
    2727    async function checkPropertyValue(name, expectedValue) {
     
    3535            name,
    3636            async test() {
     37                let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("#test");
     38                await InspectorTest.DOMBreakpoint.createBreakpoint(node, WI.DOMBreakpoint.Type.AttributeModified);
     39
    3740                await checkPropertyValue(propertyName, propertyValueBefore);
    3841
     
    5760                await checkPropertyValue(propertyName, propertyValueAfter);
    5861            },
     62            teardown: InspectorTest.DOMBreakpoint.teardown,
    5963        });
    6064    }
    6165
    6266    addTestCase({
    63         name: "DOMBreakpoints.AttributeModified.Style.Add",
     67        name: "DOMBreakpoint.AttributeModified.Style.Add",
    6468        propertyName: "display",
    6569        propertyValueBefore: "",
     
    6771    });
    6872    addTestCase({
    69         name: "DOMBreakpoints.AttributeModified.Style.ReplaceSame",
     73        name: "DOMBreakpoint.AttributeModified.Style.ReplaceSame",
    7074        propertyName: "display",
    7175        propertyValueBefore: "none",
     
    7377    });
    7478    addTestCase({
    75         name: "DOMBreakpoints.AttributeModified.Style.ReplaceDifferent",
     79        name: "DOMBreakpoint.AttributeModified.Style.ReplaceDifferent",
    7680        propertyName: "display",
    7781        propertyValueBefore: "none",
     
    7983    });
    8084    addTestCase({
    81         name: "DOMBreakpoints.AttributeModified.Style.Remove",
     85        name: "DOMBreakpoint.AttributeModified.Style.Remove",
    8286        propertyName: "display",
    8387        propertyValueBefore: "block",
     
    8690
    8791    suite.addTestCase({
    88         name: "DOMBreakpoints.AttributeModified.Style.Text",
     92        name: "DOMBreakpoint.AttributeModified.Style.Text",
    8993        async test() {
     94            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("#test");
     95            await InspectorTest.DOMBreakpoint.createBreakpoint(node, WI.DOMBreakpoint.Type.AttributeModified);
     96
    9097            await checkPropertyValue("display", "");
    9198
     
    107114            await checkPropertyValue("display", "none");
    108115        },
     116        teardown: InspectorTest.DOMBreakpoint.teardown,
    109117    });
    110118
    111     WI.domManager.requestDocument((documentNode) => {
    112         if (!documentNode) {
    113             InspectorTest.fail("Missing document node");
    114             InspectorTest.completeTest();
    115             return;
    116         }
     119    InspectorTest.BreakpointOptions.addTestCases(suite, {
     120        testCaseNamePrefix: "Style.",
     121        async createBreakpoint() {
     122            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("#test");
     123            return InspectorTest.DOMBreakpoint.createBreakpoint(node, WI.DOMBreakpoint.Type.AttributeModified);
     124        },
     125        async triggerBreakpoint(breakpoint) {
     126            return InspectorTest.evaluateInPage(`setProperty("--test", Math.random())`);
     127        },
     128    });
    117129
    118         documentNode.querySelector("#test", async (nodeId) => {
    119             let testNode = WI.domManager.nodeForId(nodeId);
    120             if (!testNode) {
    121                 InspectorTest.fail("Missing #test node");
    122                 InspectorTest.completeTest();
    123                 return;
    124             }
    125 
    126             let promise = WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.DOMBreakpointAdded)
    127             .then((event) => {
    128                 InspectorTest.pass("Added attribute modified breakpoint to #test.");
    129             });
    130 
    131             InspectorTest.log("Adding attribute modified breakpoint to #test...");
    132             WI.domDebuggerManager.addDOMBreakpoint(new WI.DOMBreakpoint(testNode, WI.DOMBreakpoint.Type.AttributeModified));
    133             await promise;
    134 
    135             InspectorTest.assert(!WI.debuggerManager.paused, "Should not be paused.");
    136 
    137             suite.runTestCasesAndFinish();
    138         });
    139     });
     130    suite.runTestCasesAndFinish();
    140131}
    141132</script>
  • trunk/LayoutTests/inspector/dom-debugger/dom-breakpoints-expected.txt

    r251871 r266669  
    22
    33
    4 == Running test suite: DOMBreakpoints
    5 -- Running test case: BasicBreakpoint
    6 PASS: Added 'subtree-modified' breakpoint.
     4== Running test suite: DOMBreakpoint
     5-- Running test case: DOMBreakpoint.Basic
     6Adding "subtree-modified:1,HTML,1,BODY,1,DIV,0,DIV" DOM Breakpoint...
    77PASS: Breakpoint should not be disabled.
    88PASS: Breakpoint should have node identifier.
     
    1010-- Running test teardown.
    1111
    12 -- Running test case: DOMBreakpoints.SubtreeModified.BreakpointEnabled
    13 PASS: Added 'subtree-modified' breakpoint.
    14 PASS: Breakpoint should have expected type.
    15 Call DOM operation.
    16 PAUSED:
    17 PASS: Pause reason should be DOM.
    18 PASS: Pause type should be 'subtree-modified'.
    19 PASS: Pause nodeId should be expected value.
    20 PASS: Pause insertion should be 'true'.
    21 PASS: Pause targetNodeId should match nodeId.
    22 CALL STACK:
    23 0: [F] subtreeModifiedTest
    24 1: [P] Global Code
     12-- Running test case: DOMBreakpoint.RemoveAllBreakpointsForNode
     13Adding "subtree-modified:1,HTML,1,BODY,1,DIV,1,DIV" DOM Breakpoint...
     14Adding "attribute-modified:1,HTML,1,BODY,1,DIV,1,DIV" DOM Breakpoint...
     15Adding "node-removed:1,HTML,1,BODY,1,DIV,1,DIV" DOM Breakpoint...
     16PASS: Should have 3 breakpoints.
     17Removing all breakpoints for #multiple-breakpoints-test...
     18PASS: Should remove all breakpoints.
    2519-- Running test teardown.
    2620
    27 -- Running test case: DOMBreakpoints.SubtreeModified.BreakpointDisabled
    28 PASS: Added 'subtree-modified' breakpoint.
    29 Wait for evaluate in page to return.
    30 PASS: Should not pause for disabled breakpoint.
    31 -- Running test teardown.
     21-- Running test case: DOMBreakpoint.SetBreakpointWithInvalidNodeId
     22Setting breakpoint...
     23PASS: Should produce an exception.
     24Error: Missing node for given nodeId
    3225
    33 -- Running test case: DOMBreakpoints.SubtreeModified.DebuggerDisabled
    34 PASS: Added 'subtree-modified' breakpoint.
    35 Wait for evaluate in page to return.
    36 PASS: Should not pause for disabled breakpoint.
    37 -- Running test teardown.
     26-- Running test case: DOMBreakpoint.SetBreakpointWithInvalidType
     27Setting breakpoint...
     28PASS: Should produce an exception.
     29Error: Unknown type: custom-breakpoint-type
    3830
    39 -- Running test case: DOMBreakpoints.SubtreeModified.RemoveBreakpoint
    40 PASS: Added 'subtree-modified' breakpoint.
    41 Remove breakpoint.
    42 Wait for evaluate in page to return.
    43 PASS: Should not pause for removed breakpoint.
    44 -- Running test teardown.
     31-- Running test case: DOMBreakpoint.RemoveBreakpointWithInvalidNodeId
     32Removing breakpoint...
     33PASS: Should produce an exception.
     34Error: Missing node for given nodeId
    4535
    46 -- Running test case: DOMBreakpoints.AttributeModified.BreakpointEnabled
    47 PASS: Added 'attribute-modified' breakpoint.
    48 PASS: Breakpoint should have expected type.
    49 Call DOM operation.
    50 PAUSED:
    51 PASS: Pause reason should be DOM.
    52 PASS: Pause type should be 'attribute-modified'.
    53 PASS: Pause nodeId should be expected value.
    54 CALL STACK:
    55 0: [F] attributeModifiedTest
    56 1: [P] Global Code
    57 -- Running test teardown.
     36-- Running test case: DOMBreakpoint.RemoveBreakpointWithInvalidType
     37Removing breakpoint...
     38PASS: Should produce an exception.
     39Error: Unknown type: custom-breakpoint-type
    5840
    59 -- Running test case: DOMBreakpoints.AttributeModified.BreakpointDisabled
    60 PASS: Added 'attribute-modified' breakpoint.
    61 Wait for evaluate in page to return.
    62 PASS: Should not pause for disabled breakpoint.
    63 -- Running test teardown.
    64 
    65 -- Running test case: DOMBreakpoints.AttributeModified.DebuggerDisabled
    66 PASS: Added 'attribute-modified' breakpoint.
    67 Wait for evaluate in page to return.
    68 PASS: Should not pause for disabled breakpoint.
    69 -- Running test teardown.
    70 
    71 -- Running test case: DOMBreakpoints.AttributeModified.RemoveBreakpoint
    72 PASS: Added 'attribute-modified' breakpoint.
    73 Remove breakpoint.
    74 Wait for evaluate in page to return.
    75 PASS: Should not pause for removed breakpoint.
    76 -- Running test teardown.
    77 
    78 -- Running test case: DOMBreakpoints.NodeRemovedSelf.BreakpointEnabled
    79 PASS: Added 'node-removed' breakpoint.
    80 PASS: Breakpoint should have expected type.
    81 Call DOM operation.
    82 PAUSED:
    83 PASS: Pause reason should be DOM.
    84 PASS: Pause type should be 'node-removed'.
    85 PASS: Pause nodeId should be expected value.
    86 CALL STACK:
    87 0: [F] nodeRemovedDirectTest
    88 1: [P] Global Code
    89 -- Running test teardown.
    90 
    91 -- Running test case: DOMBreakpoints.NodeRemovedSelf.BreakpointDisabled
    92 PASS: Added 'node-removed' breakpoint.
    93 Wait for evaluate in page to return.
    94 PASS: Should not pause for disabled breakpoint.
    95 -- Running test teardown.
    96 
    97 -- Running test case: DOMBreakpoints.NodeRemovedSelf.DebuggerDisabled
    98 PASS: Added 'node-removed' breakpoint.
    99 Wait for evaluate in page to return.
    100 PASS: Should not pause for disabled breakpoint.
    101 -- Running test teardown.
    102 
    103 -- Running test case: DOMBreakpoints.NodeRemovedSelf.RemoveBreakpoint
    104 PASS: Added 'node-removed' breakpoint.
    105 Remove breakpoint.
    106 Wait for evaluate in page to return.
    107 PASS: Should not pause for removed breakpoint.
    108 -- Running test teardown.
    109 
    110 -- Running test case: DOMBreakpoints.NodeRemovedAncestor.BreakpointEnabled
    111 PASS: Added 'node-removed' breakpoint.
    112 PASS: Breakpoint should have expected type.
    113 Call DOM operation.
    114 PAUSED:
    115 PASS: Pause reason should be DOM.
    116 PASS: Pause type should be 'node-removed'.
    117 PASS: Pause nodeId should be expected value.
    118 PASS: Pause targetNodeId should not match nodeId.
    119 CALL STACK:
    120 0: [F] nodeRemovedAncestorTest
    121 1: [P] Global Code
    122 -- Running test teardown.
    123 
    124 -- Running test case: DOMBreakpoints.NodeRemovedAncestor.BreakpointDisabled
    125 PASS: Added 'node-removed' breakpoint.
    126 Wait for evaluate in page to return.
    127 PASS: Should not pause for disabled breakpoint.
    128 -- Running test teardown.
    129 
    130 -- Running test case: DOMBreakpoints.NodeRemovedAncestor.DebuggerDisabled
    131 PASS: Added 'node-removed' breakpoint.
    132 Wait for evaluate in page to return.
    133 PASS: Should not pause for disabled breakpoint.
    134 -- Running test teardown.
    135 
    136 -- Running test case: DOMBreakpoints.NodeRemovedAncestor.RemoveBreakpoint
    137 PASS: Added 'node-removed' breakpoint.
    138 Remove breakpoint.
    139 Wait for evaluate in page to return.
    140 PASS: Should not pause for removed breakpoint.
    141 -- Running test teardown.
    142 
    143 -- Running test case: RemoveAllBreakpointsForNode
    144 PASS: Added 'subtree-modified' breakpoint.
    145 PASS: Added 'attribute-modified' breakpoint.
    146 PASS: Added 'node-removed' breakpoint.
    147 PASS: Removed 3 breakpoints.
    148 PASS: DOM node should have no breakpoints.
    149 -- Running test teardown.
    150 
    151 -- Running test case: SetBreakpointWithInvalidNodeId
    152 Attempting to set breakpoint.
    153 Protocol result: Missing node for given nodeId
    154 PASS: Protocol should return an error.
    155 -- Running test teardown.
    156 
    157 -- Running test case: SetBreakpointWithInvalidType
    158 Attempting to set breakpoint.
    159 Protocol result: Unknown DOM breakpoint type: custom-breakpoint-type
    160 PASS: Protocol should return an error.
    161 -- Running test teardown.
    162 
    163 -- Running test case: RemoveBreakpointWithInvalidNodeId
    164 Attempting to remove breakpoint.
    165 Protocol result: Missing node for given nodeId
    166 PASS: Protocol should return an error.
    167 -- Running test teardown.
    168 
    169 -- Running test case: RemoveBreakpointWithInvalidType
    170 Attempting to remove breakpoint.
    171 Protocol result: Unknown DOM breakpoint type: custom-breakpoint-type
    172 PASS: Protocol should return an error.
    173 -- Running test teardown.
    174 
  • trunk/LayoutTests/inspector/dom-debugger/dom-breakpoints.html

    r253242 r266669  
    44<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
    55<script src="../debugger/resources/log-active-stack-trace.js"></script>
     6<script src="resources/dom-breakpoint-utilities.js"></script>
    67<script>
    7 function subtreeModifiedTest() {
    8     document.getElementById("subtree-modified-test").append(document.createElement("p"));
    9 }
    10 
    11 function attributeModifiedTest() {
    12     document.getElementById("attribute-modified-test").setAttribute("display", "none");
    13 }
    14 
    15 function nodeRemovedDirectTest() {
    16     let node = document.getElementById("node-removed-direct-test");
    17     let parent = node.parentNode;
    18     node.remove();
    19     parent.append(node);
    20 }
    21 
    22 function nodeRemovedAncestorTest() {
    23     let node = document.getElementById("node-removed-ancestor-test");
    24     let parent = node.parentNode;
    25     let grandparent = parent.parentNode;
    26     parent.remove(node);
    27     grandparent.append(parent);
    28 }
    29 
    308function test()
    319{
    32     const subtreeModifiedTestId = "subtree-modified-test";
    33     const attributeModifiedTestId = "attribute-modified-test";
    34     const nodeRemovedDirectTestId = "node-removed-direct-test";
    35     const nodeRemovedAncestorTestId = "node-removed-ancestor-test";
    36     const multipleBreakpointsTestId = "multiple-breakpoints-test";
    37 
    38     let debuggerPausedListener = null;
    39 
    40     let suite = InspectorTest.createAsyncSuite("DOMBreakpoints");
    41 
    42     function teardown(resolve) {
    43         let breakpoints = WI.domDebuggerManager.domBreakpoints;
    44         for (let breakpoint of breakpoints)
    45             WI.domDebuggerManager.removeDOMBreakpoint(breakpoint);
    46         InspectorTest.assert(!WI.domDebuggerManager.domBreakpoints.length);
    47 
    48         WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, debuggerPausedListener);
    49         debuggerPausedListener = null;
    50 
    51         resolve();
    52     }
    53 
    54     function rejectOnPause() {
    55         return new Promise((resolve, reject) => {
    56             InspectorTest.assert(!debuggerPausedListener);
    57             debuggerPausedListener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
    58                 WI.debuggerManager.resume();
    59                 InspectorTest.fail("Should not pause.");
    60                 reject();
    61             });
    62         });
    63     }
    64 
    65     function awaitEvaluateInPage(expression) {
    66         return new Promise((resolve, reject) => {
    67             InspectorTest.log("Wait for evaluate in page to return.");
    68             InspectorTest.evaluateInPage(expression, (error) => {
    69                 if (error)
    70                     reject(error);
    71 
    72                 resolve();
    73             });
    74         });
    75     }
    76 
    77     function awaitQuerySelector(selector) {
    78         return new Promise((resolve, reject) => {
    79             WI.domManager.requestDocument((documentNode) => {
    80                 if (!documentNode) {
    81                     reject();
    82                     return;
    83                 }
    84 
    85                 documentNode.querySelector(selector, (nodeId) => {
    86                     if (!nodeId) {
    87                         InspectorTest.fail("Selector returned no nodes.", selector);
    88                         reject();
    89                         return;
    90                     }
    91 
    92                     let node = WI.domManager.nodeForId(nodeId);
    93                     InspectorTest.assert(node, "Missing node for id.", nodeId);
    94                     if (!node) {
    95                         reject();
    96                         return;
    97                     }
    98 
    99                     resolve(node);
    100                 });
    101             });
    102         });
    103     }
    104 
    105     function addBreakpointForElementIdentifier(elementIdentifier, type, disabled) {
    106         return new Promise((resolve, reject) => {
    107             awaitQuerySelector("#" + elementIdentifier)
    108             .then((node) => {
    109                 WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.DOMBreakpointAdded)
    110                 .then((event) => {
    111                     let breakpoint = event.data.breakpoint;
    112                     InspectorTest.pass(`Added '${type}' breakpoint.`);
    113                     resolve(event);
    114                 });
    115 
    116                 WI.domDebuggerManager.addDOMBreakpoint(new WI.DOMBreakpoint(node, type, {disabled}));
    117             });
    118         });
    119     }
    120 
    121     function addTestsForBreakpointType({name, elementIdentifier, type, expression, insertion, shouldMatch}) {
    122         suite.addTestCase({
    123             name: `DOMBreakpoints.${name}.BreakpointEnabled`,
    124             description: "Check that debugger pauses for breakpoint type.",
    125             teardown,
    126             test(resolve, reject) {
    127                 addBreakpointForElementIdentifier(elementIdentifier, type)
    128                 .then((event) => {
    129                     let breakpoint = event.data.breakpoint;
    130                     InspectorTest.expectEqual(breakpoint.type, type, "Breakpoint should have expected type.");
    131 
    132                     InspectorTest.log("Call DOM operation.");
    133                     InspectorTest.evaluateInPage(expression);
    134                     return WI.debuggerManager.awaitEvent(WI.DebuggerManager.Event.Paused);
    135                 })
    136                 .then(() => {
    137                     let targetData = WI.debuggerManager.dataForTarget(WI.debuggerManager.activeCallFrame.target);
    138                     InspectorTest.log("PAUSED:");
    139                     InspectorTest.expectEqual(targetData.pauseReason, WI.DebuggerManager.PauseReason.DOM, "Pause reason should be DOM.");
    140 
    141                     let pauseData = targetData.pauseData;
    142                     InspectorTest.expectEqual(pauseData.type, type, `Pause type should be '${type}'.`);
    143                     InspectorTest.expectEqual(pauseData.nodeId, domNodeIdentifier, `Pause nodeId should be expected value.`);
    144 
    145                     if (insertion !== undefined)
    146                         InspectorTest.expectEqual(insertion, target.pauseData.insertion, `Pause insertion should be '${insertion}'.`);
    147 
    148                     if (pauseData.targetNodeId) {
    149                         if (shouldMatch)
    150                             InspectorTest.expectEqual(pauseData.targetNodeId, pauseData.nodeId, "Pause targetNodeId should match nodeId.");
    151                         else
    152                             InspectorTest.expectNotEqual(pauseData.targetNodeId, pauseData.nodeId, "Pause targetNodeId should not match nodeId.");
    153                     }
    154 
    155                     logActiveStackTrace();
    156                     return WI.debuggerManager.resume();
    157                 })
    158                 .then(resolve, reject);
    159             }
    160         });
    161 
    162         suite.addTestCase({
    163             name: `DOMBreakpoints.${name}.BreakpointDisabled`,
    164             description: "Check that debugger does not pause for disabled breakpoint.",
    165             teardown,
    166             test(resolve, reject) {
    167                 const disabled = true;
    168                 addBreakpointForElementIdentifier(elementIdentifier, type, disabled)
    169                 .then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
    170                 .then(() => {
    171                     InspectorTest.pass("Should not pause for disabled breakpoint.");
    172                     resolve();
    173                 })
    174                 .catch(reject);
    175             }
    176         });
    177 
    178         suite.addTestCase({
    179             name: `DOMBreakpoints.${name}.DebuggerDisabled`,
    180             description: "Check that debugger does not pause when the debugger is disabled.",
    181             teardown,
    182             test(resolve, reject) {
    183                 addBreakpointForElementIdentifier(elementIdentifier, type)
    184                 .then(() => DebuggerAgent.setBreakpointsActive(false))
    185                 .then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
    186                 .then(() => DebuggerAgent.setBreakpointsActive(true))
    187                 .then(() => {
    188                     InspectorTest.pass("Should not pause for disabled breakpoint.");
    189                     resolve();
    190                 })
    191                 .catch(reject);
    192             }
    193         });
    194 
    195         suite.addTestCase({
    196             name: `DOMBreakpoints.${name}.RemoveBreakpoint`,
    197             description: "Check that debugger does not pause for removed breakpoint.",
    198             teardown,
    199             test(resolve, reject) {
    200                 addBreakpointForElementIdentifier(elementIdentifier, type)
    201                 .then((event) => {
    202                     let promise = WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved);
    203                     let breakpoint = event.data.breakpoint;
    204 
    205                     InspectorTest.log("Remove breakpoint.");
    206                     WI.domDebuggerManager.removeDOMBreakpoint(breakpoint);
    207                     return promise;
    208                 })
    209                 .then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
    210                 .then(() => {
    211                     InspectorTest.pass("Should not pause for removed breakpoint.");
    212                     resolve();
    213                 })
    214                 .catch(reject);
    215             }
    216         });
    217     }
     10    let suite = InspectorTest.createAsyncSuite("DOMBreakpoint");
    21811
    21912    suite.addTestCase({
    220         name: "BasicBreakpoint",
     13        name: "DOMBreakpoint.Basic",
    22114        description: "Check that breakpoint is in a valid initial state.",
    222         teardown,
    223         test(resolve, reject) {
    224             addBreakpointForElementIdentifier(subtreeModifiedTestId, WI.DOMBreakpoint.Type.SubtreeModified)
    225             .then((event) => {
    226                 let breakpoint = event.data.breakpoint;
    227                 InspectorTest.expectFalse(breakpoint.disabled, "Breakpoint should not be disabled.");
    228                 InspectorTest.expectThat(breakpoint.domNodeIdentifier, "Breakpoint should have node identifier.");
     15        async test() {
     16            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("#basic-test");
    22917
    230                 InspectorTest.assert(WI.networkManager.mainFrame, "Missing main frame.");
    231                 let documentURL = WI.networkManager.mainFrame.url;
    232                 InspectorTest.expectEqual(breakpoint.url, documentURL, "Breakpoint URL should match document URL.");
    233             })
    234             .then(resolve, reject);
    235         }
    236     });
    237 
    238     addTestsForBreakpointType({
    239         name: "SubtreeModified",
    240         elementIdentifier: subtreeModifiedTestId,
    241         type: WI.DOMBreakpoint.Type.SubtreeModified,
    242         expression: "subtreeModifiedTest()",
    243         insertion: true,
    244         shouldMatch: true,
    245     });
    246 
    247     addTestsForBreakpointType({
    248         name: "AttributeModified",
    249         elementIdentifier: attributeModifiedTestId,
    250         type: WI.DOMBreakpoint.Type.AttributeModified,
    251         expression: "attributeModifiedTest()",
    252     });
    253 
    254     addTestsForBreakpointType({
    255         name: "NodeRemovedSelf",
    256         elementIdentifier: nodeRemovedDirectTestId,
    257         type: WI.DOMBreakpoint.Type.NodeRemoved,
    258         expression: "nodeRemovedDirectTest()",
    259     });
    260 
    261     addTestsForBreakpointType({
    262         name: "NodeRemovedAncestor",
    263         elementIdentifier: nodeRemovedAncestorTestId,
    264         type: WI.DOMBreakpoint.Type.NodeRemoved,
    265         expression: "nodeRemovedAncestorTest()",
    266         shouldMatch: true,
     18            let breakpoint = await InspectorTest.DOMBreakpoint.createBreakpoint(node, WI.DOMBreakpoint.Type.SubtreeModified);
     19            InspectorTest.expectFalse(breakpoint.disabled, "Breakpoint should not be disabled.");
     20            InspectorTest.expectEqual(breakpoint.domNode, node, "Breakpoint should have node identifier.");
     21            InspectorTest.expectEqual(breakpoint.url, WI.networkManager.mainFrame.url, "Breakpoint URL should match document URL.");
     22        },
     23        teardown: InspectorTest.DOMBreakpoint.teardown,
    26724    });
    26825
    26926    suite.addTestCase({
    270         name: "RemoveAllBreakpointsForNode",
     27        name: "DOMBreakpoint.RemoveAllBreakpointsForNode",
    27128        description: "Check that debugger does not pause for removed breakpoints on node.",
    272         teardown,
    273         test(resolve, reject) {
    274             let breakpointTypes = Object.values(WI.DOMBreakpoint.Type);
    275             let breakpointPromises = breakpointTypes.map(type => addBreakpointForElementIdentifier(multipleBreakpointsTestId, type));
     29        async test() {
     30            const types = Object.values(WI.DOMBreakpoint.Type);
    27631
    277             Promise.all(breakpointPromises)
    278             .then((values) => {
    279                 let breakpoints = values.map(event => event.data.breakpoint);
    280                 if (!breakpoints.length) {
    281                     InspectorTest.fail("No breakpoints added.");
    282                     reject();
    283                     return;
    284                 }
     32            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("#remove-all-breakpoints-for-node-test");
    28533
    286                 let node = WI.domManager.nodeForId(breakpoints[0].domNodeIdentifier);
    287                 if (!node) {
    288                     InspectorTest.fail("No DOM node associated with breakpoint.");
    289                     reject();
    290                     return;
    291                 }
     34            let breakpointsBefore = await Promise.all(types.map((type) => InspectorTest.DOMBreakpoint.createBreakpoint(node, type)));
     35            InspectorTest.expectEqual(breakpointsBefore.length, types.length, `Should have ${types.length} breakpoints.`);
    29236
    293                 let breakpointsRemoved = 0;
     37            InspectorTest.log("Removing all breakpoints for #multiple-breakpoints-test...");
     38            WI.domDebuggerManager.removeDOMBreakpointsForNode(node);
    29439
    295                 function breakpointRemoved() {
    296                     if (++breakpointsRemoved === breakpoints.length) {
    297                         InspectorTest.pass(`Removed ${breakpointsRemoved} breakpoints.`);
    298 
    299                         let nodeBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(node);
    300                         InspectorTest.expectThat(!nodeBreakpoints.length, "DOM node should have no breakpoints.");
    301 
    302                         WI.domDebuggerManager.removeEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, breakpointRemoved);
    303                         resolve();
    304                     }
    305                 }
    306 
    307                 WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, breakpointRemoved);
    308                 WI.domDebuggerManager.removeDOMBreakpointsForNode(node);
    309             })
    310             .then(resolve, reject);
    311         }
     40            let breakpointsAfter = WI.domDebuggerManager.domBreakpointsForNode(node);
     41            InspectorTest.expectEqual(breakpointsAfter.length, 0, "Should remove all breakpoints.");
     42        },
     43        teardown: InspectorTest.DOMBreakpoint.teardown,
    31244    });
    31345
    31446    suite.addTestCase({
    315         name: "SetBreakpointWithInvalidNodeId",
     47        name: "DOMBreakpoint.SetBreakpointWithInvalidNodeId",
    31648        description: "Check that setting a breakpoint for a nonexistant node returns an error.",
    317         teardown,
    318         test(resolve, reject) {
    319             InspectorTest.log("Attempting to set breakpoint.");
    320 
    321             DOMDebuggerAgent.setDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified, (error) => {
    322                 InspectorTest.log("Protocol result: " + error);
    323                 InspectorTest.expectThat(error, "Protocol should return an error.")
    324                 resolve();
     49        async test() {
     50            InspectorTest.log("Setting breakpoint...");
     51            await InspectorTest.expectException(async () => {
     52                await DOMDebuggerAgent.setDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified);
    32553            });
    326         }
     54        },
    32755    });
    32856
    32957    suite.addTestCase({
    330         name: "SetBreakpointWithInvalidType",
     58        name: "DOMBreakpoint.SetBreakpointWithInvalidType",
    33159        description: "Check that setting a breakpoint with an invalid type returns an error.",
    332         teardown,
    333         test(resolve, reject) {
    334             awaitQuerySelector("body")
    335             .then((node) => {
    336                 InspectorTest.log("Attempting to set breakpoint.");
     60        async test() {
     61            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("body");
    33762
    338                 DOMDebuggerAgent.setDOMBreakpoint(node.id, "custom-breakpoint-type", (error) => {
    339                     InspectorTest.log("Protocol result: " + error);
    340                     InspectorTest.expectThat(error, "Protocol should return an error.")
    341                     resolve();
    342                 });
     63            InspectorTest.log("Setting breakpoint...");
     64            await InspectorTest.expectException(async () => {
     65                await DOMDebuggerAgent.setDOMBreakpoint(node.id, "custom-breakpoint-type");
    34366            });
    344         }
     67        },
    34568    });
    34669
    34770    suite.addTestCase({
    348         name: "RemoveBreakpointWithInvalidNodeId",
     71        name: "DOMBreakpoint.RemoveBreakpointWithInvalidNodeId",
    34972        description: "Check that removing a breakpoint for a nonexistant node returns an error.",
    350         teardown,
    351         test(resolve, reject) {
    352             InspectorTest.log("Attempting to remove breakpoint.");
    353 
    354             DOMDebuggerAgent.removeDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified, (error) => {
    355                 InspectorTest.log("Protocol result: " + error);
    356                 InspectorTest.expectThat(error, "Protocol should return an error.")
    357                 resolve();
     73        async test() {
     74            InspectorTest.log("Removing breakpoint...");
     75            await InspectorTest.expectException(async () => {
     76                await DOMDebuggerAgent.removeDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified);
    35877            });
    359         }
     78        },
    36079    });
    36180
    36281    suite.addTestCase({
    363         name: "RemoveBreakpointWithInvalidType",
     82        name: "DOMBreakpoint.RemoveBreakpointWithInvalidType",
    36483        description: "Check that removing a breakpoint with an invalid type returns an error.",
    365         teardown,
    366         test(resolve, reject) {
    367             awaitQuerySelector("body")
    368             .then((node) => {
    369                 InspectorTest.log("Attempting to remove breakpoint.");
     84        async test() {
     85            let node = await InspectorTest.DOMBreakpoint.awaitQuerySelector("body");
    37086
    371                 DOMDebuggerAgent.removeDOMBreakpoint(node.id, "custom-breakpoint-type", (error) => {
    372                     InspectorTest.log("Protocol result: " + error);
    373                     InspectorTest.expectThat(error, "Protocol should return an error.")
    374                     resolve();
    375                 });
     87            InspectorTest.log("Removing breakpoint...");
     88            await InspectorTest.expectException(async () => {
     89                await DOMDebuggerAgent.removeDOMBreakpoint(node.id, "custom-breakpoint-type");
    37690            });
    377         }
     91        },
    37892    });
    37993
     
    38599<p>Tests for DOM breakpoints.</p>
    386100<div id="test-container" style="display: none">
    387     <div id="subtree-modified-test"></div>
    388     <div id="attribute-modified-test"></div>
    389     <div id="node-removed-direct-test"></div>
    390     <div><div id="node-removed-ancestor-test"></div></div>
    391     <div id="multiple-breakpoints-test"></div>
     101    <div id="basic-test"></div>
     102    <div id="remove-all-breakpoints-for-node-test"></div>
    392103</div>
    393104</body>
  • trunk/Source/JavaScriptCore/ChangeLog

    r266655 r266669  
     12020-09-05  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: allow DOM breakpoints to be configured
     4        https://bugs.webkit.org/show_bug.cgi?id=215795
     5
     6        Reviewed by Brian Burg.
     7
     8        * inspector/protocol/DOMDebugger.json:
     9        Add an `options` parameter to `DOMDebugger.setDOMBreakpoint` to allow configuration.
     10
    1112020-09-04  Yusuke Suzuki  <ysuzuki@apple.com>
    212
  • trunk/Source/JavaScriptCore/inspector/protocol/DOMDebugger.json

    r266538 r266669  
    2424            "parameters": [
    2525                { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." },
    26                 { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." }
     26                { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." },
     27                { "name": "options", "$ref": "Debugger.BreakpointOptions", "optional": true, "description": "Options to apply to this breakpoint to modify its behavior." }
    2728            ]
    2829        },
  • trunk/Source/WebCore/ChangeLog

    r266668 r266669  
     12020-09-05  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: allow DOM breakpoints to be configured
     4        https://bugs.webkit.org/show_bug.cgi?id=215795
     5
     6        Reviewed by Brian Burg.
     7
     8        Tests: inspector/dom-debugger/attribute-modified-style.html
     9               inspector/dom-debugger/dom-breakpoints.html
     10               inspector/dom-debugger/dom-breakpoint-attribute-modified.html
     11               inspector/dom-debugger/dom-breakpoint-node-removed-ancestor.html
     12               inspector/dom-debugger/dom-breakpoint-node-removed-direct.html
     13               inspector/dom-debugger/dom-breakpoint-subtree-modified-add.html
     14               inspector/dom-debugger/dom-breakpoint-subtree-modified-remove.html
     15
     16        * inspector/agents/page/PageDOMDebuggerAgent.h:
     17        * inspector/agents/page/PageDOMDebuggerAgent.cpp:
     18        (WebCore::PageDOMDebuggerAgent::disable):
     19        (WebCore::PageDOMDebuggerAgent::setDOMBreakpoint):
     20        (WebCore::PageDOMDebuggerAgent::removeDOMBreakpoint):
     21        (WebCore::PageDOMDebuggerAgent::frameDocumentUpdated):
     22        (WebCore::calculateDistance): Added.
     23        (WebCore::PageDOMDebuggerAgent::willInsertDOMNode):
     24        (WebCore::PageDOMDebuggerAgent::willRemoveDOMNode):
     25        (WebCore::PageDOMDebuggerAgent::didRemoveDOMNode):
     26        (WebCore::PageDOMDebuggerAgent::willModifyDOMAttr):
     27        (WebCore::PageDOMDebuggerAgent::willInvalidateStyleAttr):
     28        (WebCore::PageDOMDebuggerAgent::buildPauseDataForDOMBreakpoint): Added.
     29        (WebCore::domTypeForName): Deleted.
     30        (WebCore::domTypeName): Deleted.
     31        (WebCore::PageDOMDebuggerAgent::didInsertDOMNode): Deleted.
     32        (WebCore::PageDOMDebuggerAgent::descriptionForDOMEvent): Deleted.
     33        (WebCore::PageDOMDebuggerAgent::updateSubtreeBreakpoints): Deleted.
     34        (WebCore::PageDOMDebuggerAgent::hasBreakpoint): Deleted.
     35        Replace the bitmask with separate `HashMap` for each type of DOM breakpoint. Instead of
     36        propagating the `SubtreeModified` bit to the entire subtree when new nodes are added (which
     37        means there's an entry in the `HashMap` for every descendant) and removing them all when
     38        that node is removed, only keep nodes in each `HashMap` if it directly has a DOM breakpoint.
     39        Walk up the ancestor chain when nodes are added/removed to determine if there is a nearby
     40        breakpoint to pause. When a node is removed, remove any existing DOM breakpoint if it's
     41        owner is part of the removed subtree.
     42
     43        * inspector/agents/worker/WorkerDOMDebuggerAgent.h:
     44        * inspector/agents/worker/WorkerDOMDebuggerAgent.cpp:
     45        (WebCore::WorkerDOMDebuggerAgent::setDOMBreakpoint):
     46
     47        * inspector/InspectorInstrumentation.cpp:
     48        (WebCore::InspectorInstrumentation::didInsertDOMNodeImpl):
     49
    1502020-09-05  Alex Christensen  <achristensen@webkit.org>
    251
  • trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp

    r266074 r266669  
    161161    if (auto* domAgent = instrumentingAgents.persistentDOMAgent())
    162162        domAgent->didInsertDOMNode(node);
    163     if (auto* pageDOMDebuggerAgent = instrumentingAgents.enabledPageDOMDebuggerAgent())
    164         pageDOMDebuggerAgent->didInsertDOMNode(node);
    165163}
    166164
  • trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp

    r266074 r266669  
    3232#include "InstrumentingAgents.h"
    3333#include "Node.h"
     34#include <wtf/Optional.h>
    3435
    3536namespace WebCore {
    3637
    3738using namespace Inspector;
    38 
    39 enum DOMBreakpointType {
    40     SubtreeModified,
    41     AttributeModified,
    42     NodeRemoved,
    43 };
    44 
    45 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
    46 const int domBreakpointDerivedTypeShift = 16;
    47 
    48 static int domTypeForName(ErrorString& errorString, const String& typeString)
    49 {
    50     if (typeString == "subtree-modified")
    51         return SubtreeModified;
    52     if (typeString == "attribute-modified")
    53         return AttributeModified;
    54     if (typeString == "node-removed")
    55         return NodeRemoved;
    56     errorString = makeString("Unknown DOM breakpoint type: ", typeString);
    57     return -1;
    58 }
    59 
    60 static String domTypeName(int type)
    61 {
    62     switch (type) {
    63     case SubtreeModified:
    64         return "subtree-modified"_s;
    65     case AttributeModified:
    66         return "attribute-modified"_s;
    67     case NodeRemoved:
    68         return "node-removed"_s;
    69     }
    70     return emptyString();
    71 }
    7239
    7340PageDOMDebuggerAgent::PageDOMDebuggerAgent(PageAgentContext& context, InspectorDebuggerAgent* debuggerAgent)
     
    9461    m_instrumentingAgents.setEnabledPageDOMDebuggerAgent(nullptr);
    9562
    96     m_domBreakpoints.clear();
     63    m_domSubtreeModifiedBreakpoints.clear();
     64    m_domAttributeModifiedBreakpoints.clear();
     65    m_domNodeRemovedBreakpoints.clear();
    9766
    9867    m_pauseOnAllAnimationFramesBreakpoint = nullptr;
     
    10170}
    10271
    103 void PageDOMDebuggerAgent::setDOMBreakpoint(ErrorString& errorString, int nodeId, const String& typeString)
     72void PageDOMDebuggerAgent::setDOMBreakpoint(ErrorString& errorString, int nodeId, const String& typeString, const JSON::Object* optionsPayload)
    10473{
    10574    auto* domAgent = m_instrumentingAgents.persistentDOMAgent();
     
    11382        return;
    11483
    115     int type = domTypeForName(errorString, typeString);
    116     if (type == -1)
    117         return;
    118 
    119     uint32_t rootBit = 1 << type;
    120     m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
    121     if (rootBit & inheritableDOMBreakpointTypesMask) {
    122         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    123             updateSubtreeBreakpoints(child, rootBit, true);
    124     }
     84    auto type = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::DOMDebugger::DOMBreakpointType>(typeString);
     85    if (!type) {
     86        errorString = makeString("Unknown type: "_s, typeString);
     87        return;
     88    }
     89
     90    auto breakpoint = InspectorDebuggerAgent::debuggerBreakpointFromPayload(errorString, optionsPayload);
     91    if (!breakpoint)
     92        return;
     93
     94    switch (*type) {
     95    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::SubtreeModified:
     96        if (!m_domSubtreeModifiedBreakpoints.add(node, breakpoint.releaseNonNull()))
     97            errorString = "Breakpoint for given node and given type already exists"_s;
     98        return;
     99
     100    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::AttributeModified:
     101        if (!m_domAttributeModifiedBreakpoints.add(node, breakpoint.releaseNonNull()))
     102            errorString = "Breakpoint for given node and given type already exists"_s;
     103        return;
     104
     105    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::NodeRemoved:
     106        if (!m_domNodeRemovedBreakpoints.add(node, breakpoint.releaseNonNull()))
     107            errorString = "Breakpoint for given node and given type already exists"_s;
     108        return;
     109    }
     110
     111    ASSERT_NOT_REACHED();
    125112}
    126113
     
    137124        return;
    138125
    139     int type = domTypeForName(errorString, typeString);
    140     if (type == -1)
    141         return;
    142 
    143     uint32_t rootBit = 1 << type;
    144     uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
    145     if (mask)
    146         m_domBreakpoints.set(node, mask);
    147     else
    148         m_domBreakpoints.remove(node);
    149 
    150     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
    151         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    152             updateSubtreeBreakpoints(child, rootBit, false);
    153     }
     126    auto type = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::DOMDebugger::DOMBreakpointType>(typeString);
     127    if (!type) {
     128        errorString = makeString("Unknown type: "_s, typeString);
     129        return;
     130    }
     131
     132    switch (*type) {
     133    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::SubtreeModified:
     134        if (!m_domSubtreeModifiedBreakpoints.remove(node))
     135            errorString = "Breakpoint for given node and given type missing"_s;
     136        return;
     137
     138    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::AttributeModified:
     139        if (!m_domAttributeModifiedBreakpoints.remove(node))
     140            errorString = "Breakpoint for given node and given type missing"_s;
     141        return;
     142
     143    case Inspector::Protocol::DOMDebugger::DOMBreakpointType::NodeRemoved:
     144        if (!m_domNodeRemovedBreakpoints.remove(node))
     145            errorString = "Breakpoint for given node and given type missing"_s;
     146        return;
     147    }
     148
     149    ASSERT_NOT_REACHED();
    154150}
    155151
     
    167163        return;
    168164
    169     m_domBreakpoints.clear();
     165    m_domSubtreeModifiedBreakpoints.clear();
     166    m_domAttributeModifiedBreakpoints.clear();
     167    m_domNodeRemovedBreakpoints.clear();
     168}
     169
     170
     171static Optional<size_t> calculateDistance(Node& child, Node& ancestor)
     172{
     173    size_t distance = 0;
     174
     175    auto* current = &child;
     176    while (current != &ancestor) {
     177        ++distance;
     178
     179        current = InspectorDOMAgent::innerParentNode(current);
     180        if (!current)
     181            return WTF::nullopt;
     182    }
     183
     184    return distance;
    170185}
    171186
     
    175190        return;
    176191
    177     if (hasBreakpoint(&parent, SubtreeModified)) {
    178         Ref<JSON::Object> eventData = JSON::Object::create();
    179         descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
    180         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    181     }
    182 }
    183 
    184 void PageDOMDebuggerAgent::didInsertDOMNode(Node& node)
    185 {
    186     if (m_domBreakpoints.size()) {
    187         uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(&node));
    188         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
    189         if (inheritableTypesMask)
    190             updateSubtreeBreakpoints(&node, inheritableTypesMask, true);
    191     }
     192    if (m_domSubtreeModifiedBreakpoints.isEmpty())
     193        return;
     194
     195    Optional<size_t> closestDistance;
     196    RefPtr<JSC::Breakpoint> closestBreakpoint;
     197    Node* closestBreakpointOwner = nullptr;
     198
     199    for (auto [breakpointOwner, breakpoint] : m_domSubtreeModifiedBreakpoints) {
     200        auto distance = calculateDistance(parent, *breakpointOwner);
     201        if (!distance)
     202            continue;
     203
     204        if (!closestDistance || distance < closestDistance) {
     205            closestDistance = distance;
     206            closestBreakpoint = breakpoint.copyRef();
     207            closestBreakpointOwner = breakpointOwner;
     208        }
     209    }
     210
     211    if (!closestBreakpoint)
     212        return;
     213
     214    ASSERT(closestBreakpointOwner);
     215
     216    auto pauseData = buildPauseDataForDOMBreakpoint(Inspector::Protocol::DOMDebugger::DOMBreakpointType::SubtreeModified, *closestBreakpointOwner);
     217    pauseData->setBoolean("insertion", true);
     218    // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
     219    // Include the new child node ID so the frontend can show the node that's about to be inserted.
     220    m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(pauseData), WTFMove(closestBreakpoint));
    192221}
    193222
     
    197226        return;
    198227
    199     if (hasBreakpoint(&node, NodeRemoved)) {
    200         auto eventData = JSON::Object::create();
    201         descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
    202         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    203         return;
    204     }
    205 
    206     uint32_t rootBit = 1 << NodeRemoved;
    207     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
    208     uint32_t matchBit = rootBit | derivedBit;
    209     for (auto& [nodeWithBreakpoint, breakpointTypes] : m_domBreakpoints) {
    210         if (node.contains(nodeWithBreakpoint) && (breakpointTypes & matchBit)) {
    211             auto eventData = JSON::Object::create();
    212             descriptionForDOMEvent(*nodeWithBreakpoint, NodeRemoved, false, eventData.get());
    213             if (auto* domAgent = m_instrumentingAgents.persistentDOMAgent())
    214                 eventData->setInteger("targetNodeId"_s, domAgent->pushNodeToFrontend(&node));
    215             m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    216             return;
     228    if (m_domNodeRemovedBreakpoints.isEmpty() && m_domSubtreeModifiedBreakpoints.isEmpty())
     229        return;
     230
     231    Optional<size_t> closestDistance;
     232    RefPtr<JSC::Breakpoint> closestBreakpoint;
     233    Optional<Inspector::Protocol::DOMDebugger::DOMBreakpointType> closestBreakpointType;
     234    Node* closestBreakpointOwner = nullptr;
     235
     236    for (auto [breakpointOwner, breakpoint] : m_domNodeRemovedBreakpoints) {
     237        auto distance = calculateDistance(*breakpointOwner, node);
     238        if (!distance)
     239            continue;
     240
     241        if (!closestDistance || distance < closestDistance) {
     242            closestDistance = distance;
     243            closestBreakpoint = breakpoint.copyRef();
     244            closestBreakpointType = Inspector::Protocol::DOMDebugger::DOMBreakpointType::NodeRemoved;
     245            closestBreakpointOwner = breakpointOwner;
    217246        }
    218247    }
    219248
    220     auto* parentNode = InspectorDOMAgent::innerParentNode(&node);
    221     if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
    222         auto eventData = JSON::Object::create();
    223         descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
    224         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    225         return;
    226     }
     249    if (!closestBreakpoint) {
     250        for (auto [breakpointOwner, breakpoint] : m_domSubtreeModifiedBreakpoints) {
     251            auto distance = calculateDistance(node, *breakpointOwner);
     252            if (!distance)
     253                continue;
     254
     255            if (!closestDistance || distance < closestDistance) {
     256                closestDistance = distance;
     257                closestBreakpoint = breakpoint.copyRef();
     258                closestBreakpointType = Inspector::Protocol::DOMDebugger::DOMBreakpointType::SubtreeModified;
     259                closestBreakpointOwner = breakpointOwner;
     260            }
     261        }
     262    }
     263
     264    if (!closestBreakpoint)
     265        return;
     266
     267    ASSERT(closestBreakpointType);
     268    ASSERT(closestBreakpointOwner);
     269
     270    auto pauseData = buildPauseDataForDOMBreakpoint(*closestBreakpointType, *closestBreakpointOwner);
     271    if (auto* domAgent = m_instrumentingAgents.persistentDOMAgent()) {
     272        if (&node != closestBreakpointOwner) {
     273            if (auto targetNodeId = domAgent->pushNodeToFrontend(&node))
     274                pauseData->setInteger("targetNodeId", targetNodeId);
     275        }
     276    }
     277    m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(pauseData), WTFMove(closestBreakpoint));
    227278}
    228279
    229280void PageDOMDebuggerAgent::didRemoveDOMNode(Node& node)
    230281{
    231     if (m_domBreakpoints.size()) {
    232         // Remove subtree breakpoints.
    233         m_domBreakpoints.remove(&node);
    234         Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(&node));
    235         do {
    236             Node* node = stack.last();
    237             stack.removeLast();
    238             if (!node)
    239                 continue;
    240             m_domBreakpoints.remove(node);
    241             stack.append(InspectorDOMAgent::innerFirstChild(node));
    242             stack.append(InspectorDOMAgent::innerNextSibling(node));
    243         } while (!stack.isEmpty());
    244     }
     282    auto nodeContainsBreakpointOwner = [&] (auto& entry) {
     283        return node.contains(entry.key);
     284    };
     285    m_domSubtreeModifiedBreakpoints.removeIf(nodeContainsBreakpointOwner);
     286    m_domAttributeModifiedBreakpoints.removeIf(nodeContainsBreakpointOwner);
     287    m_domNodeRemovedBreakpoints.removeIf(nodeContainsBreakpointOwner);
    245288}
    246289
     
    250293        return;
    251294
    252     if (hasBreakpoint(&element, AttributeModified)) {
    253         Ref<JSON::Object> eventData = JSON::Object::create();
    254         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
    255         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    256     }
     295    auto it = m_domAttributeModifiedBreakpoints.find(&element);
     296    if (it == m_domAttributeModifiedBreakpoints.end())
     297        return;
     298
     299    auto pauseData = buildPauseDataForDOMBreakpoint(Inspector::Protocol::DOMDebugger::DOMBreakpointType::AttributeModified, element);
     300    m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(pauseData), it->value.copyRef());
    257301}
    258302
     
    286330        return;
    287331
    288     if (hasBreakpoint(&element, AttributeModified)) {
    289         Ref<JSON::Object> eventData = JSON::Object::create();
    290         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
    291         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
    292     }
     332    auto it = m_domAttributeModifiedBreakpoints.find(&element);
     333    if (it == m_domAttributeModifiedBreakpoints.end())
     334        return;
     335
     336    auto pauseData = buildPauseDataForDOMBreakpoint(Inspector::Protocol::DOMDebugger::DOMBreakpointType::AttributeModified, element);
     337    m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(pauseData), it->value.copyRef());
    293338}
    294339
     
    303348}
    304349
    305 void PageDOMDebuggerAgent::descriptionForDOMEvent(Node& target, int breakpointType, bool insertion, JSON::Object& description)
     350Ref<JSON::Object> PageDOMDebuggerAgent::buildPauseDataForDOMBreakpoint(Inspector::Protocol::DOMDebugger::DOMBreakpointType breakpointType, Node& breakpointOwner)
    306351{
    307352    ASSERT(m_debuggerAgent->breakpointsActive());
    308     ASSERT(hasBreakpoint(&target, breakpointType));
    309 
    310     auto* domAgent = m_instrumentingAgents.persistentDOMAgent();
    311 
    312     Node* breakpointOwner = &target;
    313     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
    314         if (domAgent) {
    315             // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
    316             // Target node may be unknown to frontend, so we need to push it first.
    317             description.setInteger("targetNodeId"_s, domAgent->pushNodeToFrontend(&target));
    318         }
    319 
    320         // Find breakpoint owner node.
    321         if (!insertion)
    322             breakpointOwner = InspectorDOMAgent::innerParentNode(&target);
    323         ASSERT(breakpointOwner);
    324         while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
    325             Node* parentNode = InspectorDOMAgent::innerParentNode(breakpointOwner);
    326             if (!parentNode)
    327                 break;
    328             breakpointOwner = parentNode;
    329         }
    330 
    331         if (breakpointType == SubtreeModified)
    332             description.setBoolean("insertion", insertion);
    333     }
    334 
    335     if (domAgent) {
    336         int breakpointOwnerNodeId = domAgent->boundNodeId(breakpointOwner);
    337         ASSERT(breakpointOwnerNodeId);
    338         description.setInteger("nodeId", breakpointOwnerNodeId);
    339     }
    340 
    341     description.setString("type", domTypeName(breakpointType));
    342 }
    343 
    344 void PageDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
    345 {
    346     uint32_t oldMask = m_domBreakpoints.get(node);
    347     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
    348     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
    349     if (newMask)
    350         m_domBreakpoints.set(node, newMask);
    351     else
    352         m_domBreakpoints.remove(node);
    353 
    354     uint32_t newRootMask = rootMask & ~newMask;
    355     if (!newRootMask)
    356         return;
    357 
    358     for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    359         updateSubtreeBreakpoints(child, newRootMask, set);
    360 }
    361 
    362 bool PageDOMDebuggerAgent::hasBreakpoint(Node* node, int type)
    363 {
    364     uint32_t rootBit = 1 << type;
    365     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
    366     return m_domBreakpoints.get(node) & (rootBit | derivedBit);
     353    ASSERT(m_domSubtreeModifiedBreakpoints.contains(&breakpointOwner) || m_domAttributeModifiedBreakpoints.contains(&breakpointOwner) || m_domNodeRemovedBreakpoints.contains(&breakpointOwner));
     354
     355    auto pauseData = JSON::Object::create();
     356    pauseData->setString("type", Inspector::Protocol::InspectorHelpers::getEnumConstantValue(breakpointType));
     357    if (auto* domAgent = m_instrumentingAgents.persistentDOMAgent()) {
     358        if (auto breakpointOwnerNodeId = domAgent->pushNodeToFrontend(&breakpointOwner))
     359            pauseData->setInteger("nodeId", breakpointOwnerNodeId);
     360    }
     361    return pauseData;
    367362}
    368363
  • trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h

    r266074 r266669  
    2828#include "InspectorDOMDebuggerAgent.h"
    2929#include <JavaScriptCore/Breakpoint.h>
     30#include <JavaScriptCore/InspectorProtocolObjects.h>
    3031#include <wtf/RefPtr.h>
    3132
     
    4647
    4748    // DOMDebuggerBackendDispatcherHandler
    48     void setDOMBreakpoint(ErrorString&, int nodeId, const String& type) override;
     49    void setDOMBreakpoint(ErrorString&, int nodeId, const String& type, const JSON::Object* options) override;
    4950    void removeDOMBreakpoint(ErrorString&, int nodeId, const String& type) override;
    5051
     
    5354    void frameDocumentUpdated(Frame&);
    5455    void willInsertDOMNode(Node& parent);
    55     void didInsertDOMNode(Node&);
    5656    void willRemoveDOMNode(Node&);
    5757    void didRemoveDOMNode(Node&);
     
    6767    void setAnimationFrameBreakpoint(ErrorString&, RefPtr<JSC::Breakpoint>&&) override;
    6868
    69     void descriptionForDOMEvent(Node&, int breakpointType, bool insertion, JSON::Object& description);
    70     void updateSubtreeBreakpoints(Node*, uint32_t rootMask, bool set);
    71     bool checkSubtreeForNodeRemoved(Node*);
    72     bool hasBreakpoint(Node*, int type);
     69    Ref<JSON::Object> buildPauseDataForDOMBreakpoint(Inspector::Protocol::DOMDebugger::DOMBreakpointType, Node& breakpointOwner);
    7370
    74     HashMap<Node*, uint32_t> m_domBreakpoints;
     71    HashMap<Node*, Ref<JSC::Breakpoint>> m_domSubtreeModifiedBreakpoints;
     72    HashMap<Node*, Ref<JSC::Breakpoint>> m_domAttributeModifiedBreakpoints;
     73    HashMap<Node*, Ref<JSC::Breakpoint>> m_domNodeRemovedBreakpoints;
    7574
    7675    RefPtr<JSC::Breakpoint> m_pauseOnAllAnimationFramesBreakpoint;
  • trunk/Source/WebCore/inspector/agents/worker/WorkerDOMDebuggerAgent.cpp

    r266074 r266669  
    3838WorkerDOMDebuggerAgent::~WorkerDOMDebuggerAgent() = default;
    3939
    40 void WorkerDOMDebuggerAgent::setDOMBreakpoint(ErrorString& errorString, int /* nodeId */, const String& /* typeString */)
     40void WorkerDOMDebuggerAgent::setDOMBreakpoint(ErrorString& errorString, int /* nodeId */, const String& /* typeString */, const JSON::Object* /* optionsPayload */)
    4141{
    4242    errorString = "Not supported"_s;
  • trunk/Source/WebCore/inspector/agents/worker/WorkerDOMDebuggerAgent.h

    r266074 r266669  
    4040
    4141    // DOMDebuggerBackendDispatcherHandler
    42     void setDOMBreakpoint(ErrorString&, int nodeId, const String& type) override;
     42    void setDOMBreakpoint(ErrorString&, int nodeId, const String& type, const JSON::Object* options) override;
    4343    void removeDOMBreakpoint(ErrorString&, int nodeId, const String& type) override;
    4444
  • trunk/Source/WebInspectorUI/ChangeLog

    r266624 r266669  
     12020-09-05  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: allow DOM breakpoints to be configured
     4        https://bugs.webkit.org/show_bug.cgi?id=215795
     5
     6        Reviewed by Brian Burg.
     7
     8        * UserInterface/Controllers/DOMManager.js:
     9        (WI.DOMManager.prototype._setChildNodes):
     10        Dispatch events for each new child node added and each existing child node removed so that
     11        any listeners can also know when new nodes are added via a full children payload update.
     12
     13        * UserInterface/Controllers/DOMDebuggerManager.js:
     14        (WI.DOMDebuggerManager):
     15        (WI.DOMDebuggerManager.prototype.addDOMBreakpoint):
     16        (WI.DOMDebuggerManager.prototype.removeDOMBreakpoint):
     17        (WI.DOMDebuggerManager.prototype._detachDOMBreakpointsForFrame): Added.
     18        (WI.DOMDebuggerManager.prototype._speculativelyResolveDOMBreakpointsForURL):
     19        (WI.DOMDebuggerManager.prototype._speculativelyResolveDOMBreakpoint): Added.
     20        (WI.DOMDebuggerManager.prototype._resolveDOMBreakpoint):
     21        (WI.DOMDebuggerManager.prototype._setDOMBreakpoint): Added.
     22        (WI.DOMDebuggerManager.prototype._removeDOMBreakpoint): Added.
     23        (WI.DOMDebuggerManager.prototype._handleDOMBreakpointDisabledStateChanged):
     24        (WI.DOMDebuggerManager.prototype._handleDOMBreakpointEditablePropertyChanged): Added.
     25        (WI.DOMDebuggerManager.prototype._handleDOMBreakpointActionsChanged): Added.
     26        (WI.DOMDebuggerManager.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
     27        (WI.DOMDebuggerManager.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
     28        (WI.DOMDebuggerManager.prototype._childFrameWasRemoved):
     29        (WI.DOMDebuggerManager.prototype._mainResourceDidChange):
     30        (WI.DOMDebuggerManager.prototype._nodeInserted):
     31        (WI.DOMDebuggerManager.prototype._nodeRemoved):
     32        (WI.DOMDebuggerManager.prototype._detachDOMBreakpoint): Deleted.
     33        (WI.DOMDebuggerManager.prototype._detachBreakpointsForFrame): Deleted.
     34        (WI.DOMDebuggerManager.prototype._updateDOMBreakpoint): Deleted.
     35        Recursively walk any added node's subtree to resolve and set any matching DOM breakpoints.
     36        Iterate all existing DOM breakpoints to see if the owner node is part of the subtree of any
     37        removed nodes and remove ot if so.
     38
     39        * UserInterface/Models/DOMBreakpoint.js:
     40        (WI.DOMBreakpoint):
     41        (WI.DOMBreakpoint.displayNameForType):
     42        (WI.DOMBreakpoint.fromJSON):
     43        (WI.DOMBreakpoint.prototype.get editable): Added.
     44        (WI.DOMBreakpoint.prototype.get domNode): Added.
     45        (WI.DOMBreakpoint.prototype.set domNode): Added.
     46        (WI.DOMBreakpoint.prototype.get domNodeIdentifier): Deleted.
     47        (WI.DOMBreakpoint.prototype.set domNodeIdentifier): Deleted.
     48        * UserInterface/Views/DOMTreeContentView.js:
     49        (WI.DOMTreeContentView):
     50        (WI.DOMTreeContentView.prototype.closed):
     51        (WI.DOMTreeContentView.prototype._domTreeElementAdded):
     52        (WI.DOMTreeContentView.prototype._domBreakpointAddedOrRemoved):
     53        (WI.DOMTreeContentView.prototype._handleDOMBreakpointDisabledStateChanged):
     54        (WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
     55        (WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
     56        (WI.DOMTreeContentView.prototype._updateBreakpointStatus):
     57        (WI.DOMTreeContentView.prototype._restoreBreakpointsAfterUpdate):
     58        (WI.DOMTreeContentView.prototype._handleDOMBreakpointDOMNodeChanged): Deleted.
     59        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
     60        (WI.SourcesNavigationSidebarPanel):
     61        (WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
     62        (WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeWillChange): Added.
     63        (WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeDidChange): Added.
     64        (WI.SourcesNavigationSidebarPanel.prototype._handleDOMBreakpointDOMNodeChanged): Deleted.
     65        Use a `WI.DOMNode` instead of an identifier instead of fetching the node for the identifier
     66        each time and checking if is valid.
     67
    1682020-09-04  Patrick Angle  <pangle@apple.com>
    269
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js

    r266538 r266669  
    3232        this._domBreakpointURLMap = new Multimap;
    3333        this._domBreakpointFrameIdentifierMap = new Map;
     34        this._clearingDOMBreakpointsForRemovedDOMNode = false;
    3435
    3536        this._listenerBreakpoints = [];
     
    4344
    4445        WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleDOMBreakpointDisabledStateChanged, this);
     46        WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
     47        WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
     48        WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._handleDOMBreakpointEditablePropertyChanged, this);
     49        WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._handleDOMBreakpointActionsChanged, this);
     50        WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeWillChange, this._handleDOMBreakpointDOMNodeWillChange, this);
     51        WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeDidChange, this._handleDOMBreakpointDOMNodeDidChange, this);
    4552
    4653        WI.EventBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleEventBreakpointDisabledStateChanged, this);
     
    231238            let frame = frames.shift();
    232239
    233             let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame.id);
     240            let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame);
    234241            if (domBreakpointNodeIdentifierMap)
    235242                resolvedBreakpoints.pushAll(domBreakpointNodeIdentifierMap.values());
     
    251258            return [];
    252259
    253         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
     260        let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
    254261        if (!domBreakpointNodeIdentifierMap)
    255262            return [];
    256263
    257         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
     264        let breakpoints = domBreakpointNodeIdentifierMap.get(node);
    258265        return breakpoints ? Array.from(breakpoints) : [];
    259266    }
     
    280287    addDOMBreakpoint(breakpoint)
    281288    {
    282         console.assert(breakpoint instanceof WI.DOMBreakpoint);
     289        console.assert(breakpoint instanceof WI.DOMBreakpoint, breakpoint);
     290        console.assert(breakpoint.url, breakpoint);
    283291        if (!breakpoint || !breakpoint.url)
    284292            return;
    285293
    286         if (breakpoint.special) {
     294        console.assert(!breakpoint.special, breakpoint);
     295
     296        this._domBreakpointURLMap.add(breakpoint.url, breakpoint);
     297
     298        if (breakpoint.domNode) {
     299            this._resolveDOMBreakpoint(breakpoint, breakpoint.domNode);
     300
     301            if (!breakpoint.disabled) {
     302                // We should get the target associated with the nodeIdentifier of this breakpoint.
     303                let target = WI.assumingMainTarget();
     304                if (target)
     305                    this._setDOMBreakpoint(breakpoint, target);
     306            }
     307
    287308            this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
    288             return;
    289         }
    290 
    291         this._domBreakpointURLMap.add(breakpoint.url, breakpoint);
    292 
    293         if (breakpoint.domNodeIdentifier)
    294             this._resolveDOMBreakpoint(breakpoint, breakpoint.domNodeIdentifier);
    295 
    296         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, {breakpoint});
     309
     310            WI.debuggerManager.addProbesForBreakpoint(breakpoint);
     311        } else
     312            this._speculativelyResolveDOMBreakpoint(breakpoint);
    297313
    298314        if (!this._restoringBreakpoints)
     
    302318    removeDOMBreakpoint(breakpoint)
    303319    {
    304         console.assert(breakpoint instanceof WI.DOMBreakpoint);
    305         if (!breakpoint)
    306             return;
    307 
    308         if (breakpoint.special) {
    309             breakpoint.disabled = true;
     320        console.assert(breakpoint instanceof WI.DOMBreakpoint, breakpoint);
     321        console.assert(breakpoint.url, breakpoint);
     322        if (!breakpoint || !breakpoint.url)
     323            return;
     324
     325        console.assert(!breakpoint.special, breakpoint);
     326
     327        // Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
     328        breakpoint.disabled = true;
     329        breakpoint.clearActions();
     330
     331        this._domBreakpointURLMap.delete(breakpoint.url);
     332
     333        if (breakpoint.domNode) {
     334            if (breakpoint.domNode.frame) {
     335                let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(breakpoint.domNode.frame);
     336                domBreakpointNodeIdentifierMap.delete(breakpoint.domNode, breakpoint);
     337                if (!domBreakpointNodeIdentifierMap.size)
     338                    this._domBreakpointFrameIdentifierMap.delete(breakpoint.domNode.frame);
     339            }
     340
    310341            this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
    311             return;
    312         }
    313 
    314         this._detachDOMBreakpoint(breakpoint);
    315 
    316         this._domBreakpointURLMap.delete(breakpoint.url);
    317 
    318         if (!breakpoint.disabled) {
    319             // We should get the target associated with the nodeIdentifier of this breakpoint.
    320             let target = WI.assumingMainTarget();
    321             target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
    322         }
    323 
    324         this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, {breakpoint});
    325 
    326         breakpoint.domNodeIdentifier = null;
     342
     343            breakpoint.domNode = null;
     344        }
    327345
    328346        if (!this._restoringBreakpoints)
     
    510528    // Private
    511529
    512     _detachDOMBreakpoint(breakpoint)
    513     {
    514         let nodeIdentifier = breakpoint.domNodeIdentifier;
    515         let node = WI.domManager.nodeForId(nodeIdentifier);
    516         console.assert(node, "Missing DOM node for breakpoint.", breakpoint);
    517         if (!node || !node.frame)
    518             return;
    519 
    520         let frameIdentifier = node.frame.id;
    521         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
    522         console.assert(domBreakpointNodeIdentifierMap, "Missing DOM breakpoints for node parent frame.", node);
    523         if (!domBreakpointNodeIdentifierMap)
    524             return;
    525 
    526         domBreakpointNodeIdentifierMap.delete(nodeIdentifier, breakpoint);
    527 
    528         if (!domBreakpointNodeIdentifierMap.size)
    529             this._domBreakpointFrameIdentifierMap.delete(frameIdentifier);
    530     }
    531 
    532     _detachBreakpointsForFrame(frame)
    533     {
    534         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame.id);
    535         if (!domBreakpointNodeIdentifierMap)
    536             return;
    537 
    538         this._domBreakpointFrameIdentifierMap.delete(frame.id);
    539 
    540         for (let breakpoint of domBreakpointNodeIdentifierMap.values())
    541             breakpoint.domNodeIdentifier = null;
     530    _detachDOMBreakpointsForFrame(frame)
     531    {
     532        let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frame);
     533        if (domBreakpointNodeIdentifierMap) {
     534            this._domBreakpointFrameIdentifierMap.delete(frame);
     535
     536            this._clearingDOMBreakpointsForRemovedDOMNode = true;
     537            for (let breakpoint of domBreakpointNodeIdentifierMap.values())
     538                breakpoint.domNode = null;
     539            this._clearingDOMBreakpointsForRemovedDOMNode = false;
     540        }
     541
     542        for (let childFrame of frame.childFrameCollection)
     543            this._detachDOMBreakpointsForFrame(childFrame);
    542544    }
    543545
     
    548550            return;
    549551
    550         for (let breakpoint of domBreakpoints) {
    551             if (breakpoint.domNodeIdentifier)
    552                 continue;
    553 
    554             WI.domManager.pushNodeByPathToFrontend(breakpoint.path, (nodeIdentifier) => {
    555                 if (breakpoint.domNodeIdentifier) {
    556                     // This breakpoint may have been resolved by a node being inserted before this
    557                     // callback is invoked.  If so, the `nodeIdentifier` should match, so don't try
    558                     // to resolve it again as it would've already been resolved.
    559                     console.assert(breakpoint.domNodeIdentifier === nodeIdentifier);
    560                     return;
    561                 }
    562 
    563                 if (!nodeIdentifier)
    564                     return;
    565 
    566                 this._restoringBreakpoints = true;
    567                 this._resolveDOMBreakpoint(breakpoint, nodeIdentifier);
    568                 this._restoringBreakpoints = false;
    569             });
    570         }
    571     }
    572 
    573     _resolveDOMBreakpoint(breakpoint, nodeIdentifier)
    574     {
    575         let node = WI.domManager.nodeForId(nodeIdentifier);
    576         console.assert(node, "Missing DOM node for nodeIdentifier.", nodeIdentifier);
    577         if (!node || !node.frame)
    578             return;
    579 
    580         let frameIdentifier = node.frame.id;
    581         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(frameIdentifier);
     552        for (let breakpoint of domBreakpoints)
     553            this._speculativelyResolveDOMBreakpoint(breakpoint);
     554    }
     555
     556    _speculativelyResolveDOMBreakpoint(breakpoint)
     557    {
     558        if (breakpoint.domNode)
     559            return;
     560
     561        WI.domManager.pushNodeByPathToFrontend(breakpoint.path, (nodeIdentifier) => {
     562            if (!nodeIdentifier)
     563                return;
     564
     565            if (breakpoint.domNode) {
     566                // This breakpoint may have been resolved by a node being inserted before this
     567                // callback is invoked.  If so, the `nodeIdentifier` should match, so don't try
     568                // to resolve it again as it would've already been resolved.
     569                console.assert(breakpoint.domNode.id === nodeIdentifier);
     570                return;
     571            }
     572
     573            this._restoringBreakpoints = true;
     574            this._resolveDOMBreakpoint(breakpoint, WI.domManager.nodeForId(nodeIdentifier));
     575            this._restoringBreakpoints = false;
     576        });
     577    }
     578
     579    _resolveDOMBreakpoint(breakpoint, node)
     580    {
     581        console.assert(node instanceof WI.DOMNode, node);
     582
     583        if (!node.frame)
     584            return;
     585
     586        let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
    582587        if (!domBreakpointNodeIdentifierMap) {
    583588            domBreakpointNodeIdentifierMap = new Multimap;
    584             this._domBreakpointFrameIdentifierMap.set(frameIdentifier, domBreakpointNodeIdentifierMap);
    585         }
    586 
    587         domBreakpointNodeIdentifierMap.add(nodeIdentifier, breakpoint);
    588 
    589         breakpoint.domNodeIdentifier = nodeIdentifier;
    590 
    591         if (!breakpoint.disabled) {
    592             // We should get the target associated with the nodeIdentifier of this breakpoint.
    593             let target = WI.assumingMainTarget();
    594             if (target)
    595                 this._updateDOMBreakpoint(breakpoint, target);
    596         }
    597     }
    598 
    599     _updateDOMBreakpoint(breakpoint, target)
    600     {
    601         console.assert(target.type !== WI.TargetType.Worker, "Worker targets do not support DOM breakpoints");
    602         if (target.type === WI.TargetType.Worker)
    603             return;
    604 
    605         if (!target.hasCommand("DOMDebugger.setDOMBreakpoint") || !target.hasCommand("DOMDebugger.removeDOMBreakpoint"))
    606             return;
    607 
    608         if (!breakpoint.domNodeIdentifier)
    609             return;
    610 
    611         if (breakpoint.disabled)
    612             target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
    613         else {
    614             if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
    615                 WI.debuggerManager.breakpointsEnabled = true;
    616 
    617             target.DOMDebuggerAgent.setDOMBreakpoint(breakpoint.domNodeIdentifier, breakpoint.type);
    618         }
     589            this._domBreakpointFrameIdentifierMap.set(node.frame, domBreakpointNodeIdentifierMap);
     590        }
     591
     592        domBreakpointNodeIdentifierMap.add(node, breakpoint);
     593
     594        breakpoint.domNode = node;
     595    }
     596
     597    _setDOMBreakpoint(breakpoint, target)
     598    {
     599        console.assert(!breakpoint.disabled, breakpoint);
     600        console.assert(breakpoint.domNode instanceof WI.DOMNode, breakpoint);
     601        console.assert(target.type !== WI.TargetType.Worker, "Worker targets do not support DOM breakpoints", target);
     602
     603        // COMPATIBILITY (iOS 10.3): DOMDebugger.setDOMBreakpoint did not exist yet.
     604        if (!target.hasCommand("DOMDebugger.setDOMBreakpoint"))
     605            return;
     606
     607        if (!this._restoringBreakpoints && !WI.debuggerManager.breakpointsDisabledTemporarily)
     608            WI.debuggerManager.breakpointsEnabled = true;
     609
     610        target.DOMDebuggerAgent.setDOMBreakpoint.invoke({
     611            nodeId: breakpoint.domNode.id,
     612            type: breakpoint.type,
     613            options: breakpoint.optionsToProtocol(),
     614        });
     615    }
     616
     617    _removeDOMBreakpoint(breakpoint, target)
     618    {
     619        console.assert(breakpoint.domNode instanceof WI.DOMNode, breakpoint);
     620        console.assert(target.type !== WI.TargetType.Worker, "Worker targets do not support DOM breakpoints", target);
     621
     622        // COMPATIBILITY (iOS 10.3): DOMDebugger.removeDOMBreakpoint did not exist yet.
     623        if (!target.hasCommand("DOMDebugger.removeDOMBreakpoint"))
     624            return;
     625
     626        target.DOMDebuggerAgent.removeDOMBreakpoint(breakpoint.domNode.id, breakpoint.type);
    619627    }
    620628
     
    773781    {
    774782        let breakpoint = event.target;
     783
     784        if (!this._restoringBreakpoints)
     785            WI.objectStores.domBreakpoints.putObject(breakpoint);
     786
     787        if (!breakpoint.domNode)
     788            return;
     789
     790        // We should get the target associated with the nodeIdentifier of this breakpoint.
    775791        let target = WI.assumingMainTarget();
    776         if (target)
    777             this._updateDOMBreakpoint(breakpoint, target);
     792        if (target) {
     793            if (breakpoint.disabled)
     794                this._removeDOMBreakpoint(breakpoint, target);
     795            else
     796                this._setDOMBreakpoint(breakpoint, target);
     797        }
     798    }
     799
     800    _handleDOMBreakpointEditablePropertyChanged(event)
     801    {
     802        let breakpoint = event.target;
    778803
    779804        if (!this._restoringBreakpoints)
    780805            WI.objectStores.domBreakpoints.putObject(breakpoint);
     806
     807        if (!breakpoint.domNode)
     808            return;
     809
     810        if (breakpoint.disabled)
     811            return;
     812
     813        this._restoringBreakpoints = true;
     814        // We should get the target associated with the nodeIdentifier of this breakpoint.
     815        let target = WI.assumingMainTarget();
     816        if (target) {
     817            // Clear the old breakpoint from the backend before setting the new one.
     818            this._removeDOMBreakpoint(breakpoint, target);
     819            this._setDOMBreakpoint(breakpoint, target);
     820        }
     821        this._restoringBreakpoints = false;
     822    }
     823
     824    _handleDOMBreakpointActionsChanged(event)
     825    {
     826        let breakpoint = event.target;
     827
     828        this._handleDOMBreakpointEditablePropertyChanged(event);
     829
     830        if (!breakpoint.domNode)
     831            return;
     832
     833        WI.debuggerManager.updateProbesForBreakpoint(breakpoint);
     834    }
     835
     836    _handleDOMBreakpointDOMNodeWillChange(event)
     837    {
     838        if (this._clearingDOMBreakpointsForRemovedDOMNode)
     839            return;
     840
     841        let breakpoint = event.target;
     842
     843        if (!breakpoint.domNode)
     844            return;
     845
     846        if (!breakpoint.disabled) {
     847            // We should get the target associated with the nodeIdentifier of this breakpoint.
     848            let target = WI.assumingMainTarget();
     849            if (target)
     850                this._removeDOMBreakpoint(breakpoint, target);
     851        }
     852
     853        WI.debuggerManager.removeProbesForBreakpoint(breakpoint);
     854    }
     855
     856    _handleDOMBreakpointDOMNodeDidChange(event)
     857    {
     858        let breakpoint = event.target;
     859
     860        if (!breakpoint.domNode)
     861            return;
     862
     863        if (!breakpoint.disabled) {
     864            // We should get the target associated with the nodeIdentifier of this breakpoint.
     865            let target = WI.assumingMainTarget();
     866            if (target)
     867                this._setDOMBreakpoint(breakpoint, target);
     868        }
     869
     870        WI.debuggerManager.addProbesForBreakpoint(breakpoint);
    781871    }
    782872
     
    882972    {
    883973        let frame = event.data.childFrame;
    884         this._detachBreakpointsForFrame(frame);
     974        this._detachDOMBreakpointsForFrame(frame);
    885975    }
    886976
     
    894984        let frame = event.target;
    895985        if (frame.isMainFrame()) {
     986            this._clearingDOMBreakpointsForRemovedDOMNode = true;
    896987            for (let breakpoint of this._domBreakpointURLMap.values())
    897                 breakpoint.domNodeIdentifier = null;
     988                breakpoint.domNode = null;
     989            this._clearingDOMBreakpointsForRemovedDOMNode = false;
    898990
    899991            this._domBreakpointFrameIdentifierMap.clear();
    900992        } else
    901             this._detachBreakpointsForFrame(frame);
     993            this._detachDOMBreakpointsForFrame(frame);
    902994
    903995        this._speculativelyResolveDOMBreakpointsForURL(frame.url);
     
    9151007            return;
    9161008
     1009        let resolvableBreakpoints = [];
    9171010        for (let breakpoint of breakpoints) {
    918             if (breakpoint.domNodeIdentifier)
    919                 continue;
    920 
    921             if (breakpoint.path !== node.path())
    922                 continue;
    923 
    924             this._restoringBreakpoints = true;
    925             this._resolveDOMBreakpoint(breakpoint, node.id);
    926             this._restoringBreakpoints = false;
     1011            if (!breakpoint.domNode)
     1012                resolvableBreakpoints.push(breakpoint);
     1013        }
     1014        if (!resolvableBreakpoints.length)
     1015            return;
     1016
     1017        // This is not very expensive because `WI.DOMNode` children are lazily populated, so it's
     1018        // unlikely that there will be a deep subtree to walk.
     1019        let stack = [node];
     1020        while (stack.length) {
     1021            let child = stack.pop();
     1022            let path = child.path();
     1023
     1024            for (let i = resolvableBreakpoints.length - 1; i >= 0; --i) {
     1025                if (resolvableBreakpoints[i].path === path) {
     1026                    this._restoringBreakpoints = true;
     1027                    this._resolveDOMBreakpoint(resolvableBreakpoints[i], child);
     1028                    this._restoringBreakpoints = false;
     1029
     1030                    resolvableBreakpoints.splice(i, 1);
     1031                }
     1032            }
     1033            if (!resolvableBreakpoints.length)
     1034                break;
     1035
     1036            if (child.children?.length)
     1037                stack.pushAll(child.children);
    9271038        }
    9281039    }
     
    9341045            return;
    9351046
    936         let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame.id);
     1047        let domBreakpointNodeIdentifierMap = this._domBreakpointFrameIdentifierMap.get(node.frame);
    9371048        if (!domBreakpointNodeIdentifierMap)
    9381049            return;
    9391050
    940         let breakpoints = domBreakpointNodeIdentifierMap.get(node.id);
    941         if (!breakpoints)
    942             return;
    943 
    944         domBreakpointNodeIdentifierMap.delete(node.id);
    945 
    946         if (!domBreakpointNodeIdentifierMap.size)
    947             this._domBreakpointFrameIdentifierMap.delete(node.frame.id);
    948 
    949         for (let breakpoint of breakpoints)
    950             breakpoint.domNodeIdentifier = null;
     1051        for (let [breakpointOwner, breakpoints] of domBreakpointNodeIdentifierMap.sets()) {
     1052            if (breakpointOwner == node || node.isAncestor(breakpointOwner)) {
     1053                this._clearingDOMBreakpointsForRemovedDOMNode = true;
     1054                for (let breakpoint of breakpoints)
     1055                    breakpoint.domNode = null;
     1056                this._clearingDOMBreakpointsForRemovedDOMNode = false;
     1057
     1058                domBreakpointNodeIdentifierMap.delete(breakpointOwner);
     1059                if (!domBreakpointNodeIdentifierMap.size) {
     1060                    this._domBreakpointFrameIdentifierMap.delete(node.frame);
     1061                    break;
     1062                }
     1063            }
     1064        }
    9511065    }
    9521066};
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js

    r266074 r266669  
    381381
    382382        var parent = this._idToDOMNode[parentId];
     383
     384        if (parent.children) {
     385            for (let node of parent.children)
     386                this.dispatchEventToListeners(WI.DOMManager.Event.NodeRemoved, {node, parent});
     387        }
     388
    383389        parent._setChildrenPayload(payloads);
     390
     391        for (let node of parent.children)
     392            this.dispatchEventToListeners(WI.DOMManager.Event.NodeInserted, {node, parent});
    384393    }
    385394
  • trunk/Source/WebInspectorUI/UserInterface/Models/DOMBreakpoint.js

    r266480 r266669  
    2626WI.DOMBreakpoint = class DOMBreakpoint extends WI.Breakpoint
    2727{
    28     constructor(domNodeOrInfo, type, {disabled} = {})
     28    constructor(domNodeOrInfo, type, {disabled, actions, condition, ignoreCount, autoContinue} = {})
    2929    {
    3030        console.assert(domNodeOrInfo instanceof WI.DOMNode || typeof domNodeOrInfo === "object", domNodeOrInfo);
    3131        console.assert(Object.values(WI.DOMBreakpoint.Type).includes(type), type);
    3232
    33         super({disabled});
     33        super({disabled, actions, condition, ignoreCount, autoContinue});
    3434
    3535        if (domNodeOrInfo instanceof WI.DOMNode) {
    36             this._domNodeIdentifier = domNodeOrInfo.id;
     36            this._domNode = domNodeOrInfo;
    3737            this._path = domNodeOrInfo.path();
    3838            console.assert(WI.networkManager.mainFrame);
    3939            this._url = WI.networkManager.mainFrame.url;
    4040        } else if (domNodeOrInfo && typeof domNodeOrInfo === "object") {
    41             this._domNodeIdentifier = null;
     41            this._domNode = null;
    4242            this._path = domNodeOrInfo.path;
    4343            this._url = domNodeOrInfo.url;
     
    6464        }
    6565
    66         console.error();
     66        console.assert(false, "Unknown DOM breakpoint type", type);
    6767        return WI.UIString("DOM");
    6868    }
     
    7272        return new WI.DOMBreakpoint(json, json.type, {
    7373            disabled: json.disabled,
     74            condition: json.condition,
     75            actions: json.actions?.map((actionJSON) => WI.BreakpointAction.fromJSON(actionJSON)) || [],
     76            ignoreCount: json.ignoreCount,
     77            autoContinue: json.autoContinue,
    7478        });
    7579    }
     
    8690    }
    8791
    88     get domNodeIdentifier()
     92    get editable()
    8993    {
    90         return this._domNodeIdentifier;
     94        // COMPATIBILITY (iOS 14): DOMDebugger.setDOMBreakpoint did not have an "options" parameter yet.
     95        return InspectorBackend.hasCommand("DOMDebugger.setDOMBreakpoint", "options");
    9196    }
    9297
    93     set domNodeIdentifier(nodeIdentifier)
     98    get domNode()
    9499    {
    95         if (this._domNodeIdentifier === nodeIdentifier)
     100        return this._domNode;
     101    }
     102
     103    set domNode(domNode)
     104    {
     105        console.assert(domNode instanceof WI.DOMNode, domNode);
     106        console.assert(!this._domNode !== !domNode, "domNode should not change once set");
     107        if (!this._domNode === !domNode)
    96108            return;
    97109
    98         let data = {};
    99         if (!nodeIdentifier)
    100             data.oldNodeIdentifier = this._domNodeIdentifier;
    101 
    102         this._domNodeIdentifier = nodeIdentifier;
    103 
    104         this.dispatchEventToListeners(WI.DOMBreakpoint.Event.DOMNodeChanged, data);
     110        this.dispatchEventToListeners(WI.DOMBreakpoint.Event.DOMNodeWillChange);
     111        this._domNode = domNode;
     112        this.dispatchEventToListeners(WI.DOMBreakpoint.Event.DOMNodeDidChange);
    105113    }
    106114
     
    138146
    139147WI.DOMBreakpoint.Event = {
    140     DOMNodeChanged: "dom-breakpoint-dom-node-changed",
     148    DOMNodeDidChange: "dom-breakpoint-dom-node-did-change",
     149    DOMNodeWillChange: "dom-breakpoint-dom-node-will-change",
    141150};
    142151
  • trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeContentView.js

    r266074 r266669  
    8686
    8787        this._breakpointGutterEnabled = false;
    88         this._pendingBreakpointNodeIdentifiers = new Set;
     88        this._pendingBreakpointNodes = new Set;
    8989
    9090        this._defaultAppearanceDidChange();
     
    9797
    9898            WI.DOMBreakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._handleDOMBreakpointDisabledStateChanged, this);
    99             WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
     99            WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeWillChange, this._handleDOMBreakpointDOMNodeWillChange, this);
     100            WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeDidChange, this._handleDOMBreakpointDOMNodeDidChange, this);
    100101
    101102            this._breakpointsEnabledDidChange();
     
    171172
    172173        this._domTreeOutline.close();
    173         this._pendingBreakpointNodeIdentifiers.clear();
     174        this._pendingBreakpointNodes.clear();
    174175    }
    175176
     
    488489    _domTreeElementAdded(event)
    489490    {
    490         if (!this._pendingBreakpointNodeIdentifiers.size)
     491        if (!this._pendingBreakpointNodes.size)
    491492            return;
    492493
     
    497498            return;
    498499
    499         if (!this._pendingBreakpointNodeIdentifiers.delete(node.id))
     500        if (!this._pendingBreakpointNodes.delete(node))
    500501            return;
    501502
     
    754755    {
    755756        let breakpoint = event.data.breakpoint;
    756         this._updateBreakpointStatus(breakpoint.domNodeIdentifier);
     757        this._updateBreakpointStatus(breakpoint.domNode);
    757758    }
    758759
     
    760761    {
    761762        let breakpoint = event.target;
    762         this._updateBreakpointStatus(breakpoint.domNodeIdentifier);
    763     }
    764 
    765     _handleDOMBreakpointDOMNodeChanged(event)
     763        this._updateBreakpointStatus(breakpoint.domNode);
     764    }
     765
     766    _handleDOMBreakpointDOMNodeWillChange(event)
    766767    {
    767768        let breakpoint = event.target;
    768         let nodeIdentifier = breakpoint.domNodeIdentifier || event.data.oldNodeIdentifier;
    769         this._updateBreakpointStatus(nodeIdentifier);
    770     }
    771 
    772     _updateBreakpointStatus(nodeIdentifier)
    773     {
    774         let domNode = WI.domManager.nodeForId(nodeIdentifier);
     769        this._updateBreakpointStatus(breakpoint.domNode);
     770    }
     771
     772    _handleDOMBreakpointDOMNodeDidChange(event)
     773    {
     774        let breakpoint = event.target;
     775        this._updateBreakpointStatus(breakpoint.domNode);
     776    }
     777
     778    _updateBreakpointStatus(domNode)
     779    {
    775780        if (!domNode)
    776781            return;
     
    778783        let treeElement = this._domTreeOutline.findTreeElement(domNode);
    779784        if (!treeElement) {
    780             this._pendingBreakpointNodeIdentifiers.add(nodeIdentifier);
     785            this._pendingBreakpointNodes.add(domNode);
    781786            return;
    782787        }
     
    796801    _restoreBreakpointsAfterUpdate()
    797802    {
    798         this._pendingBreakpointNodeIdentifiers.clear();
     803        this._pendingBreakpointNodes.clear();
    799804
    800805        this.breakpointGutterEnabled = false;
     
    802807        let updatedNodes = new Set;
    803808        for (let breakpoint of WI.domDebuggerManager.domBreakpoints) {
    804             if (updatedNodes.has(breakpoint.domNodeIdentifier))
     809            if (updatedNodes.has(breakpoint.domNode))
    805810                continue;
    806811
    807             this._updateBreakpointStatus(breakpoint.domNodeIdentifier);
     812            this._updateBreakpointStatus(breakpoint.domNode);
    808813        }
    809814    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js

    r266538 r266669  
    329329        WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
    330330
    331         WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
     331        WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeWillChange, this._handleDOMBreakpointDOMNodeWillChange, this);
     332        WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeDidChange, this._handleDOMBreakpointDOMNodeDidChange, this);
    332333
    333334        WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleConsoleIssueAdded, this);
     
    12581259
    12591260        let getDOMNodeTreeElement = (domNode) => {
    1260             console.assert(domNode, "Missing DOMNode for identifier", breakpoint.domNodeIdentifier);
    1261             if (!domNode)
    1262                 return null;
    1263 
    12641261            let domNodeTreeElement = this._breakpointsTreeOutline.findTreeElement(domNode);
    12651262            if (!domNodeTreeElement) {
     
    12811278            options.classNames = ["microtask"];
    12821279        else if (breakpoint instanceof WI.DOMBreakpoint) {
    1283             if (!breakpoint.domNodeIdentifier)
     1280            if (!breakpoint.domNode)
    12841281                return null;
    12851282
    12861283            constructor = WI.DOMBreakpointTreeElement;
    12871284
    1288             let domNode = WI.domManager.nodeForId(breakpoint.domNodeIdentifier);
    1289             parentTreeElement = getDOMNodeTreeElement(domNode);
     1285            parentTreeElement = getDOMNodeTreeElement(breakpoint.domNode);
    12901286        } else if (breakpoint instanceof WI.EventBreakpoint) {
    12911287            constructor = WI.EventBreakpointTreeElement;
     
    16721668            if (pauseData.targetNodeId) {
    16731669                console.assert(domBreakpoint.type === WI.DOMBreakpoint.Type.SubtreeModified || domBreakpoint.type === WI.DOMBreakpoint.Type.NodeRemoved);
     1670                console.assert(pauseData.targetNodeId !== domBreakpoint.domNode.id, pauseData.targetNodeId, domBreakpoint);
    16741671                updateTargetDescription(pauseData.targetNodeId);
    16751672            } else if (pauseData.targetNode) { // COMPATIBILITY (iOS 13): `targetNode` was renamed to `targetNodeId` and was changed from a `Runtime.RemoteObject` to a `DOM.NodeId`.
     
    23822379    }
    23832380
    2384     _handleDOMBreakpointDOMNodeChanged(event)
     2381    _handleDOMBreakpointDOMNodeWillChange(event)
    23852382    {
    23862383        let breakpoint = event.target;
    2387         if (breakpoint.domNodeIdentifier)
    2388             this._addBreakpoint(breakpoint);
    2389         else
    2390             this._removeBreakpoint(breakpoint);
     2384        this._removeBreakpoint(breakpoint);
     2385    }
     2386
     2387    _handleDOMBreakpointDOMNodeDidChange(event)
     2388    {
     2389        let breakpoint = event.target;
     2390        this._addBreakpoint(breakpoint);
    23912391    }
    23922392
Note: See TracChangeset for help on using the changeset viewer.