DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

Firefox Debugger Deep Dive: Conditional Breakpoints, Logpoints, and XHR Tracing

Firefox's JavaScript debugger is powerful and has features Chrome's DevTools doesn't. Here's a practical guide to the parts that save the most time.

The Debugger Basics

Open: F12 → Debugger (or Ctrl+Shift+S)

Sources panel: Left sidebar shows all JavaScript files loaded by the page. Organized by origin. Click any file to view and set breakpoints.

Breakpoints: Click the line number gutter to set a breakpoint. Execution pauses when that line runs.

Call stack: When paused, shows the full call stack. Click any frame to inspect that scope's variables.

Scopes: Shows variables in current scope — local variables, closure variables, and global scope.

Conditional Breakpoints

Right-click a line number → "Add Conditional Breakpoint"

Enter a JavaScript expression. Execution pauses only when the expression is truthy.

Example: You're debugging a loop processing items but only want to pause when item.id === 'specific-id':

item.id === 'specific-id'
Enter fullscreen mode Exit fullscreen mode

This avoids pausing 10,000 times on the wrong iterations.

Logpoints: Breakpoints Without Pausing

Right-click a line number → "Add Log"

Enter an expression to log. When the line executes, it prints to the console without pausing execution.

Example: "user:", user.id, user.role

Useful for understanding code flow without stopping execution or adding console.log to source files.

Why better than console.log: Logpoints don't modify your source code. No risk of committing accidental debug logging. Disappear when you close DevTools.

Event Breakpoints

Debugger → Event Listener Breakpoints (in the right panel)

Check specific DOM events (click, submit, keypress, etc.). Execution pauses whenever that event fires, regardless of which handler handles it.

Useful for: "Something happens when I click this, but I don't know which handler is firing." Enable click breakpoints and click the element — execution pauses at the first handler that runs.

Exception Breakpoints

Click the "Pause on exceptions" button (looks like a stop sign or exclamation mark in the toolbar).

Options:

  • Pause on all exceptions
  • Pause on uncaught exceptions only

When enabled, execution pauses at the throw site of any exception, not just where it's caught. Useful for tracking down silent error swallowing.

Watch Expressions

Right panel → Watch Expressions → Add

Enter any expression (variable name, property access, function call). The expression is re-evaluated every time execution pauses, showing you the current value.

Useful when: You're debugging deep in nested code and want to watch window.myState.currentUser.permissions without navigating to it in Scopes every time.

The Console Inside the Debugger

When execution is paused, the browser console operates in the paused scope. You can:

  • Access local variables by name
  • Call functions
  • Inspect and modify state
  • Test fixes without reloading

Example: Paused inside a function with config variable, you can type config in the console to inspect it, or config.debug = true to modify and continue.

Step Controls

Button Action
Resume (F8) Continue to next breakpoint
Step over (F10) Execute current line, stay at same depth
Step into (F11) Enter the function call on current line
Step out (Shift+F11) Run until current function returns
Step (F9) Step to next expression (more granular than step over)

Firefox has "Step" (F9) which steps through each expression in a compound statement — more granular than Chrome's step over. Useful when you have a = foo() + bar() and want to see each function call result.

Source Maps

If your code is bundled/minified, configure source maps in your build tool. When source maps are detected, the Debugger shows original source files.

Verify source maps are loading: Sources panel → check if you see webpack:// or your original file paths alongside the bundle.

If source maps aren't loading, check:

  1. The .map file URL is accessible
  2. The //# sourceMappingURL= comment at end of bundle points to the right location
  3. CORS isn't blocking the .map file load

Worker and Service Worker Debugging

Debugger → Workers (section in left panel) → Shows any Web Workers or Service Workers.

Click on a worker to open its source and set breakpoints inside worker scripts. Firefox makes worker debugging accessible from the same Debugger interface.

For Service Workers specifically: about:debugging → This Firefox → Service Workers shows all registered SWs with inspect/stop/unregister buttons.

The Pretty Print Button

If you're debugging minified code without source maps, click the {} button (pretty print) at the bottom of the source panel. Firefox formats the minified code to be readable and adjusts breakpoint positions accordingly.

Not perfect (variable names are still minified), but makes the structure readable.

XHR/Fetch Breakpoints

In the Breakpoints panel (right sidebar) → XHR/Fetch Breakpoints → Add

Enter a URL substring. When any XHR or fetch request is made to a URL containing that string, execution pauses.

Useful: "An API call is happening that I can't find in the code." Add an XHR breakpoint for the API endpoint and the call stack will show you exactly where it's initiated.

DOM Mutation Breakpoints

Inspector → Right-click a DOM node → Break on → Subtree Modifications / Attribute Modifications / Node Removal

When the DOM changes at that node, execution pauses in the JavaScript that caused the change.

Useful for debugging "something is unexpectedly modifying this element" scenarios.

A Practical Debugging Workflow

  1. Identify the symptom (wrong state, unexpected behavior)
  2. Set an XHR breakpoint or event listener breakpoint to find where the flow starts
  3. Follow the call stack — step into relevant functions
  4. Use watch expressions for state that matters
  5. Use logpoints for broader flow understanding without pausing
  6. Set conditional breakpoints once you've narrowed to specific conditions

Firefox's debugger rewards building up from where the problem is observed backward to the root cause, using the call stack as a map.

Top comments (0)