Web Inspector: support breakpoints for timers and animation-frame events
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Aug 2018 21:36:40 +0000 (21:36 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Aug 2018 21:36:40 +0000 (21:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=188778

Reviewed by Brian Burg.

Source/JavaScriptCore:

* inspector/protocol/Debugger.json:
Add `AnimationFrame` and `Timer` types to the list of pause reasons.

* inspector/protocol/DOMDebugger.json:
Introduced `setEventBreakpoint` and `removeEventBreakpoint` to replace the more specific:
 - `setEventListenerBreakpoint`
 - `removeEventListenerBreakpoint`
 - `setInstrumentationBreakpoint`
 - `removeInstrumentationBreakpoint`
Also created an `EventBreakpointType` to enumerate the available types of event breakpoints.

* inspector/scripts/codegen/generate_cpp_protocol_types_header.py:
(CppProtocolTypesHeaderGenerator.generate_output):
(CppProtocolTypesHeaderGenerator._generate_forward_declarations_for_binding_traits):
(CppProtocolTypesHeaderGenerator._generate_declarations_for_enum_conversion_methods):
(CppProtocolTypesHeaderGenerator._generate_hash_declarations): Added.
Generate `DefaultHash` for all `enum class` used by inspector protocols.

* inspector/scripts/tests/generic/expected/commands-with-async-attribute.json-result:
* inspector/scripts/tests/generic/expected/commands-with-optional-call-return-parameters.json-result:
* inspector/scripts/tests/generic/expected/enum-values.json-result:
* inspector/scripts/tests/generic/expected/type-declaration-array-type.json-result:
* inspector/scripts/tests/generic/expected/type-declaration-enum-type.json-result:
* inspector/scripts/tests/generic/expected/type-declaration-object-type.json-result:
* inspector/scripts/tests/generic/expected/type-requiring-runtime-casts.json-result:

Source/WebCore:

The original implementation of "instrumentation" breakpoints relied upon the frontend
sending somewhat arbitrary strings when enabling breakpoints for specific events. As an
example, setting a breakpoint for `requestAnimationFrame` expects `"animationFrameFired"`
as the string, which doesn't make much sense. This patch removes the usage of these strings
and instead expects the agent to implement a method that matches what is happening.

Tests: inspector/dom-debugger/event-animation-frame-breakpoints.html
       inspector/dom-debugger/event-listener-breakpoints.html
       inspector/dom-debugger/event-timer-breakpoints.html

* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::willFireTimer):
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::didInstallTimerImpl):
(WebCore::InspectorInstrumentation::didRemoveTimerImpl):
(WebCore::InspectorInstrumentation::willFireTimerImpl):
(WebCore::InspectorInstrumentation::didRequestAnimationFrameImpl):
(WebCore::InspectorInstrumentation::didCancelAnimationFrameImpl):
(WebCore::InspectorInstrumentation::willFireAnimationFrameImpl):
(WebCore::InspectorInstrumentation::pauseOnNativeEventIfNeeded): Deleted.

* inspector/agents/InspectorDOMDebuggerAgent.h:
* inspector/agents/InspectorDOMDebuggerAgent.cpp:
(WebCore::InspectorDOMDebuggerAgent::setEventBreakpoint): Added.
(WebCore::InspectorDOMDebuggerAgent::removeEventBreakpoint): Added.
(WebCore::InspectorDOMDebuggerAgent::willHandleEvent):
(WebCore::InspectorDOMDebuggerAgent::willFireTimer): Added.
(WebCore::InspectorDOMDebuggerAgent::willFireAnimationFrame): Added.
(WebCore::InspectorDOMDebuggerAgent::setEventListenerBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::setInstrumentationBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::setBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::removeEventListenerBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::removeBreakpoint): Deleted.
(WebCore::InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded): Deleted.
Unify the event listener and instrumentation breakpoint commands into a single method,
`setEventBreakpoint`, that takes in both an `EventBreakpointType` and `eventName`.

* page/DOMTimer.cpp:
(WebCore::DOMTimer::fired):

Source/WebInspectorUI:

Add a `type` to `WI.EventBreakpoint` that matches `DOMDebugger.EventBreakpointType`:
 - `AnimationFrame` for `requestAnimationFrame`
 - `Listener` for any named DOM Event
 - `Timer` for `setTimeout` and `setInterval`

Modified `WI.EventBreakpointPopover` to provide ways for selecting these other types, which
is then passed to `WI.DOMDebuggerManager`, which now calls through to the newly added
`DOMDebugger.removeEventBreakpoint` and `DOMDebugger.setEventBreakpoint` that sets
breakpoints for all event types.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Images/EventBreakpointAnimationFrame.svg: Added.
* UserInterface/Images/EventBreakpointListener.svg: Renamed from Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg.
* UserInterface/Images/EventBreakpointTimer.svg: Added.

* UserInterface/Controllers/DOMDebuggerManager.js:
(WI.DOMDebuggerManager.supportsEventBreakpoints): Added.
(WI.DOMDebuggerManager.prototype.eventBreakpointForTypeAndEventName): Added.
(WI.DOMDebuggerManager.prototype.addEventBreakpoint):
(WI.DOMDebuggerManager.prototype.removeEventBreakpoint.breakpointRemoved): Added.
(WI.DOMDebuggerManager.prototype.removeEventBreakpoint):
(WI.DOMDebuggerManager.prototype._updateEventBreakpoint):
(WI.DOMDebuggerManager.prototype.eventBreakpointForEventName): Deleted.

* UserInterface/Controllers/DOMTreeManager.js:
(WI.DOMTreeManager.prototype.setBreakpointForEventListener):

* UserInterface/Controllers/DebuggerManager.js:
(WI.DebuggerManager.prototype._pauseReasonFromPayload):

* UserInterface/Models/EventBreakpoint.js:
(WI.EventBreakpoint):
(WI.EventBreakpoint.fromPayload):
(WI.EventBreakpoint.prototype.get type): Added.
(WI.EventBreakpoint.prototype.get serializableInfo):
(WI.EventBreakpoint.prototype.saveIdentityToCookie):

* UserInterface/Views/DebuggerSidebarPanel.js:
(WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):
(WI.DebuggerSidebarPanel.prototype.willDismissPopover):

* UserInterface/Views/EventBreakpointPopover.js:
(WI.EventBreakpointPopover):
(WI.EventBreakpointPopover.prototype.get breakpoint): Added.
(WI.EventBreakpointPopover.prototype.show):
(WI.EventBreakpointPopover.prototype.show.createOption): Added.
(WI.EventBreakpointPopover.prototype.dismiss): Added.
(WI.EventBreakpointPopover.prototype._presentOverTargetElement):
(WI.EventBreakpointPopover.prototype._handleTypeSelectChange): Added.
(WI.EventBreakpointPopover.prototype.get result): Deleted.
(WI.EventBreakpointPopover.prototype.get value): Deleted.
* UserInterface/Views/EventBreakpointPopover.css:
(.popover .event-breakpoint-content > .event-type): Added.
(.popover .event-breakpoint-content > input): Deleted.

* UserInterface/Views/EventBreakpointTreeElement.js:
(WI.EventBreakpointTreeElement):
* UserInterface/Views/EventBreakpointTreeElement.css:
(.breakpoint.event.animation-frame:not(.breakpoint-paused-icon) .icon): Added.
(.breakpoint.event.listener:not(.breakpoint-paused-icon) .icon): Added.
(.breakpoint.event.timer:not(.breakpoint-paused-icon) .icon): Added.
(.breakpoint.event:not(.breakpoint-paused-icon) .icon): Deleted.

LayoutTests:

* inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt: Added.
* inspector/dom-debugger/event-animation-frame-breakpoints.html: Added.

* inspector/dom-debugger/event-breakpoint-with-navigation.html:

* inspector/dom-debugger/event-timer-breakpoints-expected.txt: Added.
* inspector/dom-debugger/event-timer-breakpoints.html: Added.

* inspector/dom-debugger/event-listener-breakpoints-expected.txt: Renamed from LayoutTests/inspector/dom-debugger/event-breakpoints-expected.txt.
* inspector/dom-debugger/event-listener-breakpoints.html: Renamed from LayoutTests/inspector/dom-debugger/event-breakpoints.html.

* inspector/dom-debugger/resources/event-breakpoint-utilities.js: Added.
(TestPage.registerInitializer.window.teardown):
(TestPage.registerInitializer.window.failOnPause):
(TestPage.registerInitializer.window.addBreakpoint):
(TestPage.registerInitializer.window.removeBreakpoint):
(TestPage.registerInitializer.window.disableBreakpoint):
(TestPage.registerInitializer.window.awaitEvent):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@235248 268f45cc-cd09-0410-ab3c-d52691b4dbfc

41 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt [new file with mode: 0644]
LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints.html [new file with mode: 0644]
LayoutTests/inspector/dom-debugger/event-breakpoint-with-navigation.html
LayoutTests/inspector/dom-debugger/event-breakpoints.html [deleted file]
LayoutTests/inspector/dom-debugger/event-listener-breakpoints-expected.txt [moved from LayoutTests/inspector/dom-debugger/event-breakpoints-expected.txt with 69% similarity]
LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html [new file with mode: 0644]
LayoutTests/inspector/dom-debugger/event-timer-breakpoints-expected.txt [new file with mode: 0644]
LayoutTests/inspector/dom-debugger/event-timer-breakpoints.html [new file with mode: 0644]
LayoutTests/inspector/dom-debugger/resources/event-breakpoint-utilities.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/DOMDebugger.json
Source/JavaScriptCore/inspector/protocol/Debugger.json
Source/JavaScriptCore/inspector/scripts/codegen/generate_cpp_protocol_types_header.py
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/commands-with-async-attribute.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/commands-with-optional-call-return-parameters.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/enum-values.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/type-declaration-array-type.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/type-declaration-enum-type.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/type-declaration-object-type.json-result
Source/JavaScriptCore/inspector/scripts/tests/generic/expected/type-requiring-runtime-casts.json-result
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.cpp
Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.h
Source/WebCore/page/DOMTimer.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js
Source/WebInspectorUI/UserInterface/Controllers/DOMTreeManager.js
Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js
Source/WebInspectorUI/UserInterface/Images/EventBreakpointAnimationFrame.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/EventBreakpointListener.svg [moved from Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg with 100% similarity]
Source/WebInspectorUI/UserInterface/Images/EventBreakpointTimer.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Models/EventBreakpoint.js
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/EventBreakpointPopover.css
Source/WebInspectorUI/UserInterface/Views/EventBreakpointPopover.js
Source/WebInspectorUI/UserInterface/Views/EventBreakpointTreeElement.css
Source/WebInspectorUI/UserInterface/Views/EventBreakpointTreeElement.js

index 9f20bfb..f1f3fe5 100644 (file)
@@ -1,3 +1,29 @@
+2018-08-23  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: support breakpoints for timers and animation-frame events
+        https://bugs.webkit.org/show_bug.cgi?id=188778
+
+        Reviewed by Brian Burg.
+
+        * inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt: Added.
+        * inspector/dom-debugger/event-animation-frame-breakpoints.html: Added.
+
+        * inspector/dom-debugger/event-breakpoint-with-navigation.html:
+
+        * inspector/dom-debugger/event-timer-breakpoints-expected.txt: Added.
+        * inspector/dom-debugger/event-timer-breakpoints.html: Added.
+
+        * inspector/dom-debugger/event-listener-breakpoints-expected.txt: Renamed from LayoutTests/inspector/dom-debugger/event-breakpoints-expected.txt.
+        * inspector/dom-debugger/event-listener-breakpoints.html: Renamed from LayoutTests/inspector/dom-debugger/event-breakpoints.html.
+
+        * inspector/dom-debugger/resources/event-breakpoint-utilities.js: Added.
+        (TestPage.registerInitializer.window.teardown):
+        (TestPage.registerInitializer.window.failOnPause):
+        (TestPage.registerInitializer.window.addBreakpoint):
+        (TestPage.registerInitializer.window.removeBreakpoint):
+        (TestPage.registerInitializer.window.disableBreakpoint):
+        (TestPage.registerInitializer.window.awaitEvent):
+
 2018-08-23  Aditya Keerthi  <akeerthi@apple.com>
 
         [iOS] Support the inputmode attribute on contenteditable elements
diff --git a/LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt b/LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt
new file mode 100644 (file)
index 0000000..9c72701
--- /dev/null
@@ -0,0 +1,38 @@
+Tests for Event AnimationFrame breakpoints.
+
+
+== Running test suite: DOMDebugger.Event.AnimationFrame
+-- Running test case: DOMDebugger.Event.AnimationFrame.AddBreakpoint "requestAnimationFrame"
+Adding "requestAnimationFrame" Event Breakpoint...
+Firing "requestAnimationFrame" on window...
+PASS: Should pause before event handler is run.
+CALL STACK:
+0: [F] handleWindow_requestAnimationFrame
+ASYNC CALL STACK:
+1: --- requestAnimationFrame ---
+2: [F] trigger_requestAnimationFrame
+3: [P] Global Code
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.AnimationFrame.AddDisabledBreakpoint "requestAnimationFrame"
+Adding "requestAnimationFrame" Event Breakpoint...
+Disabling "requestAnimationFrame" Event Breakpoint...
+Firing "requestAnimationFrame" on window...
+PASS: Should not pause for disabled breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.AnimationFrame.RemoveBreakpoint "requestAnimationFrame"
+Adding "requestAnimationFrame" Event Breakpoint...
+Removing "requestAnimationFrame" Event Breakpoint...
+Firing "requestAnimationFrame" on window...
+PASS: Should not pause for removed breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.AnimationFrame.RemoveDisabledBreakpoint "requestAnimationFrame"
+Adding "requestAnimationFrame" Event Breakpoint...
+Disabling "requestAnimationFrame" Event Breakpoint...
+Removing "requestAnimationFrame" Event Breakpoint...
+Firing "requestAnimationFrame" on window...
+PASS: Should not pause for removed disabled breakpoint.
+-- Running test teardown.
+
diff --git a/LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints.html b/LayoutTests/inspector/dom-debugger/event-animation-frame-breakpoints.html
new file mode 100644 (file)
index 0000000..19f3f1b
--- /dev/null
@@ -0,0 +1,107 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="../debugger/resources/log-active-stack-trace.js"></script>
+<script src="resources/event-breakpoint-utilities.js"></script>
+<script>
+
+function handleWindow_requestAnimationFrame() {
+    TestPage.dispatchEventToFrontend("TestPage-requestAnimationFrame");
+}
+
+function trigger_requestAnimationFrame() {
+    requestAnimationFrame(handleWindow_requestAnimationFrame);
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("DOMDebugger.Event.AnimationFrame");
+
+    function addTestCasesForEventName(eventName) {
+        suite.addTestCase({
+            name: `DOMDebugger.Event.AnimationFrame.AddBreakpoint "${eventName}"`,
+            description: "Check that the debugger pauses for enabled breakpoints.",
+            test(resolve, reject) {
+                let paused = false;
+
+                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                    paused = true;
+
+                    InspectorTest.pass("Should pause before event handler is run.");
+                    logActiveStackTrace();
+
+                    WI.debuggerManager.resume()
+                    .catch(reject);
+                });
+
+                InspectorTest.singleFireEventListener(`TestPage-${eventName}`, (event) => {
+                    if (!paused) {
+                        WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                        InspectorTest.fail("Should pause before event handler is run.");
+                    }
+
+                    resolve();
+                });
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.AnimationFrame, eventName)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.AnimationFrame.AddDisabledBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for disabled breakpoints.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.AnimationFrame, eventName, "Should not pause for disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.AnimationFrame, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.AnimationFrame.RemoveBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for removed breakpoint.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.AnimationFrame, eventName, "Should not pause for removed breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.AnimationFrame, eventName)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.AnimationFrame.RemoveDisabledBreakpoint "${eventName}"`,
+            description: "Check that a disabled breakpoint can be removed.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.AnimationFrame, eventName, "Should not pause for removed disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.AnimationFrame, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+    }
+
+    addTestCasesForEventName("requestAnimationFrame");
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Tests for Event AnimationFrame breakpoints.</p>
+</body>
+</html>
index 586f59d..e70bb65 100644 (file)
@@ -44,11 +44,12 @@ function test() {
 
             InspectorTest.log("Adding \"load\" Event Breakpoint...");
 
-            let breakpoint = new WI.EventBreakpoint("load");
+            let breakpoint = new WI.EventBreakpoint(WI.EventBreakpoint.Type.Listener, "load");
 
             WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventBreakpointAdded)
             .then((event) => {
-                InspectorTest.assert(event.data.breakpoint.eventName, "load", "Breakpoint should be for expected event name.");
+                InspectorTest.assert(event.data.breakpoint.type === WI.EventBreakpoint.Type.Listener, "Breakpoint should be for expected type.");
+                InspectorTest.assert(event.data.breakpoint.eventName === "load", "Breakpoint should be for expected event name.");
 
                 InspectorTest.log("Reloading WebInspector...");
                 return InspectorTest.reloadPage();
diff --git a/LayoutTests/inspector/dom-debugger/event-breakpoints.html b/LayoutTests/inspector/dom-debugger/event-breakpoints.html
deleted file mode 100644 (file)
index 29fb9b9..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
-<script src="../debugger/resources/log-active-stack-trace.js"></script>
-<script>
-function handleBody_click(event) {
-    TestPage.dispatchEventToFrontend("TestPageBody-click");
-}
-
-function handleBody_custom(event) {
-    TestPage.dispatchEventToFrontend("TestPageBody-custom");
-}
-
-function handleX_click(event) {
-    TestPage.dispatchEventToFrontend("TestPageX-click");
-}
-
-function bodyFire_click() {
-    document.body.click();
-}
-
-function bodyFire_custom() {
-    document.body.dispatchEvent(new Event("custom"));
-}
-
-function xFire_click() {
-    document.getElementById("x").click();
-}
-
-function test() {
-    let suite = InspectorTest.createAsyncSuite("DOMDebugger.Event");
-
-    function teardown(resolve, reject) {
-        let breakpoints = WI.domDebuggerManager.eventBreakpoints;
-        for (let breakpoint of breakpoints)
-            WI.domDebuggerManager.removeEventBreakpoint(breakpoint);
-
-        resolve();
-    }
-
-    function awaitBodyEvent(eventName) {
-        return function() {
-            InspectorTest.log(`Firing "${eventName}" on body...`);
-            return InspectorTest.evaluateInPage(`bodyFire_${eventName}()`);
-        };
-    }
-
-    function failOnPause(resolve, eventName, message) {
-        let paused = false;
-
-        let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
-            paused = true;
-
-            let targetData = WI.debuggerManager.dataForTarget(WI.debuggerManager.activeCallFrame.target);
-            InspectorTest.assert(targetData.pauseReason === WI.DebuggerManager.PauseReason.EventListener, "Pause reason should be EventListener.");
-            InspectorTest.assert(targetData.pauseData.eventName === eventName, `Pause data eventName should be "${eventName}".`);
-
-            InspectorTest.fail(message);
-            logActiveStackTrace();
-
-            WI.debuggerManager.resume()
-            .catch((reason) => {
-                InspectorTest.fail(reason);
-                resolve();
-            });
-        });
-
-        InspectorTest.singleFireEventListener("TestPageBody-" + eventName, (event) => {
-            if (!paused) {
-                WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
-
-                InspectorTest.pass(message);
-            }
-
-            resolve();
-        });
-    }
-
-    function addBreakpoint(eventName) {
-        InspectorTest.log(`Adding "${eventName}" Event Breakpoint...`);
-
-        return new Promise((resolve, reject) => {
-            let breakpoint = new WI.EventBreakpoint(eventName);
-
-            WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventBreakpointAdded)
-            .then((event) => {
-                InspectorTest.assert(event.data.breakpoint.eventName === eventName, "Breakpoint should be for expected event name.");
-                InspectorTest.assert(!breakpoint.disabled, "Breakpoint should not be disabled initially.");
-                resolve(breakpoint);
-            });
-
-            WI.domDebuggerManager.addEventBreakpoint(breakpoint);
-        });
-    }
-
-    function removeBreakpoint(breakpoint) {
-        InspectorTest.log(`Removing "${breakpoint.eventName}" Event Breakpoint...`);
-
-        return new Promise((resolve, reject) => {
-            WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventBreakpointRemoved)
-            .then((event) => {
-                InspectorTest.assert(event.data.breakpoint === breakpoint, "Removed Breakpoint should be expected object.");
-                InspectorTest.assert(!WI.domDebuggerManager.eventBreakpoints.includes(breakpoint), "Breakpoint should not be in the list of breakpoints.");
-                resolve(breakpoint);
-            });
-
-            WI.domDebuggerManager.removeEventBreakpoint(breakpoint);
-        });
-    }
-
-    function disableBreakpoint(breakpoint) {
-        InspectorTest.log(`Disabling "${breakpoint.eventName}" Event Breakpoint...`);
-
-        breakpoint.disabled = true;
-        return breakpoint;
-    }
-
-    function addTestCasesForEventName(eventName) {
-        suite.addTestCase({
-            name: `DOMDebugger.Event.AddBreakpoint "${eventName}"`,
-            description: "Check that the debugger pauses for enabled breakpoints.",
-            teardown,
-            test(resolve, reject) {
-                let paused = false;
-
-                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
-                    paused = true;
-
-                    InspectorTest.pass("Should pause before event handler is run.");
-                    logActiveStackTrace();
-
-                    WI.debuggerManager.resume();
-                });
-
-                InspectorTest.singleFireEventListener(`TestPageBody-${eventName}`, (event) => {
-                    if (!paused) {
-                        WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
-
-                        InspectorTest.fail("Should pause before event handler is run.");
-                    }
-
-                    resolve();
-                });
-
-                addBreakpoint(eventName)
-                .then(awaitBodyEvent(eventName))
-                .catch(reject);
-            },
-        });
-
-        suite.addTestCase({
-            name: `DOMDebugger.Event.AddDisabledBreakpoint "${eventName}"`,
-            description: "Check that debugger does the not pause for disabled breakpoints.",
-            teardown,
-            test(resolve, reject) {
-                failOnPause(resolve, eventName, "Should not pause for disabled breakpoint.");
-
-                addBreakpoint(eventName)
-                .then(disableBreakpoint)
-                .then(awaitBodyEvent(eventName))
-                .catch(reject);
-            },
-        });
-
-        suite.addTestCase({
-            name: `DOMDebugger.Event.RemoveBreakpoint "${eventName}"`,
-            description: "Check that debugger does not pause for removed breakpoint.",
-            teardown,
-            test(resolve, reject) {
-                failOnPause(resolve, eventName, "Should not pause for removed breakpoint.");
-
-                addBreakpoint(eventName)
-                .then(removeBreakpoint)
-                .then(awaitBodyEvent(eventName))
-                .catch(reject);
-            },
-        });
-
-        suite.addTestCase({
-            name: `DOMDebugger.Event.RemoveDisabledBreakpoint "${eventName}"`,
-            description: "Check that a disabled breakpoint can be removed.",
-            teardown,
-            test(resolve, reject) {
-                failOnPause(resolve, eventName, "Should not pause for removed disabled breakpoint.");
-
-                addBreakpoint(eventName)
-                .then(disableBreakpoint)
-                .then(removeBreakpoint)
-                .then(awaitBodyEvent(eventName))
-                .catch(reject);
-            },
-        });
-
-    }
-
-    addTestCasesForEventName("click");
-    addTestCasesForEventName("custom");
-
-    suite.addTestCase({
-        name: `DOMDebugger.Event.AddMultipleBreakpoints`,
-        description: "Check that a single breakpoint pauses for every event of that type.",
-        teardown,
-        test(resolve, reject) {
-            let pauseCount = 0;
-
-            let listener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
-                ++pauseCount;
-
-                InspectorTest.pass("Should pause before event handler is run.");
-                logActiveStackTrace();
-
-                WI.debuggerManager.resume()
-                .catch((reason) => {
-                    InspectorTest.fail(reason);
-                    resolve();
-                });
-
-                if (pauseCount >= 2) {
-                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
-
-                    resolve();
-                }
-            });
-
-            addBreakpoint("click")
-            .then(() => {
-                InspectorTest.log("Firing \"click\" on div#x...");
-                return InspectorTest.evaluateInPage(`xFire_click()`);
-            })
-            .catch(reject);
-        },
-    });
-
-    suite.runTestCasesAndFinish();
-}
-</script>
-</head>
-<body onload="runTest()">
-    <p>Tests for Event Listener breakpoints.</p>
-    <div id="x"></div>
-    <script>
-        document.body.addEventListener("click", handleBody_click);
-        document.body.addEventListener("custom", handleBody_custom);
-
-        document.getElementById("x").addEventListener("click", handleX_click);
-    </script>
-</body>
-</html>
@@ -1,32 +1,32 @@
 Tests for Event Listener breakpoints.
 
 
-== Running test suite: DOMDebugger.Event
--- Running test case: DOMDebugger.Event.AddBreakpoint "click"
+== Running test suite: DOMDebugger.Event.Listener
+-- Running test case: DOMDebugger.Event.Listener.AddBreakpoint "click"
 Adding "click" Event Breakpoint...
 Firing "click" on body...
 PASS: Should pause before event handler is run.
 CALL STACK:
 0: [F] handleBody_click
-1: [F] bodyFire_click
+1: [F] trigger_click
 2: [P] Global Code
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.AddDisabledBreakpoint "click"
+-- Running test case: DOMDebugger.Event.Listener.AddDisabledBreakpoint "click"
 Adding "click" Event Breakpoint...
 Disabling "click" Event Breakpoint...
 Firing "click" on body...
 PASS: Should not pause for disabled breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.RemoveBreakpoint "click"
+-- Running test case: DOMDebugger.Event.Listener.RemoveBreakpoint "click"
 Adding "click" Event Breakpoint...
 Removing "click" Event Breakpoint...
 Firing "click" on body...
 PASS: Should not pause for removed breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.RemoveDisabledBreakpoint "click"
+-- Running test case: DOMDebugger.Event.Listener.RemoveDisabledBreakpoint "click"
 Adding "click" Event Breakpoint...
 Disabling "click" Event Breakpoint...
 Removing "click" Event Breakpoint...
@@ -34,31 +34,31 @@ Firing "click" on body...
 PASS: Should not pause for removed disabled breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.AddBreakpoint "custom"
+-- Running test case: DOMDebugger.Event.Listener.AddBreakpoint "custom"
 Adding "custom" Event Breakpoint...
 Firing "custom" on body...
 PASS: Should pause before event handler is run.
 CALL STACK:
 0: [F] handleBody_custom
-1: [F] bodyFire_custom
+1: [F] trigger_custom
 2: [P] Global Code
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.AddDisabledBreakpoint "custom"
+-- Running test case: DOMDebugger.Event.Listener.AddDisabledBreakpoint "custom"
 Adding "custom" Event Breakpoint...
 Disabling "custom" Event Breakpoint...
 Firing "custom" on body...
 PASS: Should not pause for disabled breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.RemoveBreakpoint "custom"
+-- Running test case: DOMDebugger.Event.Listener.RemoveBreakpoint "custom"
 Adding "custom" Event Breakpoint...
 Removing "custom" Event Breakpoint...
 Firing "custom" on body...
 PASS: Should not pause for removed breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.RemoveDisabledBreakpoint "custom"
+-- Running test case: DOMDebugger.Event.Listener.RemoveDisabledBreakpoint "custom"
 Adding "custom" Event Breakpoint...
 Disabling "custom" Event Breakpoint...
 Removing "custom" Event Breakpoint...
@@ -66,18 +66,18 @@ Firing "custom" on body...
 PASS: Should not pause for removed disabled breakpoint.
 -- Running test teardown.
 
--- Running test case: DOMDebugger.Event.AddMultipleBreakpoints
+-- Running test case: DOMDebugger.Event.Listener.AddMultipleBreakpoints
 Adding "click" Event Breakpoint...
 Firing "click" on div#x...
 PASS: Should pause before event handler is run.
 CALL STACK:
 0: [F] handleX_click
-1: [F] xFire_click
+1: [F] clickX
 2: [P] Global Code
 PASS: Should pause before event handler is run.
 CALL STACK:
 0: [F] handleBody_click
-1: [F] xFire_click
+1: [F] clickX
 2: [P] Global Code
 -- Running test teardown.
 
diff --git a/LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html b/LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html
new file mode 100644 (file)
index 0000000..f2fd5cd
--- /dev/null
@@ -0,0 +1,161 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="../debugger/resources/log-active-stack-trace.js"></script>
+<script src="resources/event-breakpoint-utilities.js"></script>
+<script>
+function handleBody_click(event) {
+    TestPage.dispatchEventToFrontend("TestPage-click");
+}
+
+function handleBody_custom(event) {
+    TestPage.dispatchEventToFrontend("TestPage-custom");
+}
+
+function handleX_click(event) {
+}
+
+function trigger_click() {
+    document.body.click();
+}
+
+function trigger_custom() {
+    document.body.dispatchEvent(new Event("custom"));
+}
+
+function clickX() {
+    document.getElementById("x").click();
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("DOMDebugger.Event.Listener");
+
+    function addTestCasesForEventName(eventName) {
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Listener.AddBreakpoint "${eventName}"`,
+            description: "Check that the debugger pauses for enabled breakpoints.",
+            test(resolve, reject) {
+                let paused = false;
+
+                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                    paused = true;
+
+                    InspectorTest.pass("Should pause before event handler is run.");
+                    logActiveStackTrace();
+
+                    WI.debuggerManager.resume()
+                    .catch(reject);
+                });
+
+                InspectorTest.singleFireEventListener(`TestPage-${eventName}`, (event) => {
+                    if (!paused) {
+                        WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                        InspectorTest.fail("Should pause before event handler is run.");
+                    }
+
+                    resolve();
+                });
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Listener, eventName)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("body", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Listener.AddDisabledBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for disabled breakpoints.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.EventListener, eventName, "Should not pause for disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Listener, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("body", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Listener.RemoveBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for removed breakpoint.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.EventListener, eventName, "Should not pause for removed breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Listener, eventName)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("body", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Listener.RemoveDisabledBreakpoint "${eventName}"`,
+            description: "Check that a disabled breakpoint can be removed.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.EventListener, eventName, "Should not pause for removed disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Listener, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("body", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+    }
+
+    addTestCasesForEventName("click");
+    addTestCasesForEventName("custom");
+
+    suite.addTestCase({
+        name: `DOMDebugger.Event.Listener.AddMultipleBreakpoints`,
+        description: "Check that a single breakpoint pauses for every event of that type.",
+        test(resolve, reject) {
+            let pauseCount = 0;
+
+            let listener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                ++pauseCount;
+
+                InspectorTest.pass("Should pause before event handler is run.");
+                logActiveStackTrace();
+
+                WI.debuggerManager.resume()
+                .catch(reject);
+
+                if (pauseCount >= 2) {
+                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                    resolve();
+                }
+            });
+
+            InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Listener, "click")
+            .then(() => {
+                InspectorTest.log("Firing \"click\" on div#x...");
+                return InspectorTest.evaluateInPage(`clickX()`);
+            })
+            .catch(reject);
+        },
+        teardown: InspectorTest.EventBreakpoint.teardown,
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Tests for Event Listener breakpoints.</p>
+    <div id="x"></div>
+    <script>
+        document.body.addEventListener("click", handleBody_click);
+        document.body.addEventListener("custom", handleBody_custom);
+
+        document.getElementById("x").addEventListener("click", handleX_click);
+    </script>
+</body>
+</html>
diff --git a/LayoutTests/inspector/dom-debugger/event-timer-breakpoints-expected.txt b/LayoutTests/inspector/dom-debugger/event-timer-breakpoints-expected.txt
new file mode 100644 (file)
index 0000000..78dc413
--- /dev/null
@@ -0,0 +1,91 @@
+Tests for Event Timer breakpoints.
+
+
+== Running test suite: DOMDebugger.Event.Timer
+-- Running test case: DOMDebugger.Event.Timer.AddBreakpoint "setTimeout"
+Adding "setTimeout" Event Breakpoint...
+Firing "setTimeout" on window...
+PASS: Should pause before event handler is run.
+CALL STACK:
+0: [F] handleWindow_setTimeout
+ASYNC CALL STACK:
+1: --- setTimeout ---
+2: [F] trigger_setTimeout
+3: [P] Global Code
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.AddDisabledBreakpoint "setTimeout"
+Adding "setTimeout" Event Breakpoint...
+Disabling "setTimeout" Event Breakpoint...
+Firing "setTimeout" on window...
+PASS: Should not pause for disabled breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.RemoveBreakpoint "setTimeout"
+Adding "setTimeout" Event Breakpoint...
+Removing "setTimeout" Event Breakpoint...
+Firing "setTimeout" on window...
+PASS: Should not pause for removed breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.RemoveDisabledBreakpoint "setTimeout"
+Adding "setTimeout" Event Breakpoint...
+Disabling "setTimeout" Event Breakpoint...
+Removing "setTimeout" Event Breakpoint...
+Firing "setTimeout" on window...
+PASS: Should not pause for removed disabled breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.AddBreakpoint "setInterval"
+Adding "setInterval" Event Breakpoint...
+Firing "setInterval" on window...
+PASS: Should pause before event handler is run.
+CALL STACK:
+0: [F] handleWindow_setInterval
+ASYNC CALL STACK:
+1: --- setInterval ---
+2: [F] trigger_setInterval
+3: [P] Global Code
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.AddDisabledBreakpoint "setInterval"
+Adding "setInterval" Event Breakpoint...
+Disabling "setInterval" Event Breakpoint...
+Firing "setInterval" on window...
+PASS: Should not pause for disabled breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.RemoveBreakpoint "setInterval"
+Adding "setInterval" Event Breakpoint...
+Removing "setInterval" Event Breakpoint...
+Firing "setInterval" on window...
+PASS: Should not pause for removed breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Timer.RemoveDisabledBreakpoint "setInterval"
+Adding "setInterval" Event Breakpoint...
+Disabling "setInterval" Event Breakpoint...
+Removing "setInterval" Event Breakpoint...
+Firing "setInterval" on window...
+PASS: Should not pause for removed disabled breakpoint.
+-- Running test teardown.
+
+-- Running test case: DOMDebugger.Event.Listener.RepeatFireBreakpoint
+Adding "setInterval" Event Breakpoint...
+Firing "setInterval" on window...
+PASS: Should pause before event handler is run.
+CALL STACK:
+0: [F] handleRepeat
+ASYNC CALL STACK:
+1: --- setInterval ---
+2: [F] repeatSetInterval
+3: [P] Global Code
+PASS: Should pause before event handler is run.
+CALL STACK:
+0: [F] handleRepeat
+ASYNC CALL STACK:
+1: --- setInterval ---
+2: [F] repeatSetInterval
+3: [P] Global Code
+-- Running test teardown.
+
diff --git a/LayoutTests/inspector/dom-debugger/event-timer-breakpoints.html b/LayoutTests/inspector/dom-debugger/event-timer-breakpoints.html
new file mode 100644 (file)
index 0000000..1c3f6cf
--- /dev/null
@@ -0,0 +1,165 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="../debugger/resources/log-active-stack-trace.js"></script>
+<script src="resources/event-breakpoint-utilities.js"></script>
+<script>
+
+let intervalID = NaN;
+
+function handleWindow_setTimeout() {
+    TestPage.dispatchEventToFrontend("TestPage-setTimeout");
+}
+
+function handleWindow_setInterval() {
+    TestPage.dispatchEventToFrontend("TestPage-setInterval");
+
+    repeatClearInterval();
+}
+
+function handleRepeat() {
+}
+
+function trigger_setTimeout() {
+    setTimeout(handleWindow_setTimeout, 100);
+}
+
+function trigger_setInterval() {
+    intervalID = setInterval(handleWindow_setInterval, 100);
+}
+
+function repeatSetInterval() {
+    intervalID = setInterval(handleRepeat, 100);
+}
+
+function repeatClearInterval() {
+    clearInterval(intervalID);
+    intervalID = NaN;
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("DOMDebugger.Event.Timer");
+
+    function addTestCasesForEventName(eventName) {
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Timer.AddBreakpoint "${eventName}"`,
+            description: "Check that the debugger pauses for enabled breakpoints.",
+            test(resolve, reject) {
+                let paused = false;
+
+                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                    paused = true;
+
+                    InspectorTest.pass("Should pause before event handler is run.");
+                    logActiveStackTrace();
+
+                    WI.debuggerManager.resume()
+                    .catch(reject);
+                });
+
+                InspectorTest.singleFireEventListener(`TestPage-${eventName}`, (event) => {
+                    if (!paused) {
+                        WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                        InspectorTest.fail("Should pause before event handler is run.");
+                    }
+
+                    resolve();
+                });
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Timer, eventName)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Timer.AddDisabledBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for disabled breakpoints.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.Timer, eventName, "Should not pause for disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Timer, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Timer.RemoveBreakpoint "${eventName}"`,
+            description: "Check that debugger does not pause for removed breakpoint.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.Timer, eventName, "Should not pause for removed breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Timer, eventName)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+
+        suite.addTestCase({
+            name: `DOMDebugger.Event.Timer.RemoveDisabledBreakpoint "${eventName}"`,
+            description: "Check that a disabled breakpoint can be removed.",
+            test(resolve, reject) {
+                InspectorTest.EventBreakpoint.failOnPause(resolve, reject, WI.DebuggerManager.PauseReason.Timer, eventName, "Should not pause for removed disabled breakpoint.");
+
+                InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Timer, eventName)
+                .then(InspectorTest.EventBreakpoint.disableBreakpoint)
+                .then(InspectorTest.EventBreakpoint.removeBreakpoint)
+                .then(InspectorTest.EventBreakpoint.awaitEvent("window", eventName))
+                .catch(reject);
+            },
+            teardown: InspectorTest.EventBreakpoint.teardown,
+        });
+    }
+
+    addTestCasesForEventName("setTimeout");
+    addTestCasesForEventName("setInterval");
+
+    suite.addTestCase({
+        name: `DOMDebugger.Event.Listener.RepeatFireBreakpoint`,
+        description: "Check that a single breakpoint pauses multiple times for the same interval.",
+        test(resolve, reject) {
+            let pauseCount = 0;
+
+            let listener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                ++pauseCount;
+
+                InspectorTest.pass("Should pause before event handler is run.");
+                logActiveStackTrace();
+
+                WI.debuggerManager.resume()
+                .catch(reject);
+
+                if (pauseCount >= 2) {
+                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                    InspectorTest.evaluateInPage(`repeatClearInterval()`)
+                    .then(resolve, reject)
+                }
+            });
+
+            InspectorTest.EventBreakpoint.addBreakpoint(WI.EventBreakpoint.Type.Timer, "setInterval")
+            .then(() => {
+                InspectorTest.log("Firing \"setInterval\" on window...");
+                return InspectorTest.evaluateInPage(`repeatSetInterval()`);
+            })
+            .catch(reject);
+        },
+        teardown: InspectorTest.EventBreakpoint.teardown,
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Tests for Event Timer breakpoints.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/dom-debugger/resources/event-breakpoint-utilities.js b/LayoutTests/inspector/dom-debugger/resources/event-breakpoint-utilities.js
new file mode 100644 (file)
index 0000000..9e84d47
--- /dev/null
@@ -0,0 +1,86 @@
+TestPage.registerInitializer(() => {
+    InspectorTest.EventBreakpoint = {};
+
+    InspectorTest.EventBreakpoint.teardown = function(resolve, reject) {
+        let breakpoints = WI.domDebuggerManager.eventBreakpoints;
+        for (let breakpoint of breakpoints)
+            WI.domDebuggerManager.removeEventBreakpoint(breakpoint);
+
+        resolve();
+    };
+
+    InspectorTest.EventBreakpoint.failOnPause = function(resolve, reject, pauseReason, eventName, message) {
+        let paused = false;
+
+        let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+            paused = true;
+
+            let targetData = WI.debuggerManager.dataForTarget(WI.debuggerManager.activeCallFrame.target);
+            InspectorTest.assert(targetData.pauseReason === pauseReason, `Pause reason should be "${pauseReason}".`);
+            InspectorTest.assert(targetData.pauseData.eventName === eventName, `Pause data eventName should be "${eventName}".`);
+
+            InspectorTest.fail(message);
+            logActiveStackTrace();
+
+            WI.debuggerManager.resume()
+            .catch(reject);
+        });
+
+        InspectorTest.singleFireEventListener("TestPage-" + eventName, (event) => {
+            if (!paused) {
+                WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+
+                InspectorTest.pass(message);
+            }
+
+            resolve();
+        });
+    };
+
+    InspectorTest.EventBreakpoint.addBreakpoint = function(type, eventName) {
+        InspectorTest.log(`Adding "${eventName}" Event Breakpoint...`);
+
+        return new Promise((resolve, reject) => {
+            let breakpoint = new WI.EventBreakpoint(type, eventName);
+
+            WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventBreakpointAdded)
+            .then((event) => {
+                InspectorTest.assert(event.data.breakpoint.type === type, `Breakpoint should be for expected type "${type}".`);
+                InspectorTest.assert(event.data.breakpoint.eventName === eventName, `Breakpoint should be for expected event name "${eventName}".`);
+                InspectorTest.assert(!event.data.breakpoint.disabled, "Breakpoint should not be disabled initially.");
+                resolve(breakpoint);
+            });
+
+            WI.domDebuggerManager.addEventBreakpoint(breakpoint);
+        });
+    };
+
+    InspectorTest.EventBreakpoint.removeBreakpoint = function(breakpoint) {
+        InspectorTest.log(`Removing "${breakpoint.eventName}" Event Breakpoint...`);
+
+        return new Promise((resolve, reject) => {
+            WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventBreakpointRemoved)
+            .then((event) => {
+                InspectorTest.assert(event.data.breakpoint === breakpoint, "Removed Breakpoint should be expected object.");
+                InspectorTest.assert(!WI.domDebuggerManager.eventBreakpoints.includes(breakpoint), "Breakpoint should not be in the list of breakpoints.");
+                resolve(breakpoint);
+            });
+
+            WI.domDebuggerManager.removeEventBreakpoint(breakpoint);
+        });
+    };
+
+    InspectorTest.EventBreakpoint.disableBreakpoint = function(breakpoint) {
+        InspectorTest.log(`Disabling "${breakpoint.eventName}" Event Breakpoint...`);
+
+        breakpoint.disabled = true;
+        return breakpoint;
+    };
+
+    InspectorTest.EventBreakpoint.awaitEvent = function(context, eventName) {
+        return function() {
+            InspectorTest.log(`Firing "${eventName}" on ${context}...`);
+            return InspectorTest.evaluateInPage(`trigger_${eventName}()`);
+        };
+    };
+});
index a534076..ebde848 100644 (file)
@@ -1,3 +1,36 @@
+2018-08-23  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: support breakpoints for timers and animation-frame events
+        https://bugs.webkit.org/show_bug.cgi?id=188778
+
+        Reviewed by Brian Burg.
+
+        * inspector/protocol/Debugger.json:
+        Add `AnimationFrame` and `Timer` types to the list of pause reasons.
+
+        * inspector/protocol/DOMDebugger.json:
+        Introduced `setEventBreakpoint` and `removeEventBreakpoint` to replace the more specific:
+         - `setEventListenerBreakpoint`
+         - `removeEventListenerBreakpoint`
+         - `setInstrumentationBreakpoint`
+         - `removeInstrumentationBreakpoint`
+        Also created an `EventBreakpointType` to enumerate the available types of event breakpoints.
+
+        * inspector/scripts/codegen/generate_cpp_protocol_types_header.py:
+        (CppProtocolTypesHeaderGenerator.generate_output):
+        (CppProtocolTypesHeaderGenerator._generate_forward_declarations_for_binding_traits):
+        (CppProtocolTypesHeaderGenerator._generate_declarations_for_enum_conversion_methods):
+        (CppProtocolTypesHeaderGenerator._generate_hash_declarations): Added.
+        Generate `DefaultHash` for all `enum class` used by inspector protocols.
+
+        * inspector/scripts/tests/generic/expected/commands-with-async-attribute.json-result:
+        * inspector/scripts/tests/generic/expected/commands-with-optional-call-return-parameters.json-result:
+        * inspector/scripts/tests/generic/expected/enum-values.json-result:
+        * inspector/scripts/tests/generic/expected/type-declaration-array-type.json-result:
+        * inspector/scripts/tests/generic/expected/type-declaration-enum-type.json-result:
+        * inspector/scripts/tests/generic/expected/type-declaration-object-type.json-result:
+        * inspector/scripts/tests/generic/expected/type-requiring-runtime-casts.json-result:
+
 2018-08-23  Michael Saboff  <msaboff@apple.com>
 
         YARR: Need to JIT compile a RegExp before using containsNestedSubpatterns flag
index 04c1761..b4204d1 100644 (file)
@@ -8,6 +8,12 @@
             "type": "string",
             "enum": ["subtree-modified", "attribute-modified", "node-removed"],
             "description": "DOM breakpoint type."
+        },
+        {
+            "id": "EventBreakpointType",
+            "type": "string",
+            "enum": ["animation-frame", "listener", "timer"],
+            "description": "Event breakpoint type."
         }
     ],
     "commands": [
             ]
         },
         {
-            "name": "setEventListenerBreakpoint",
-            "description": "Sets breakpoint on particular DOM event.",
-            "parameters": [
-                { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." }
-            ]
-        },
-        {
-            "name": "removeEventListenerBreakpoint",
-            "description": "Removes breakpoint on particular DOM event.",
-            "parameters": [
-                { "name": "eventName", "type": "string", "description": "Event name." }
-            ]
-        },
-        {
-            "name": "setInstrumentationBreakpoint",
-            "description": "Sets breakpoint on particular native event.",
+            "name": "setEventBreakpoint",
+            "description": "Sets breakpoint on particular event of given type.",
             "parameters": [
-                { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                { "name": "breakpointType", "$ref": "EventBreakpointType" },
+                { "name": "eventName", "type": "string", "description": "The name of the event to stop on." }
             ]
         },
         {
-            "name": "removeInstrumentationBreakpoint",
-            "description": "Sets breakpoint on particular native event.",
+            "name": "removeEventBreakpoint",
+            "description": "Removes breakpoint on particular event of given type.",
             "parameters": [
-                { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." }
+                { "name": "breakpointType", "$ref": "EventBreakpointType" },
+                { "name": "eventName", "type": "string", "description": "The name of the event to stop on." }
             ]
         },
         {
index 045b91f..3143141 100644 (file)
             "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.",
             "parameters": [
                 { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
-                { "name": "reason", "type": "string", "enum": ["XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "DebuggerStatement", "Breakpoint", "PauseOnNextStatement", "other"], "description": "Pause reason." },
+                { "name": "reason", "type": "string", "enum": ["XHR", "DOM", "AnimationFrame", "EventListener", "Timer", "exception", "assert", "CSPViolation", "DebuggerStatement", "Breakpoint", "PauseOnNextStatement", "other"], "description": "Pause reason." },
                 { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." },
                 { "name": "asyncStackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Linked list of asynchronous StackTraces." }
             ]
index a418e51..7548d63 100755 (executable)
@@ -64,10 +64,11 @@ class CppProtocolTypesHeaderGenerator(CppGenerator):
         sections.extend(self._generate_enum_constant_value_conversion_methods())
         builder_sections = map(self._generate_builders_for_domain, domains)
         sections.extend(filter(lambda section: len(section) > 0, builder_sections))
-        sections.append(self._generate_forward_declarations_for_binding_traits())
-        sections.extend(self._generate_declarations_for_enum_conversion_methods())
+        sections.append(self._generate_forward_declarations_for_binding_traits(domains))
+        sections.extend(self._generate_declarations_for_enum_conversion_methods(domains))
         sections.append('} // namespace Protocol')
         sections.append(Template(CppTemplates.HeaderPostlude).substitute(None, **header_args))
+        sections.extend(self._generate_hash_declarations(domains))
         return "\n\n".join(sections)
 
     # Private methods.
@@ -337,11 +338,11 @@ class CppProtocolTypesHeaderGenerator(CppGenerator):
         lines.append('    }')
         return '\n'.join(lines)
 
-    def _generate_forward_declarations_for_binding_traits(self):
+    def _generate_forward_declarations_for_binding_traits(self, domains):
         # A list of (builder_type, needs_runtime_cast)
         type_arguments = []
 
-        for domain in self.domains_to_generate():
+        for domain in domains:
             type_declarations = self.type_declarations_for_domain(domain)
             declarations_to_generate = filter(lambda decl: self.type_needs_shape_assertions(decl.type), type_declarations)
 
@@ -369,7 +370,7 @@ class CppProtocolTypesHeaderGenerator(CppGenerator):
             lines.append('};')
         return '\n'.join(lines)
 
-    def _generate_declarations_for_enum_conversion_methods(self):
+    def _generate_declarations_for_enum_conversion_methods(self, domains):
         sections = []
         sections.append('\n'.join([
             'namespace %s {' % self.helpers_namespace(),
@@ -389,7 +390,7 @@ class CppProtocolTypesHeaderGenerator(CppGenerator):
         def type_member_is_anonymous_enum_type(type_member):
             return isinstance(type_member.type, EnumType) and type_member.type.is_anonymous
 
-        for domain in self.domains_to_generate():
+        for domain in domains:
             type_declarations = self.type_declarations_for_domain(domain)
             declaration_types = [decl.type for decl in type_declarations]
             object_types = filter(lambda _type: isinstance(_type, ObjectType), declaration_types)
@@ -424,3 +425,35 @@ class CppProtocolTypesHeaderGenerator(CppGenerator):
         sections.append('} // namespace %s' % self.helpers_namespace())
 
         return ['\n\n'.join(sections)]
+
+    def _generate_hash_declarations(self, domains):
+        lines = []
+
+        for domain in domains:
+            type_declarations = self.type_declarations_for_domain(domain)
+            declaration_types = [decl.type for decl in type_declarations]
+            enum_types = filter(lambda _type: isinstance(_type, EnumType), declaration_types)
+
+            if len(enum_types) == 0:
+                continue
+
+            if len(lines) == 0:
+                lines.append('namespace WTF {')
+                lines.append('')
+                lines.append('template<typename T> struct DefaultHash;')
+
+            lines.append('')
+            lines.append("// Hash declarations in the '%s' Domain" % domain.domain_name)
+
+            for enum_type in enum_types:
+                lines.append('template<>')
+                lines.append('struct DefaultHash<Inspector::Protocol::%s::%s> {' % (domain.domain_name, enum_type.raw_name()))
+                lines.append('    typedef IntHash<Inspector::Protocol::%s::%s> Hash;' % (domain.domain_name, enum_type.raw_name()))
+                lines.append('};')
+
+        if len(lines) == 0:
+            return []
+
+        lines.append('')
+        lines.append('} // namespace WTF')
+        return ['\n'.join(lines)]
index b417e67..9a4d977 100644 (file)
@@ -720,6 +720,18 @@ std::optional<Inspector::Protocol::Database::PrimaryColors> parseEnumValueFromSt
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Database' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Database::PrimaryColors> {
+    typedef IntHash<Inspector::Protocol::Database::PrimaryColors> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 7cc8015..3ebce2b 100644 (file)
@@ -635,6 +635,18 @@ std::optional<Inspector::Protocol::Database::PrimaryColors> parseEnumValueFromSt
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Database' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Database::PrimaryColors> {
+    typedef IntHash<Inspector::Protocol::Database::PrimaryColors> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 6f6b2c6..a00de43 100644 (file)
@@ -483,6 +483,18 @@ std::optional<Inspector::Protocol::TypeDomain::TypeDomainEnum> parseEnumValueFro
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'TypeDomain' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::TypeDomain::TypeDomainEnum> {
+    typedef IntHash<Inspector::Protocol::TypeDomain::TypeDomainEnum> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 0f4c310..87f9d4f 100644 (file)
@@ -375,6 +375,18 @@ std::optional<Inspector::Protocol::Debugger::Reason> parseEnumValueFromString<In
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Debugger' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Debugger::Reason> {
+    typedef IntHash<Inspector::Protocol::Debugger::Reason> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 6cca537..f667dd2 100644 (file)
@@ -374,6 +374,22 @@ std::optional<Inspector::Protocol::Runtime::TwoLeggedAnimals> parseEnumValueFrom
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Runtime' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Runtime::FarmAnimals> {
+    typedef IntHash<Inspector::Protocol::Runtime::FarmAnimals> Hash;
+};
+template<>
+struct DefaultHash<Inspector::Protocol::Runtime::TwoLeggedAnimals> {
+    typedef IntHash<Inspector::Protocol::Runtime::TwoLeggedAnimals> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 7ed5ad8..ac7adc7 100644 (file)
@@ -897,6 +897,18 @@ std::optional<Inspector::Protocol::Test::ParameterBundle::Directionality> parseE
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Database' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Database::MouseButton> {
+    typedef IntHash<Inspector::Protocol::Database::MouseButton> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index c578923..76f8842 100644 (file)
@@ -588,6 +588,22 @@ std::optional<Inspector::Protocol::Test::CastedAnimals> parseEnumValueFromString
 } // namespace Protocol
 
 } // namespace Inspector
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+
+// Hash declarations in the 'Test' Domain
+template<>
+struct DefaultHash<Inspector::Protocol::Test::UncastedAnimals> {
+    typedef IntHash<Inspector::Protocol::Test::UncastedAnimals> Hash;
+};
+template<>
+struct DefaultHash<Inspector::Protocol::Test::CastedAnimals> {
+    typedef IntHash<Inspector::Protocol::Test::CastedAnimals> Hash;
+};
+
+} // namespace WTF
 ### End File: TestProtocolObjects.h
 
 ### Begin File: TestProtocolObjects.cpp
index 802bcdf..93852d1 100644 (file)
@@ -1,3 +1,51 @@
+2018-08-23  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: support breakpoints for timers and animation-frame events
+        https://bugs.webkit.org/show_bug.cgi?id=188778
+
+        Reviewed by Brian Burg.
+
+        The original implementation of "instrumentation" breakpoints relied upon the frontend
+        sending somewhat arbitrary strings when enabling breakpoints for specific events. As an
+        example, setting a breakpoint for `requestAnimationFrame` expects `"animationFrameFired"`
+        as the string, which doesn't make much sense. This patch removes the usage of these strings
+        and instead expects the agent to implement a method that matches what is happening.
+
+        Tests: inspector/dom-debugger/event-animation-frame-breakpoints.html
+               inspector/dom-debugger/event-listener-breakpoints.html
+               inspector/dom-debugger/event-timer-breakpoints.html
+
+        * inspector/InspectorInstrumentation.h:
+        (WebCore::InspectorInstrumentation::willFireTimer):
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::didInstallTimerImpl):
+        (WebCore::InspectorInstrumentation::didRemoveTimerImpl):
+        (WebCore::InspectorInstrumentation::willFireTimerImpl):
+        (WebCore::InspectorInstrumentation::didRequestAnimationFrameImpl):
+        (WebCore::InspectorInstrumentation::didCancelAnimationFrameImpl):
+        (WebCore::InspectorInstrumentation::willFireAnimationFrameImpl):
+        (WebCore::InspectorInstrumentation::pauseOnNativeEventIfNeeded): Deleted.
+
+        * inspector/agents/InspectorDOMDebuggerAgent.h:
+        * inspector/agents/InspectorDOMDebuggerAgent.cpp:
+        (WebCore::InspectorDOMDebuggerAgent::setEventBreakpoint): Added.
+        (WebCore::InspectorDOMDebuggerAgent::removeEventBreakpoint): Added.
+        (WebCore::InspectorDOMDebuggerAgent::willHandleEvent):
+        (WebCore::InspectorDOMDebuggerAgent::willFireTimer): Added.
+        (WebCore::InspectorDOMDebuggerAgent::willFireAnimationFrame): Added.
+        (WebCore::InspectorDOMDebuggerAgent::setEventListenerBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::setInstrumentationBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::setBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::removeEventListenerBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::removeBreakpoint): Deleted.
+        (WebCore::InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded): Deleted.
+        Unify the event listener and instrumentation breakpoint commands into a single method,
+        `setEventBreakpoint`, that takes in both an `EventBreakpointType` and `eventName`.
+
+        * page/DOMTimer.cpp:
+        (WebCore::DOMTimer::fired):
+
 2018-08-23  Aditya Keerthi  <akeerthi@apple.com>
 
         [iOS] Support the inputmode attribute on contenteditable elements
index 41b7877..7f7f126 100644 (file)
@@ -76,13 +76,6 @@ namespace WebCore {
 
 using namespace Inspector;
 
-static const char* const requestAnimationFrameEventName = "requestAnimationFrame";
-static const char* const cancelAnimationFrameEventName = "cancelAnimationFrame";
-static const char* const animationFrameFiredEventName = "animationFrameFired";
-static const char* const setTimerEventName = "setTimer";
-static const char* const clearTimerEventName = "clearTimer";
-static const char* const timerFiredEventName = "timerFired";
-
 namespace {
 static HashSet<InstrumentingAgents*>* s_instrumentingAgentsSet = nullptr;
 }
@@ -294,8 +287,6 @@ void InspectorInstrumentation::willSendXMLHttpRequestImpl(InstrumentingAgents& i
 
 void InspectorInstrumentation::didInstallTimerImpl(InstrumentingAgents& instrumentingAgents, int timerId, Seconds timeout, bool singleShot, ScriptExecutionContext& context)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, setTimerEventName, true);
-
     if (InspectorDebuggerAgent* debuggerAgent = instrumentingAgents.inspectorDebuggerAgent())
         debuggerAgent->didScheduleAsyncCall(context.execState(), InspectorDebuggerAgent::AsyncCallType::DOMTimer, timerId, singleShot);
 
@@ -305,8 +296,6 @@ void InspectorInstrumentation::didInstallTimerImpl(InstrumentingAgents& instrume
 
 void InspectorInstrumentation::didRemoveTimerImpl(InstrumentingAgents& instrumentingAgents, int timerId, ScriptExecutionContext& context)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, clearTimerEventName, true);
-
     if (InspectorDebuggerAgent* debuggerAgent = instrumentingAgents.inspectorDebuggerAgent())
         debuggerAgent->didCancelAsyncCall(InspectorDebuggerAgent::AsyncCallType::DOMTimer, timerId);
     if (InspectorTimelineAgent* timelineAgent = instrumentingAgents.inspectorTimelineAgent())
@@ -443,13 +432,14 @@ void InspectorInstrumentation::didEvaluateScriptImpl(const InspectorInstrumentat
         timelineAgent->didEvaluateScript(frame);
 }
 
-InspectorInstrumentationCookie InspectorInstrumentation::willFireTimerImpl(InstrumentingAgents& instrumentingAgents, int timerId, ScriptExecutionContext& context)
+InspectorInstrumentationCookie InspectorInstrumentation::willFireTimerImpl(InstrumentingAgents& instrumentingAgents, int timerId, bool oneShot, ScriptExecutionContext& context)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, timerFiredEventName, false);
-
     if (InspectorDebuggerAgent* debuggerAgent = instrumentingAgents.inspectorDebuggerAgent())
         debuggerAgent->willDispatchAsyncCall(InspectorDebuggerAgent::AsyncCallType::DOMTimer, timerId);
 
+    if (InspectorDOMDebuggerAgent* domDebuggerAgent = instrumentingAgents.inspectorDOMDebuggerAgent())
+        domDebuggerAgent->willFireTimer(oneShot);
+
     int timelineAgentId = 0;
     if (InspectorTimelineAgent* timelineAgent = instrumentingAgents.inspectorTimelineAgent()) {
         timelineAgent->willFireTimer(timerId, frameForScriptExecutionContext(context));
@@ -1044,16 +1034,8 @@ bool InspectorInstrumentation::timelineAgentEnabled(ScriptExecutionContext* scri
     return instrumentingAgents && instrumentingAgents->inspectorTimelineAgent();
 }
 
-void InspectorInstrumentation::pauseOnNativeEventIfNeeded(InstrumentingAgents& instrumentingAgents, const String& eventName, bool synchronous)
-{
-    if (InspectorDOMDebuggerAgent* domDebuggerAgent = instrumentingAgents.inspectorDOMDebuggerAgent())
-        domDebuggerAgent->pauseOnNativeEventIfNeeded(eventName, synchronous);
-}
-
 void InspectorInstrumentation::didRequestAnimationFrameImpl(InstrumentingAgents& instrumentingAgents, int callbackId, Document& document)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, requestAnimationFrameEventName, true);
-
     if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
         pageDebuggerAgent->didRequestAnimationFrame(callbackId, document);
     if (InspectorTimelineAgent* timelineAgent = instrumentingAgents.inspectorTimelineAgent())
@@ -1062,8 +1044,6 @@ void InspectorInstrumentation::didRequestAnimationFrameImpl(InstrumentingAgents&
 
 void InspectorInstrumentation::didCancelAnimationFrameImpl(InstrumentingAgents& instrumentingAgents, int callbackId, Document& document)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, cancelAnimationFrameEventName, true);
-
     if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
         pageDebuggerAgent->didCancelAnimationFrame(callbackId);
     if (InspectorTimelineAgent* timelineAgent = instrumentingAgents.inspectorTimelineAgent())
@@ -1072,11 +1052,12 @@ void InspectorInstrumentation::didCancelAnimationFrameImpl(InstrumentingAgents&
 
 InspectorInstrumentationCookie InspectorInstrumentation::willFireAnimationFrameImpl(InstrumentingAgents& instrumentingAgents, int callbackId, Document& document)
 {
-    pauseOnNativeEventIfNeeded(instrumentingAgents, animationFrameFiredEventName, false);
-
     if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
         pageDebuggerAgent->willFireAnimationFrame(callbackId);
 
+    if (InspectorDOMDebuggerAgent* domDebuggerAgent = instrumentingAgents.inspectorDOMDebuggerAgent())
+        domDebuggerAgent->willFireAnimationFrame();
+
     int timelineAgentId = 0;
     if (InspectorTimelineAgent* timelineAgent = instrumentingAgents.inspectorTimelineAgent()) {
         timelineAgent->willFireAnimationFrame(callbackId, document.frame());
index 622fc56..dde2122 100644 (file)
@@ -157,7 +157,7 @@ public:
     static void didDispatchEventOnWindow(const InspectorInstrumentationCookie&);
     static InspectorInstrumentationCookie willEvaluateScript(Frame&, const String& url, int lineNumber);
     static void didEvaluateScript(const InspectorInstrumentationCookie&, Frame&);
-    static InspectorInstrumentationCookie willFireTimer(ScriptExecutionContext&, int timerId);
+    static InspectorInstrumentationCookie willFireTimer(ScriptExecutionContext&, int timerId, bool oneShot);
     static void didFireTimer(const InspectorInstrumentationCookie&);
     static void didInvalidateLayout(Frame&);
     static InspectorInstrumentationCookie willLayout(Frame&);
@@ -341,7 +341,7 @@ private:
     static void didDispatchEventOnWindowImpl(const InspectorInstrumentationCookie&);
     static InspectorInstrumentationCookie willEvaluateScriptImpl(InstrumentingAgents&, Frame&, const String& url, int lineNumber);
     static void didEvaluateScriptImpl(const InspectorInstrumentationCookie&, Frame&);
-    static InspectorInstrumentationCookie willFireTimerImpl(InstrumentingAgents&, int timerId, ScriptExecutionContext&);
+    static InspectorInstrumentationCookie willFireTimerImpl(InstrumentingAgents&, int timerId, bool oneShot, ScriptExecutionContext&);
     static void didFireTimerImpl(const InspectorInstrumentationCookie&);
     static void didInvalidateLayoutImpl(InstrumentingAgents&, Frame&);
     static InspectorInstrumentationCookie willLayoutImpl(InstrumentingAgents&, Frame&);
@@ -453,8 +453,6 @@ private:
 
     static InspectorTimelineAgent* retrieveTimelineAgent(const InspectorInstrumentationCookie&);
 
-    static void pauseOnNativeEventIfNeeded(InstrumentingAgents&, const String& eventName, bool synchronous);
-
     WEBCORE_EXPORT static int s_frontendCounter;
 };
 
@@ -807,11 +805,11 @@ inline void InspectorInstrumentation::didEvaluateScript(const InspectorInstrumen
         didEvaluateScriptImpl(cookie, frame);
 }
 
-inline InspectorInstrumentationCookie InspectorInstrumentation::willFireTimer(ScriptExecutionContext& context, int timerId)
+inline InspectorInstrumentationCookie InspectorInstrumentation::willFireTimer(ScriptExecutionContext& context, int timerId, bool oneShot)
 {
     FAST_RETURN_IF_NO_FRONTENDS(InspectorInstrumentationCookie());
     if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(context))
-        return willFireTimerImpl(*instrumentingAgents, timerId, context);
+        return willFireTimerImpl(*instrumentingAgents, timerId, oneShot, context);
     return InspectorInstrumentationCookie();
 }
 
index 8a5098a..dad24f7 100644 (file)
@@ -52,9 +52,6 @@ enum DOMBreakpointType {
     DOMBreakpointTypesCount
 };
 
-static const char eventNameCategoryType[] = "event-name:";
-static const char instrumentationCategoryType[] = "instrumentation:";
-
 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
 const int domBreakpointDerivedTypeShift = 16;
 
@@ -126,44 +123,46 @@ void InspectorDOMDebuggerAgent::discardBindings()
     m_xhrBreakpoints.clear();
 }
 
-void InspectorDOMDebuggerAgent::setEventListenerBreakpoint(ErrorString& error, const String& eventName)
+void InspectorDOMDebuggerAgent::setEventBreakpoint(ErrorString& error, const String& breakpointTypeString, const String& eventName)
 {
-    setBreakpoint(error, eventNameCategoryType + eventName);
-}
+    if (breakpointTypeString.isEmpty()) {
+        error = "Event breakpoint type is empty"_s;
+        return;
+    }
 
-void InspectorDOMDebuggerAgent::setInstrumentationBreakpoint(ErrorString& error, const String& eventName)
-{
-    setBreakpoint(error, instrumentationCategoryType + eventName);
-}
+    auto breakpointType = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::DOMDebugger::EventBreakpointType>(breakpointTypeString);
+    if (!breakpointType) {
+        error = makeString("Unknown event breakpoint type: "_s, breakpointTypeString);
+        return;
+    }
 
-void InspectorDOMDebuggerAgent::setBreakpoint(ErrorString& error, const String& eventName)
-{
     if (eventName.isEmpty()) {
         error = "Event name is empty"_s;
         return;
     }
 
-    m_eventListenerBreakpoints.add(eventName);
+    m_eventBreakpoints.add(std::make_pair(*breakpointType, eventName));
 }
 
-void InspectorDOMDebuggerAgent::removeEventListenerBreakpoint(ErrorString& error, const String& eventName)
+void InspectorDOMDebuggerAgent::removeEventBreakpoint(ErrorString& error, const String& breakpointTypeString, const String& eventName)
 {
-    removeBreakpoint(error, eventNameCategoryType + eventName);
-}
+    if (breakpointTypeString.isEmpty()) {
+        error = "Event breakpoint type is empty"_s;
+        return;
+    }
 
-void InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint(ErrorString& error, const String& eventName)
-{
-    removeBreakpoint(error, instrumentationCategoryType + eventName);
-}
+    auto breakpointType = Inspector::Protocol::InspectorHelpers::parseEnumValueFromString<Inspector::Protocol::DOMDebugger::EventBreakpointType>(breakpointTypeString);
+    if (!breakpointType) {
+        error = makeString("Unknown event breakpoint type: "_s, breakpointTypeString);
+        return;
+    }
 
-void InspectorDOMDebuggerAgent::removeBreakpoint(ErrorString& error, const String& eventName)
-{
     if (eventName.isEmpty()) {
         error = "Event name is empty"_s;
         return;
     }
 
-    m_eventListenerBreakpoints.remove(eventName);
+    m_eventBreakpoints.remove(std::make_pair(*breakpointType, eventName));
 }
 
 void InspectorDOMDebuggerAgent::didInvalidateStyleAttr(Node& node)
@@ -366,7 +365,7 @@ void InspectorDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t ro
 
 void InspectorDOMDebuggerAgent::willHandleEvent(const Event& event, const RegisteredEventListener& registeredEventListener)
 {
-    bool shouldPause = m_debuggerAgent->pauseOnNextStatementEnabled() || m_eventListenerBreakpoints.contains(eventNameCategoryType + event.type());
+    bool shouldPause = m_debuggerAgent->pauseOnNextStatementEnabled() || m_eventBreakpoints.contains(std::make_pair(Inspector::Protocol::DOMDebugger::EventBreakpointType::Listener, event.type()));
 
     if (!shouldPause && m_domAgent)
         shouldPause = m_domAgent->hasBreakpointForEventListener(*event.currentTarget(), event.type(), registeredEventListener.callback(), registeredEventListener.useCapture());
@@ -385,19 +384,28 @@ void InspectorDOMDebuggerAgent::willHandleEvent(const Event& event, const Regist
     m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
 }
 
-void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(const String& eventName, bool synchronous)
+void InspectorDOMDebuggerAgent::willFireTimer(bool oneShot)
 {
-    bool shouldPause = m_debuggerAgent->pauseOnNextStatementEnabled() || m_eventListenerBreakpoints.contains(instrumentationCategoryType + eventName);
+    String eventName = oneShot ? "setTimeout"_s : "setInterval"_s;
+    bool shouldPause = m_debuggerAgent->pauseOnNextStatementEnabled() || m_eventBreakpoints.contains(std::make_pair(Inspector::Protocol::DOMDebugger::EventBreakpointType::Timer, eventName));
     if (!shouldPause)
         return;
 
     Ref<JSON::Object> eventData = JSON::Object::create();
     eventData->setString("eventName"_s, eventName);
+    m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::Timer, WTFMove(eventData));
+}
 
-    if (synchronous)
-        m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
-    else
-        m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
+void InspectorDOMDebuggerAgent::willFireAnimationFrame()
+{
+    String eventName = "requestAnimationFrame"_s;
+    bool shouldPause = m_debuggerAgent->pauseOnNextStatementEnabled() || m_eventBreakpoints.contains(std::make_pair(Inspector::Protocol::DOMDebugger::EventBreakpointType::AnimationFrame, eventName));
+    if (!shouldPause)
+        return;
+
+    Ref<JSON::Object> eventData = JSON::Object::create();
+    eventData->setString("eventName"_s, eventName);
+    m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::AnimationFrame, WTFMove(eventData));
 }
 
 void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString&, const String& url, const bool* optionalIsRegex)
index 0b30631..e3a4e1b 100644 (file)
@@ -59,10 +59,8 @@ public:
     // DOMDebugger API
     void setXHRBreakpoint(ErrorString&, const String& url, const bool* optionalIsRegex) final;
     void removeXHRBreakpoint(ErrorString&, const String& url) final;
-    void setEventListenerBreakpoint(ErrorString&, const String& eventName) final;
-    void removeEventListenerBreakpoint(ErrorString&, const String& eventName) final;
-    void setInstrumentationBreakpoint(ErrorString&, const String& eventName) final;
-    void removeInstrumentationBreakpoint(ErrorString&, const String& eventName) final;
+    void setEventBreakpoint(ErrorString&, const String& breakpointType, const String& eventName) final;
+    void removeEventBreakpoint(ErrorString&, const String& breakpointType, const String& eventName) final;
     void setDOMBreakpoint(ErrorString&, int nodeId, const String& type) final;
     void removeDOMBreakpoint(ErrorString&, int nodeId, const String& type) final;
 
@@ -76,7 +74,8 @@ public:
     void willSendXMLHttpRequest(const String& url);
     void frameDocumentUpdated(Frame&);
     void willHandleEvent(const Event&, const RegisteredEventListener&);
-    void pauseOnNativeEventIfNeeded(const String& eventName, bool synchronous);
+    void willFireTimer(bool oneShot);
+    void willFireAnimationFrame();
     void mainFrameDOMContentLoaded();
 
     void didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) final;
@@ -93,15 +92,18 @@ private:
     void updateSubtreeBreakpoints(Node*, uint32_t rootMask, bool set);
     bool hasBreakpoint(Node*, int type);
     void discardBindings();
-    void setBreakpoint(ErrorString&, const String& eventName);
-    void removeBreakpoint(ErrorString&, const String& eventName);
 
     RefPtr<Inspector::DOMDebuggerBackendDispatcher> m_backendDispatcher;
     InspectorDOMAgent* m_domAgent { nullptr };
     Inspector::InspectorDebuggerAgent* m_debuggerAgent { nullptr };
 
     HashMap<Node*, uint32_t> m_domBreakpoints;
-    HashSet<String> m_eventListenerBreakpoints;
+
+    using EventBreakpointType = Inspector::Protocol::DOMDebugger::EventBreakpointType;
+    HashSet<std::pair<EventBreakpointType, String>,
+        WTF::PairHash<EventBreakpointType, String>,
+        WTF::PairHashTraits<WTF::StrongEnumHashTraits<EventBreakpointType>, WTF::HashTraits<String>>
+    > m_eventBreakpoints;
 
     enum class XHRBreakpointType { Text, RegularExpression };
 
index 021ce79..fd79a1b 100644 (file)
@@ -321,7 +321,7 @@ void DOMTimer::fired()
     // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator.
     m_userGestureTokenToForward = nullptr;
 
-    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId);
+    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId, !repeatInterval());
 
     // Simple case for non-one-shot timers.
     if (isActive()) {
index 646875d..9d64ffe 100644 (file)
@@ -1,5 +1,75 @@
 2018-08-23  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: support breakpoints for timers and animation-frame events
+        https://bugs.webkit.org/show_bug.cgi?id=188778
+
+        Reviewed by Brian Burg.
+
+        Add a `type` to `WI.EventBreakpoint` that matches `DOMDebugger.EventBreakpointType`:
+         - `AnimationFrame` for `requestAnimationFrame`
+         - `Listener` for any named DOM Event
+         - `Timer` for `setTimeout` and `setInterval`
+
+        Modified `WI.EventBreakpointPopover` to provide ways for selecting these other types, which
+        is then passed to `WI.DOMDebuggerManager`, which now calls through to the newly added
+        `DOMDebugger.removeEventBreakpoint` and `DOMDebugger.setEventBreakpoint` that sets
+        breakpoints for all event types.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Images/EventBreakpointAnimationFrame.svg: Added.
+        * UserInterface/Images/EventBreakpointListener.svg: Renamed from Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg.
+        * UserInterface/Images/EventBreakpointTimer.svg: Added.
+
+        * UserInterface/Controllers/DOMDebuggerManager.js:
+        (WI.DOMDebuggerManager.supportsEventBreakpoints): Added.
+        (WI.DOMDebuggerManager.prototype.eventBreakpointForTypeAndEventName): Added.
+        (WI.DOMDebuggerManager.prototype.addEventBreakpoint):
+        (WI.DOMDebuggerManager.prototype.removeEventBreakpoint.breakpointRemoved): Added.
+        (WI.DOMDebuggerManager.prototype.removeEventBreakpoint):
+        (WI.DOMDebuggerManager.prototype._updateEventBreakpoint):
+        (WI.DOMDebuggerManager.prototype.eventBreakpointForEventName): Deleted.
+
+        * UserInterface/Controllers/DOMTreeManager.js:
+        (WI.DOMTreeManager.prototype.setBreakpointForEventListener):
+
+        * UserInterface/Controllers/DebuggerManager.js:
+        (WI.DebuggerManager.prototype._pauseReasonFromPayload):
+
+        * UserInterface/Models/EventBreakpoint.js:
+        (WI.EventBreakpoint):
+        (WI.EventBreakpoint.fromPayload):
+        (WI.EventBreakpoint.prototype.get type): Added.
+        (WI.EventBreakpoint.prototype.get serializableInfo):
+        (WI.EventBreakpoint.prototype.saveIdentityToCookie):
+
+        * UserInterface/Views/DebuggerSidebarPanel.js:
+        (WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):
+        (WI.DebuggerSidebarPanel.prototype.willDismissPopover):
+
+        * UserInterface/Views/EventBreakpointPopover.js:
+        (WI.EventBreakpointPopover):
+        (WI.EventBreakpointPopover.prototype.get breakpoint): Added.
+        (WI.EventBreakpointPopover.prototype.show):
+        (WI.EventBreakpointPopover.prototype.show.createOption): Added.
+        (WI.EventBreakpointPopover.prototype.dismiss): Added.
+        (WI.EventBreakpointPopover.prototype._presentOverTargetElement):
+        (WI.EventBreakpointPopover.prototype._handleTypeSelectChange): Added.
+        (WI.EventBreakpointPopover.prototype.get result): Deleted.
+        (WI.EventBreakpointPopover.prototype.get value): Deleted.
+        * UserInterface/Views/EventBreakpointPopover.css:
+        (.popover .event-breakpoint-content > .event-type): Added.
+        (.popover .event-breakpoint-content > input): Deleted.
+
+        * UserInterface/Views/EventBreakpointTreeElement.js:
+        (WI.EventBreakpointTreeElement):
+        * UserInterface/Views/EventBreakpointTreeElement.css:
+        (.breakpoint.event.animation-frame:not(.breakpoint-paused-icon) .icon): Added.
+        (.breakpoint.event.listener:not(.breakpoint-paused-icon) .icon): Added.
+        (.breakpoint.event.timer:not(.breakpoint-paused-icon) .icon): Added.
+        (.breakpoint.event:not(.breakpoint-paused-icon) .icon): Deleted.
+
+2018-08-23  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: `console.inspect(sessionStorage)` first time does not show Session Storage content view if Storage tab was previously unvisited
         https://bugs.webkit.org/show_bug.cgi?id=188801
 
index fa9c15d..024b3f2 100644 (file)
@@ -41,6 +41,7 @@ localizedStrings["%s (%s, %s)"] = "%s (%s, %s)";
 localizedStrings["%s (default)"] = "%s (default)";
 localizedStrings["%s (hidden)"] = "%s (hidden)";
 localizedStrings["%s Event Dispatched"] = "%s Event Dispatched";
+localizedStrings["%s Fired"] = "%s Fired";
 localizedStrings["%s Prototype"] = "%s Prototype";
 localizedStrings["%s \u2013 %s"] = "%s \u2013 %s";
 localizedStrings["%s \u2014 %s"] = "%s \u2014 %s";
@@ -146,7 +147,6 @@ localizedStrings["Boundary"] = "Boundary";
 localizedStrings["Box Model"] = "Box Model";
 localizedStrings["Box Shadow"] = "Box Shadow";
 localizedStrings["Break on events with name:"] = "Break on events with name:";
-localizedStrings["Break on listeners for event with name:"] = "Break on listeners for event with name:";
 localizedStrings["Break on request with URL:"] = "Break on request with URL:";
 localizedStrings["Break on…"] = "Break on…";
 localizedStrings["Breakdown"] = "Breakdown";
@@ -274,6 +274,7 @@ localizedStrings["Custom"] = "Custom";
 localizedStrings["DNS"] = "DNS";
 localizedStrings["DOM Breakpoints"] = "DOM Breakpoints";
 localizedStrings["DOM Content Loaded \u2014 %s"] = "DOM Content Loaded \u2014 %s";
+localizedStrings["DOM Event"] = "DOM Event";
 localizedStrings["Damping"] = "Damping";
 localizedStrings["Dash Array"] = "Dash Array";
 localizedStrings["Data"] = "Data";
@@ -403,6 +404,7 @@ localizedStrings["Event Breakpoints"] = "Event Breakpoints";
 localizedStrings["Event Dispatched"] = "Event Dispatched";
 localizedStrings["Event Listeners"] = "Event Listeners";
 localizedStrings["Events"] = "Events";
+localizedStrings["Example: “%s”"] = "Example: “%s”";
 localizedStrings["Exception with thrown value: %s"] = "Exception with thrown value: %s";
 localizedStrings["Expand All"] = "Expand All";
 localizedStrings["Expand columns"] = "Expand columns";
index cbabcab..7dc12ee 100644 (file)
@@ -78,6 +78,13 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
         }
     }
 
+    // Static
+
+    static supportsEventBreakpoints()
+    {
+        return DOMDebuggerAgent.setEventBreakpoint && DOMDebuggerAgent.removeEventBreakpoint;
+    }
+
     // Public
 
     get supported()
@@ -193,9 +200,9 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
         this._saveDOMBreakpoints();
     }
 
-    eventBreakpointForEventName(eventName)
+    eventBreakpointForTypeAndEventName(type, eventName)
     {
-        return this._eventBreakpoints.find((breakpoint) => breakpoint.eventName === eventName) || null;
+        return this._eventBreakpoints.find((breakpoint) => breakpoint.type === type && breakpoint.eventName === eventName) || null;
     }
 
     addEventBreakpoint(breakpoint)
@@ -204,7 +211,7 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
         if (!breakpoint)
             return;
 
-        if (this._eventBreakpoints.some((item) => item.eventName === breakpoint.eventName))
+        if (this.eventBreakpointForTypeAndEventName(breakpoint.type, breakpoint.eventName))
             return;
 
         this._eventBreakpoints.push(breakpoint);
@@ -232,10 +239,19 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
         if (breakpoint.disabled)
             return;
 
-        DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, (error) => {
+        function breakpointRemoved(error) {
             if (error)
                 console.error(error);
-        });
+        }
+
+        // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
+        if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
+            console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
+            DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, breakpointRemoved);
+            return;
+        }
+
+        DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName, breakpointRemoved);
     }
 
     xhrBreakpointForURL(url)
@@ -415,10 +431,20 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object
                 callback(error);
         }
 
+        // Compatibility (iOS 12): DOMDebuggerAgent.removeEventBreakpoint did not exist.
+        if (!WI.DOMDebuggerManager.supportsEventBreakpoints()) {
+            console.assert(breakpoint.type === WI.EventBreakpoint.Type.Listener);
+            if (breakpoint.disabled)
+                DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated);
+            else
+                DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated);
+            return;
+        }
+
         if (breakpoint.disabled)
-            DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated);
+            DOMDebuggerAgent.removeEventBreakpoint(breakpoint.type, breakpoint.eventName, breakpointUpdated);
         else
-            DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated);
+            DOMDebuggerAgent.setEventBreakpoint(breakpoint.type, breakpoint.eventName, breakpointUpdated);
     }
 
     _updateXHRBreakpoint(breakpoint, callback)
index 7c6280c..c4c8599 100644 (file)
@@ -556,7 +556,7 @@ WI.DOMTreeManager = class DOMTreeManager extends WI.Object
 
     setBreakpointForEventListener(eventListener)
     {
-        let breakpoint = new WI.EventBreakpoint(eventListener.type, {eventListener});
+        let breakpoint = new WI.EventBreakpoint(WI.EventBreakpoint.Type.Listener, eventListener.type, {eventListener});
         this._breakpointsForEventListeners.set(eventListener.eventListenerId, breakpoint);
 
         DOMAgent.setBreakpointForEventListener(eventListener.eventListenerId, (error) => {
index 60eef5b..a37affd 100644 (file)
@@ -818,6 +818,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
     {
         // FIXME: Handle other backend pause reasons.
         switch (payload) {
+        case DebuggerAgent.PausedReason.AnimationFrame:
+            return WI.DebuggerManager.PauseReason.AnimationFrame;
         case DebuggerAgent.PausedReason.Assert:
             return WI.DebuggerManager.PauseReason.Assertion;
         case DebuggerAgent.PausedReason.Breakpoint:
@@ -834,6 +836,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
             return WI.DebuggerManager.PauseReason.Exception;
         case DebuggerAgent.PausedReason.PauseOnNextStatement:
             return WI.DebuggerManager.PauseReason.PauseOnNextStatement;
+        case DebuggerAgent.PausedReason.Timer:
+            return WI.DebuggerManager.PauseReason.Timer;
         case DebuggerAgent.PausedReason.XHR:
             return WI.DebuggerManager.PauseReason.XHR;
         default:
@@ -1233,6 +1237,7 @@ WI.DebuggerManager.Event = {
 };
 
 WI.DebuggerManager.PauseReason = {
+    AnimationFrame: "animation-frame",
     Assertion: "assertion",
     Breakpoint: "breakpoint",
     CSPViolation: "CSP-violation",
@@ -1241,6 +1246,7 @@ WI.DebuggerManager.PauseReason = {
     EventListener: "event-listener",
     Exception: "exception",
     PauseOnNextStatement: "pause-on-next-statement",
+    Timer: "timer",
     XHR: "xhr",
     Other: "other",
 };
diff --git a/Source/WebInspectorUI/UserInterface/Images/EventBreakpointAnimationFrame.svg b/Source/WebInspectorUI/UserInterface/Images/EventBreakpointAnimationFrame.svg
new file mode 100644 (file)
index 0000000..02e106b
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(148, 183, 219)" d="M 13 1 L 3 1 C 1.9 1 1 1.9 1 3 L 1 13 C 1 14.1 1.9 15 3 15 L 13 15 C 14.1 15 15 14.1 15 13 L 15 3 C 15 1.9 14.1 1 13 1 L 13 1 Z"/>
+    <path fill="rgb(106, 136, 170)" d="M 13 1 L 3 1 C 1.9 1 1 1.9 1 3 L 1 13 C 1 14.1 1.9 15 3 15 L 13 15 C 14.1 15 15 14.1 15 13 L 15 3 C 15 1.9 14.1 1 13 1 M 13 2 C 13.552 2 14 2.449 14 3 L 14 13 C 14 13.552 13.552 14 13 14 L 3 14 C 2.449 14 2 13.552 2 13 L 2 3 C 2 2.449 2.449 2 3 2 L 13 2"/>
+    <path fill="rgb(113, 146, 184)" d="M 8.1104 3 L 5.0004 3 L 4.0004 3 L 4.0004 4 L 4.0004 12 L 4.0004 13 L 5.0004 13 L 7.0004 13 L 8.0004 13 L 8.0004 12 L 8.0004 11.646 L 8.5584 12.533 L 8.8524 13 L 9.4044 13 L 11.7494 13 L 13.7134 13 L 12.5574 11.412 L 10.5494 8.650999 C 10.8754 8.459 11.1754 8.226 11.3874 7.924999 C 11.7944 7.351999 12.0004 6.702999 12.0004 6 C 12.0004 4.178 10.4734 3 8.1104 3 M 7.0004 7.632 L 7.0274 7.632 C 8.280399 7.632 9.0624 7.144 9.0624 6.167 C 9.0624 5.454 8.3484 5.097 7.2294 5.097 L 7.0004 5.097 L 7.0004 7.632 M 8.1104 4 C 9.7984 4 11.0004 4.678 11.0004 6 C 11.0004 6.493999 10.8574 6.943 10.5704 7.349 C 10.2854 7.754 9.5334 8.049 9.0314 8.264999 L 11.7494 12 L 9.4044 12 L 7.3434 8.729 L 7.0004 8.729 L 7.0004 12 L 5.0004 12 L 5.0004 4 L 8.1104 4"/>
+    <path fill="white" d="M 7 7.6323 L 7.027 7.6323 C 8.281 7.6323 9.063001 7.1443 9.063001 6.1673 C 9.063001 5.4543 8.348001 5.0973 7.229001 5.0973 L 7 5.0973 L 7 7.6323 Z M 5 12.0003 L 5 4.0003 L 8.11 4.0003 C 9.799001 4.0003 11 4.6773 11 6.0003 C 11 6.4943 10.857 6.9433 10.57 7.3493 C 10.285 7.7543 9.533 8.0493 9.031 8.2643 L 11.749 12.0003 L 9.404 12.0003 L 7.344 8.7293 L 7 8.7293 L 7 12.0003 L 5 12.0003 Z"/>
+</svg>
diff --git a/Source/WebInspectorUI/UserInterface/Images/EventBreakpointTimer.svg b/Source/WebInspectorUI/UserInterface/Images/EventBreakpointTimer.svg
new file mode 100644 (file)
index 0000000..f94ac63
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(148, 183, 219)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(106, 136, 170)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(113, 146, 184)" d="M 6.980469 12.742188 C 6.429688 12.742188 5.980469 12.292969 5.980469 11.742188 C 5.980469 11.742188 5.980469 7.34375 5.980469 5.882812 C 5.140625 5.882812 4.136719 5.882812 4.136719 5.882812 C 3.582031 5.882812 3.136719 5.4375 3.136719 4.882812 L 3.136719 3.792969 C 3.136719 3.242188 3.582031 2.792969 4.136719 2.792969 L 11.488281 2.792969 C 12.042969 2.792969 12.488281 3.242188 12.488281 3.792969 L 12.488281 4.882812 C 12.488281 5.4375 12.042969 5.882812 11.488281 5.882812 C 11.488281 5.882812 10.484375 5.882812 9.640625 5.882812 C 9.640625 7.34375 9.640625 11.742188 9.640625 11.742188 C 9.640625 12.292969 9.195312 12.742188 8.640625 12.742188 L 6.980469 12.742188"/>
+    <path fill="white" d="M 6.980469 11.742188 L 6.980469 4.882812 L 4.136719 4.882812 L 4.136719 3.792969 L 11.488281 3.792969 L 11.488281 4.882812 L 8.640625 4.882812 L 8.640625 11.742188 Z"/>
+</svg>
index 7dd7f36..1bdc3f3 100644 (file)
 
 WI.EventBreakpoint = class EventBreakpoint extends WI.Object
 {
-    constructor(eventName, {disabled, eventListener} = {})
+    constructor(type, eventName, {disabled, eventListener} = {})
     {
         super();
 
+        console.assert(typeof type === "string");
+        console.assert(Object.values(WI.EventBreakpoint.Type).includes(type));
         console.assert(typeof eventName === "string");
 
+        this._type = type;
         this._eventName = eventName;
 
         this._disabled = disabled || false;
@@ -41,13 +44,14 @@ WI.EventBreakpoint = class EventBreakpoint extends WI.Object
 
     static fromPayload(payload)
     {
-        return new WI.EventBreakpoint(payload.eventName, {
+        return new WI.EventBreakpoint(payload.type, payload.eventName, {
             disabled: !!payload.disabled,
         });
     }
 
     // Public
 
+    get type() { return this._type; }
     get eventName() { return this._eventName; }
     get eventListener() { return this._eventListener; }
 
@@ -69,6 +73,7 @@ WI.EventBreakpoint = class EventBreakpoint extends WI.Object
     get serializableInfo()
     {
         let info = {
+            type: this._type,
             eventName: this._eventName,
         };
         if (this._disabled)
@@ -79,10 +84,18 @@ WI.EventBreakpoint = class EventBreakpoint extends WI.Object
 
     saveIdentityToCookie(cookie)
     {
+        cookie[WI.EventBreakpoint.TypeCookieKey] = this._type;
         cookie[WI.EventBreakpoint.EventNameCookieKey] = this._eventName;
     }
 };
 
+WI.EventBreakpoint.Type = {
+    AnimationFrame: "animation-frame",
+    Listener: "listener",
+    Timer: "timer",
+};
+
+WI.EventBreakpoint.TypeCookieKey = "event-breakpoint-type";
 WI.EventBreakpoint.EventNameCookieKey = "event-breakpoint-event-name";
 
 WI.EventBreakpoint.Event = {
index 89e3623..50919ba 100644 (file)
@@ -951,6 +951,31 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
         let {pauseReason, pauseData} = targetData;
 
         switch (pauseReason) {
+        case WI.DebuggerManager.PauseReason.AnimationFrame:
+            console.assert(pauseData, "Expected data with an animation frame, but found none.");
+            if (!pauseData)
+                return false;
+
+            var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.AnimationFrame, pauseData.eventName);
+            console.assert(eventBreakpoint, "Expected AnimationFrame breakpoint for event name.", pauseData.eventName);
+            if (!eventBreakpoint)
+                return false;
+
+            var suppressFiltering = true;
+            this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
+
+            var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
+                className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
+                title: WI.UIString("%s Fired").format(pauseData.eventName),
+            });
+            this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
+
+            var eventBreakpointRow = new WI.DetailsSectionRow;
+            eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
+
+            this._pauseReasonGroup.rows = [eventBreakpointRow];
+            return true;
+
         case WI.DebuggerManager.PauseReason.Assertion:
             // FIXME: We should include the assertion condition string.
             console.assert(pauseData, "Expected data with an assertion, but found none.");
@@ -1056,37 +1081,42 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
 
         case WI.DebuggerManager.PauseReason.EventListener:
             console.assert(pauseData, "Expected data with an event listener, but found none.");
-            if (pauseData) {
-                let eventBreakpoint = null;
-                if (pauseData.eventListenerId)
-                    eventBreakpoint = WI.domTreeManager.breakpointForEventListenerId(pauseData.eventListenerId);
-                if (!eventBreakpoint)
-                    eventBreakpoint = WI.domDebuggerManager.eventBreakpointForEventName(pauseData.eventName);
-                console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName);
-
-                this._pauseReasonTreeOutline = this.createContentTreeOutline(true);
-
-                let eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
-                    className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
-                    title: WI.UIString("“%s“ Event Fired").format(pauseData.eventName),
-                });
-                this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
+            if (!pauseData)
+                return false;
 
-                let eventBreakpointRow = new WI.DetailsSectionRow;
-                eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
+            var eventBreakpoint = null;
+            if (pauseData.eventListenerId)
+                eventBreakpoint = WI.domTreeManager.breakpointForEventListenerId(pauseData.eventListenerId);
+            if (!eventBreakpoint)
+                eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Listener, pauseData.eventName);
 
-                let rows = [eventBreakpointRow];
+            console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName);
+            if (!eventBreakpoint)
+                return false;
 
-                let eventListener = eventBreakpoint.eventListener;
-                if (eventListener) {
-                    console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
+            var suppressFiltering = true;
+            this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
 
-                    let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(eventListener.node));
-                    rows.push(ownerElementRow);
-                }
+            var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
+                className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
+                title: WI.UIString("“%s“ Event Fired").format(pauseData.eventName),
+            });
+            this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
+
+            var eventBreakpointRow = new WI.DetailsSectionRow;
+            eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
+
+            var rows = [eventBreakpointRow];
 
-                this._pauseReasonGroup.rows = rows;
+            var eventListener = eventBreakpoint.eventListener;
+            if (eventListener) {
+                console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
+
+                let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(eventListener.node));
+                rows.push(ownerElementRow);
             }
+
+            this._pauseReasonGroup.rows = rows;
             return true;
 
         case WI.DebuggerManager.PauseReason.Exception:
@@ -1105,6 +1135,31 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
             return true;
 
+        case WI.DebuggerManager.PauseReason.Timer:
+            console.assert(pauseData, "Expected data with a timer, but found none.");
+            if (!pauseData)
+                return false;
+
+            var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Timer, pauseData.eventName);
+            console.assert(eventBreakpoint, "Expected Timer breakpoint for event name.", pauseData.eventName);
+            if (!eventBreakpoint)
+                return false;
+
+            var suppressFiltering = true;
+            this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
+
+            var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
+                className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
+                title: WI.UIString("%s Fired").format(pauseData.eventName),
+            });
+            this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
+
+            var eventBreakpointRow = new WI.DetailsSectionRow;
+            eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
+
+            this._pauseReasonGroup.rows = [eventBreakpointRow];
+            return true;
+
         case WI.DebuggerManager.PauseReason.XHR:
             console.assert(WI.domDebuggerManager.supported);
             console.assert(pauseData, "Expected XHR breakpoint data, but found none.");
@@ -1264,17 +1319,14 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
 
     willDismissPopover(popover)
     {
-        if (popover.result !== WI.InputPopover.Result.Committed)
-            return;
-
         if (popover instanceof WI.EventBreakpointPopover) {
-            let eventName = popover.value;
-            if (eventName)
-                WI.domDebuggerManager.addEventBreakpoint(new WI.EventBreakpoint(eventName));
+            let breakpoint = popover.breakpoint;
+            if (breakpoint)
+                WI.domDebuggerManager.addEventBreakpoint(breakpoint);
             return;
         }
 
-        if (popover instanceof WI.XHRBreakpointPopover) {
+        if (popover instanceof WI.XHRBreakpointPopover && popover.result === WI.InputPopover.Result.Committed) {
             let url = popover.value;
             if (url)
                 WI.domDebuggerManager.addXHRBreakpoint(new WI.XHRBreakpoint(popover.type, url));
index 37a06c6..4a42ece 100644 (file)
@@ -28,9 +28,7 @@
     padding: 5px;
 }
 
-.popover .event-breakpoint-content > input {
-    width: 100%;
+.popover .event-breakpoint-content > .event-type {
+    display: flex;
     margin-top: 4px;
-    padding: 4px 0 2px 0;
-    outline: none;
 }
index 5f75b51..ad7a92a 100644 (file)
@@ -29,10 +29,8 @@ WI.EventBreakpointPopover = class EventBreakpointPopover extends WI.Popover
     {
         super(delegate);
 
-        this._result = WI.InputPopover.Result.None;
-        this._value = null;
+        this._breakpoint = null;
 
-        this._codeMirror = null;
         this._targetElement = null;
         this._preferredEdges = null;
 
@@ -41,8 +39,7 @@ WI.EventBreakpointPopover = class EventBreakpointPopover extends WI.Popover
 
     // Public
 
-    get result() { return this._result; }
-    get value() { return this._value; }
+    get breakpoint() { return this._breakpoint; }
 
     show(targetElement, preferredEdges)
     {
@@ -56,22 +53,69 @@ WI.EventBreakpointPopover = class EventBreakpointPopover extends WI.Popover
         label.classList.add("label");
         label.textContent = WI.UIString("Break on events with name:");
 
-        this._inputElement = contentElement.appendChild(document.createElement("input"));
-        this._inputElement.placeholder = "click";
-        this._inputElement.spellcheck = false;
-        this._inputElement.addEventListener("keydown", (event) => {
+        let typeContainer = contentElement.appendChild(document.createElement("div"));
+        typeContainer.classList.add("event-type");
+
+        this._typeSelectElement = typeContainer.appendChild(document.createElement("select"));
+        this._typeSelectElement.addEventListener("change", this._handleTypeSelectChange.bind(this));
+        this._typeSelectElement.addEventListener("keydown", (event) => {
+            if (isEnterKey(event))
+                this.dismiss();
+        });
+
+        let createOption = (text, value) => {
+            let optionElement = this._typeSelectElement.appendChild(document.createElement("option"));
+            optionElement.value = value;
+            optionElement.textContent = text;
+        };
+
+        createOption(WI.UIString("DOM Event"), WI.EventBreakpoint.Type.Listener);
+
+        if (WI.DOMDebuggerManager.supportsEventBreakpoints()) {
+            createOption(WI.unlocalizedString("requestAnimationFrame"), "requestAnimationFrame");
+            createOption(WI.unlocalizedString("setTimeout"), "setTimeout");
+            createOption(WI.unlocalizedString("setInterval"), "setInterval");
+        } else
+            this._typeSelectElement.hidden = true;
+
+        this._domEventNameInputElement = typeContainer.appendChild(document.createElement("input"));
+        this._domEventNameInputElement.placeholder = WI.UIString("Example: “%s”").format("click");
+        this._domEventNameInputElement.spellcheck = false;
+        this._domEventNameInputElement.addEventListener("keydown", (event) => {
             if (!isEnterKey(event))
                 return;
 
-            this._result = WI.InputPopover.Result.Committed;
-            this._value = event.target.value.trim();
-
             this.dismiss();
         });
 
         this.content = contentElement;
 
         this._presentOverTargetElement();
+
+        this._typeSelectElement.value = WI.EventBreakpoint.Type.Listener;
+        this._domEventNameInputElement.select();
+    }
+
+    dismiss()
+    {
+        let type = this._typeSelectElement.value;
+        let value = null;
+
+        if (type === WI.EventBreakpoint.Type.Listener)
+            value = this._domEventNameInputElement.value;
+        else {
+            value = type;
+
+            if (value === "requestAnimationFrame")
+                type = WI.EventBreakpoint.Type.AnimationFrame;
+            else if (value === "setTimeout" || value === "setInterval")
+                type = WI.EventBreakpoint.Type.Timer;
+        }
+
+        if (type && value)
+            this._breakpoint = new WI.EventBreakpoint(type, value);
+
+        super.dismiss();
     }
 
     // Private
@@ -83,7 +127,12 @@ WI.EventBreakpointPopover = class EventBreakpointPopover extends WI.Popover
 
         let targetFrame = WI.Rect.rectFromClientRect(this._targetElement.getBoundingClientRect());
         this.present(targetFrame, this._preferredEdges);
+    }
+
+    _handleTypeSelectChange(event)
+    {
+        this._domEventNameInputElement.hidden = this._typeSelectElement.value !== WI.EventBreakpoint.Type.Listener;
 
-        this._inputElement.select();
+        this.update();
     }
 };
index 622f025..8b5a1c2 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-.breakpoint.event:not(.breakpoint-paused-icon) .icon {
-    content: url(../Images/EventBreakpoint.svg);
+.breakpoint.event.breakpoint-for-animation-frame:not(.breakpoint-paused-icon) .icon {
+    content: url(../Images/EventBreakpointAnimationFrame.svg);
+}
+
+.breakpoint.event.breakpoint-for-listener:not(.breakpoint-paused-icon) .icon {
+    content: url(../Images/EventBreakpointListener.svg);
+}
+
+.breakpoint.event.breakpoint-for-timer:not(.breakpoint-paused-icon) .icon {
+    content: url(../Images/EventBreakpointTimer.svg);
 }
index 6964b79..a2c0b4a 100644 (file)
@@ -29,7 +29,7 @@ WI.EventBreakpointTreeElement = class EventBreakpointTreeElement extends WI.Gene
     {
         console.assert(breakpoint instanceof WI.EventBreakpoint);
 
-        let classNames = ["breakpoint", "event"];
+        let classNames = ["breakpoint", "event", `breakpoint-for-${breakpoint.type}`];
         if (className)
             classNames.push(className);