summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js')
-rw-r--r--devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js317
1 files changed, 317 insertions, 0 deletions
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
new file mode 100644
index 0000000000..1065674186
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
@@ -0,0 +1,317 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+"use strict";
+
+add_task(async function () {
+ await pushPref("dom.element.invokers.enabled", true);
+ await pushPref("dom.element.popover.enabled", true);
+
+ const dbg = await initDebugger(
+ "doc-event-breakpoints.html",
+ "event-breakpoints.js"
+ );
+ await selectSource(dbg, "event-breakpoints.js");
+ await waitForSelectedSource(dbg, "event-breakpoints.js");
+ const eventBreakpointsSource = findSource(dbg, "event-breakpoints.js");
+
+ // We want to set each breakpoint individually to test adding/removing breakpoints, see Bug 1748589.
+ await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click");
+
+ invokeInTab("clickHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12);
+
+ const whyPaused = await waitFor(
+ () => dbg.win.document.querySelector(".why-paused")?.innerText
+ );
+ is(
+ whyPaused,
+ `Paused on event breakpoint\nDOM 'click' event`,
+ "whyPaused does state that the debugger is paused as a result of a click event breakpoint"
+ );
+ await resume(dbg);
+
+ await toggleEventBreakpoint(dbg, "XHR", "event.xhr.load");
+ invokeInTab("xhrHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 20);
+ await resume(dbg);
+
+ await toggleEventBreakpoint(dbg, "Timer", "timer.timeout.set");
+ await toggleEventBreakpoint(dbg, "Timer", "timer.timeout.fire");
+ invokeInTab("timerHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 27);
+ await resume(dbg);
+
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 28);
+ await resume(dbg);
+
+ await toggleEventBreakpoint(dbg, "Script", "script.source.firstStatement");
+ invokeInTab("evalHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, findSource(dbg, "eval-test.js").id, 2);
+ await resume(dbg);
+ await toggleEventBreakpoint(dbg, "Script", "script.source.firstStatement");
+
+ await toggleEventBreakpoint(dbg, "Control", "event.control.focusin");
+ await toggleEventBreakpoint(dbg, "Control", "event.control.focusout");
+ invokeOnElement("#focus-text", "focus");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 43);
+ await resume(dbg);
+
+ // wait for focus-out event to fire
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 48);
+ await resume(dbg);
+
+ info("Deselect focus events");
+ // We need to give the input focus to test composition, but we don't want the
+ // focus breakpoints to fire.
+ await toggleEventBreakpoint(dbg, "Control", "event.control.focusin");
+ await toggleEventBreakpoint(dbg, "Control", "event.control.focusout");
+
+ await toggleEventBreakpoint(dbg, "Control", "event.control.invoke");
+ invokeOnElement("#invoker", "click");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 73);
+ await resume(dbg);
+
+ info("Enable beforetoggle and toggle events");
+ await toggleEventBreakpoint(dbg, "Control", "event.control.beforetoggle");
+ await toggleEventBreakpoint(dbg, "Control", "event.control.toggle");
+ invokeOnElement("#popover-toggle", "click");
+ info("Wait for pause in beforetoggle event listener");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 89);
+ await resume(dbg);
+ info("And wait for pause in toggle event listener after resuming");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 93);
+ await resume(dbg);
+
+ await toggleEventBreakpoint(
+ dbg,
+ "Keyboard",
+ "event.keyboard.compositionstart"
+ );
+ invokeOnElement("#focus-text", "focus");
+
+ info("Type some characters during composition");
+ invokeComposition();
+
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 53);
+ await resume(dbg);
+
+ info("Deselect compositionstart and select compositionupdate");
+ await toggleEventBreakpoint(
+ dbg,
+ "Keyboard",
+ "event.keyboard.compositionstart"
+ );
+ await toggleEventBreakpoint(
+ dbg,
+ "Keyboard",
+ "event.keyboard.compositionupdate"
+ );
+
+ invokeOnElement("#focus-text", "focus");
+
+ info("Type some characters during composition");
+ invokeComposition();
+
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 58);
+ await resume(dbg);
+
+ info("Deselect compositionupdate and select compositionend");
+ await toggleEventBreakpoint(
+ dbg,
+ "Keyboard",
+ "event.keyboard.compositionupdate"
+ );
+ await toggleEventBreakpoint(dbg, "Keyboard", "event.keyboard.compositionend");
+ invokeOnElement("#focus-text", "focus");
+
+ info("Type some characters during composition");
+ invokeComposition();
+
+ info("Commit the composition");
+ EventUtils.synthesizeComposition({
+ type: "compositioncommitasis",
+ key: { key: "KEY_Enter" },
+ });
+
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 63);
+ await resume(dbg);
+
+ info(`Check that breakpoint can be set on "scrollend"`);
+ await toggleEventBreakpoint(dbg, "Control", "event.control.scrollend");
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ content.scrollTo({ top: 20, behavior: "smooth" });
+ });
+
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 68);
+ await resume(dbg);
+
+ info("Check that the click event breakpoint is still enabled");
+ invokeInTab("clickHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12);
+ await resume(dbg);
+
+ info("Check that disabling an event breakpoint works");
+ await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click");
+ invokeInTab("clickHandler");
+ // wait for a bit to make sure the debugger do not pause
+ await wait(100);
+ assertNotPaused(dbg);
+
+ info("Check that we can re-enable event breakpoints");
+ await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click");
+ invokeInTab("clickHandler");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12);
+ await resume(dbg);
+
+ info(
+ "Test that we don't pause on event breakpoints when source is blackboxed."
+ );
+ await clickElement(dbg, "blackbox");
+ await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES");
+
+ invokeInTab("clickHandler");
+ // wait for a bit to make sure the debugger do not pause
+ await wait(100);
+ assertNotPaused(dbg);
+
+ invokeInTab("xhrHandler");
+ // wait for a bit to make sure the debugger do not pause
+ await wait(100);
+ assertNotPaused(dbg);
+
+ invokeInTab("timerHandler");
+ // wait for a bit to make sure the debugger do not pause
+ await wait(100);
+ assertNotPaused(dbg);
+
+ // Cleanup - unblackbox the source
+ await clickElement(dbg, "blackbox");
+ await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES");
+
+ info(`Check that breakpoint can be set on "beforeUnload" event`);
+ await toggleEventBreakpoint(dbg, "Load", "event.load.beforeunload");
+ let onReload = reload(dbg);
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 78);
+ await resume(dbg);
+ await onReload;
+ await toggleEventBreakpoint(dbg, "Load", "event.load.beforeunload");
+
+ info(`Check that breakpoint can be set on "unload" event`);
+ await toggleEventBreakpoint(dbg, "Load", "event.load.unload");
+ onReload = reload(dbg);
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 83);
+ await resume(dbg);
+ await onReload;
+ await toggleEventBreakpoint(dbg, "Load", "event.load.unload");
+});
+
+function getEventListenersPanel(dbg) {
+ return findElementWithSelector(dbg, ".event-listeners-pane .event-listeners");
+}
+
+async function toggleEventBreakpoint(
+ dbg,
+ eventBreakpointGroup,
+ eventBreakpointName
+) {
+ const eventCheckbox = await getEventBreakpointCheckbox(
+ dbg,
+ eventBreakpointGroup,
+ eventBreakpointName
+ );
+ eventCheckbox.scrollIntoView();
+ info(`Toggle ${eventBreakpointName} breakpoint`);
+ const onEventListenersUpdate = waitForDispatch(
+ dbg.store,
+ "UPDATE_EVENT_LISTENERS"
+ );
+ const checked = eventCheckbox.checked;
+ eventCheckbox.click();
+ await onEventListenersUpdate;
+
+ info("Wait for the event breakpoint checkbox to be toggled");
+ // Wait for he UI to be toggled, otherwise, the reducer may not be fully updated
+ await waitFor(() => {
+ return eventCheckbox.checked == !checked;
+ });
+}
+
+async function getEventBreakpointCheckbox(
+ dbg,
+ eventBreakpointGroup,
+ eventBreakpointName
+) {
+ if (!getEventListenersPanel(dbg)) {
+ // Event listeners panel is collapsed, expand it
+ findElementWithSelector(
+ dbg,
+ `.event-listeners-pane ._header .header-label`
+ ).click();
+ await waitFor(() => getEventListenersPanel(dbg));
+ }
+
+ const groupCheckbox = findElementWithSelector(
+ dbg,
+ `input[value="${eventBreakpointGroup}"]`
+ );
+ const groupEl = groupCheckbox.closest(".event-listener-group");
+ let groupEventsUl = groupEl.querySelector("ul");
+ if (!groupEventsUl) {
+ info(
+ `Expand ${eventBreakpointGroup} and wait for the sub list to be displayed`
+ );
+ groupEl.querySelector(".event-listener-expand").click();
+ groupEventsUl = await waitFor(() => groupEl.querySelector("ul"));
+ }
+
+ return findElementWithSelector(dbg, `input[value="${eventBreakpointName}"]`);
+}
+
+async function invokeOnElement(selector, action) {
+ await SpecialPowers.focus(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [selector, action],
+ (_selector, _action) => {
+ content.document.querySelector(_selector)[_action]();
+ }
+ );
+}
+
+function invokeComposition() {
+ const string = "ex";
+ EventUtils.synthesizeCompositionChange({
+ composition: {
+ string,
+ clauses: [
+ {
+ length: string.length,
+ attr: Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE,
+ },
+ ],
+ },
+ caret: { start: string.length, length: 0 },
+ key: { key: string[string.length - 1] },
+ });
+}