/* 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 . */ "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] }, }); }