tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e538d5c362b488d35a27ead7e7230b32aec4c6f1
parent eb11db3022c46017b5a35206cba7a3d7f78a705e
Author: Julian Descottes <jdescottes@mozilla.com>
Date:   Thu,  9 Oct 2025 09:11:40 +0000

Bug 1992769 - [devtools] Sanitize event breakpoints based on event ids supported by the server r=devtools-reviewers,ochameau

After retrieving the list of supported event breakpoints from the server cleanup the debugger store to remove unsupported events.

Differential Revision: https://phabricator.services.mozilla.com/D267965

Diffstat:
Mdevtools/client/debugger/src/reducers/event-listeners.js | 22++++++++++++++++++++--
Mdevtools/client/debugger/test/mochitest/browser_aj.toml | 3+++
Adevtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-unsupported.js | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdevtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js | 62--------------------------------------------------------------
Mdevtools/client/debugger/test/mochitest/head.js | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 152 insertions(+), 64 deletions(-)

diff --git a/devtools/client/debugger/src/reducers/event-listeners.js b/devtools/client/debugger/src/reducers/event-listeners.js @@ -32,8 +32,26 @@ export function initialEventListenerState() { function update(state = initialEventListenerState(), action) { switch (action.type) { - case "RECEIVE_EVENT_LISTENER_TYPES": - return { ...state, categories: action.categories }; + case "RECEIVE_EVENT_LISTENER_TYPES": { + // The stored breakpoints in asyncStorage might not match the ones + // supported by the backend, either for backward compatibility reasons, + // or because the asyncStorage contains stale data. + // + // Sanitize active breakpoints based on currently supported events. + const supportedEventIds = action.categories + .map(category => category.events) + .flat() + .map(event => event.id); + + state.byPanel.breakpoint.active = state.active.filter(id => + supportedEventIds.includes(id) + ); + + return { + ...state, + categories: action.categories, + }; + } case "UPDATE_EVENT_LISTENERS": if (action.panelKey == "tracer") { diff --git a/devtools/client/debugger/test/mochitest/browser_aj.toml b/devtools/client/debugger/test/mochitest/browser_aj.toml @@ -164,6 +164,9 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and ["browser_dbg-event-breakpoints-fission.js"] +["browser_dbg-event-breakpoints-unsupported.js"] +fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled + ["browser_dbg-event-breakpoints.js"] fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled skip-if = ["os == 'mac' && os_version == '14.70' && processor == 'x86_64'"] # Bug 1959062 diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-unsupported.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-unsupported.js @@ -0,0 +1,67 @@ +/* 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/>. */ + +// Tests early event breakpoints and event breakpoints in a remote frame. + +"use strict"; + +const BAD_EVENT_ID = "event.dom-mutation.DOMSubtreeModified"; + +add_task(async function () { + info( + "Open and close the debugger a first time to prime the preferences and async storage" + ); + let dbg = await initDebugger("doc-event-breakpoints.html"); + await dbg.toolbox.closeToolbox(); + + info("Update the event listeners breakpoint data with an invalid event id"); + const staleEventListenerBreakpointsState = { + categories: [], + byPanel: { + breakpoint: { + active: [BAD_EVENT_ID], + expanded: [], + }, + tracer: { expanded: [] }, + }, + logEventBreakpoints: false, + active: [], + expanded: [], + }; + await asyncStorage.setItem( + "debugger.event-listener-breakpoints", + staleEventListenerBreakpointsState + ); + + // Note: DO NOT use initDebugger here, as it would clear storage and + // preferences. + info("Reopen the debugger without resetting preferences"); + const toolbox = await openNewTabAndToolbox( + EXAMPLE_URL + "doc-event-breakpoints.html", + "jsdebugger" + ); + dbg = createDebuggerContext(toolbox); + await waitForSources(dbg, "event-breakpoints.js"); + + await selectSource(dbg, "event-breakpoints.js"); + await waitForSelectedSource(dbg, "event-breakpoints.js"); + const eventBreakpointsSource = findSource(dbg, "event-breakpoints.js"); + + info("Check that an event breakpoint can still be set"); + await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click"); + + invokeInTab("clickHandler"); + await waitForPaused(dbg); + await assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12); + + const sanitizedEventListenerBreakpointsState = await asyncStorage.getItem( + "debugger.event-listener-breakpoints" + ); + const sanitizedActive = + sanitizedEventListenerBreakpointsState.byPanel.breakpoint.active; + ok(!sanitizedActive.includes(BAD_EVENT_ID)); + ok(sanitizedActive.includes("event.mouse.click")); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js @@ -308,68 +308,6 @@ add_task(async function () { is(timerGroupCheckbox.checked, true); }); -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( diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js @@ -196,3 +196,65 @@ function assertCursorPosition(dbg, expectedLine, expectedColumn, message) { function selectContextMenuItem(dbg, selector) { return selectDebuggerContextMenuItem(dbg, selector); } + +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}"]`); +}