Script Panel Refactoring

From FirebugWiki

(Difference between revisions)
Jump to: navigation, search
(JSD2 API Requirements)
m (Fixed some links)
(37 intermediate revisions not shown)
Line 1: Line 1:
-
This page summarizes support for remote JS debugging in Firebug (based on JSD2).
+
This page summarizes refactoring the Firebug debugger to use JSD2 and adding support for remote JavaScript debugging. [http://code.google.com/p/fbug/issues/detail?id=5421 Issue 5421] tracks the actual changes done.
 +
 
 +
== Resources ==
 +
Firebug Wiki:
 +
* [[HTTP Monitor]]
 +
* [[Firebug 1.7: Mozilla Backend|Firebug 1.7 BTI]]
 +
* [[Net Panel Architecture Review]]
 +
* [[Remoting Prototype#Prototype|Remoting Prototype]]
 +
* [[Remoting Architecture|Remoting Architecture]]
 +
* [[JSD2 Adoption|JSD2 Adoption]]
 +
 
 +
Mozilla Wiki:
 +
* [https://wiki.mozilla.org/Debugger Debugger]
 +
* [https://wiki.mozilla.org/Debugger_Client_API Debugger Client API]
 +
* [https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Guide JS Debugger API Guide]
 +
* [https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference JS Debugger API Reference]
 +
* [https://wiki.mozilla.org/DevTools/Features/Debugger DevTools Debugger]
 +
* [https://wiki.mozilla.org/Remote_Debugging_Protocol Remote Debugging Protocol]
== Goals ==
== Goals ==
* Adopt JSD2 (get rid of all JSD1 APIs)
* Adopt JSD2 (get rid of all JSD1 APIs)
-
* Support Remote Debugging
+
* Support remote debugging
 +
== Related Issues ==
 +
* [http://code.google.com/p/fbug/issues/detail?id=5421 Issue 5421]: Implement JSD2
 +
* [http://code.google.com/p/fbug/issues/detail?id=5837 Issue 5837]: Implement remote debugging
 +
 +
== Related Mozilla Issues ==
 +
* [https://bugzilla.mozilla.org/show_bug.cgi?id=676586 Meta bug]: Implement a script Debugger
 +
* [https://bugzilla.mozilla.org/show_bug.cgi?id=740825 bug 740825]:  Implement conditional breakpoints
 +
* [https://bugzilla.mozilla.org/show_bug.cgi?id=816756 bug 816756]: CodeMirror as an alternative to Orion
== JSD2 API Requirements ==
== JSD2 API Requirements ==
-
The following list summarizes list of high-level features that Firebug needs to support. It should be verified that all the features can re-implemented on top of JSD2 before starting the refactoring.
+
The following list summarizes the high-level features that Firebug needs to support. It should be verified that all the features can be re-implemented on top of JSD2 before starting the refactoring.
-
* List of scripts available on selected tab (including static, events and evaluated scripts) including iframes.
+
* List of scripts available on selected tab (including static, events and evaluated scripts) including iframes &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#findScripts%28%29 Debugger.findScripts()]</code>
-
* Stepping (into, over, out, resume, current line)  
+
* Stepping (into, over, out, resume, current line)
-
* Support for <code>debugger;</code> keyword
+
* Support for <code>debugger;</code> keyword &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#onDebuggerStatement%28%29 Debugger.onDebuggerStatement()]</code>
-
* Support for breakpoints (add, remove, enable, disabled, conditional, list of existing breakpoints)
+
* Support for breakpoints (add, remove, enable, disabled, conditional, list of existing breakpoints) &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger.Script#Methods Debugger.Script methods]</code>
* Recognize executable lines
* Recognize executable lines
-
* Dynamic eval in a frame (used e.g. by the Watch panel when the debugger is halted)
+
* Dynamic eval in a frame (used e.g. by the [[Watch Side Panel]] when the debugger is halted) &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger.Frame#eval%28%29 Debugger.Frame.eval()]</code> and <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger.Frame#evalWithBindings%28%29 Debugger.Frame.evalWithBindings()]</code>
-
* Get stack frames (including passed arguments)
+
* Get stack frames (including passed arguments) &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#getNewestFrame%28%29 Debugger.getNewestFrame()]</code> and <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger.Frame#Accessor_properties Debugger.Frame.older]</code>
-
* Scope chain variable exploring (with & closure scopes). It should be possible to see all values.
+
* Scope chain variable exploring (with & closure scopes). It should be possible to see all values. &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger.Frame#Accessor_properties Debugger.Frame.environment]</code>
* Break on next call
* Break on next call
* Profiling (was part of JSD1)
* Profiling (was part of JSD1)
-
* Tracking (break on) throw/catch/error
+
* Tracking (break on) throw/catch/error &rarr; <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#onThrow%28%29 Debugger.onThrow()]</code> and <code>[https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#onError%28%29 Debugger.onError()]</code>
* Tracking (monitoring) function calls
* Tracking (monitoring) function calls
* Freezing page UI if debugger is halted (including timeouts, intervals and worker threads)
* Freezing page UI if debugger is halted (including timeouts, intervals and worker threads)
-
 
=== Not Ready in JSD2 ===
=== Not Ready in JSD2 ===
* There is not source for evals.
* There is not source for evals.
-
* new Function scripts?
+
* [https://developer.mozilla.org/en/SpiderMonkey/JS_Debugger_API_Reference/Debugger#onNewScript%28%29 new Function scripts?]
-
* [https://bugzilla.mozilla.org/show_bug.cgi?id=676586 Meta bug]: Implement a script Debugger
+
** As soon as this one is fixed
** As soon as this one is fixed
-
** Profiling is unrelated to JSD2. What it the plan here?
+
** Profiling is unrelated to JSD2. What is the plan here?
-
** Tracking (break on) throw/catch/error, also what is that plan?
+
** Tracking (break on) throw/catch/error; also what is the plan?
-
== Script Panel Architecture ==
+
== Current Debugger Architecture ==
-
The Script panel needs to be built on top of JSD2, remote protocol and Firebug remote architecture. Remoting is already supported by [HTTP Monitor] and both components should share the same approaches and API
+
This section describes the current debugger architecture.
-
TBD
+
[[File:Current-debugger-architecture.png]]
 +
There are several layers/objects, so let's describe them step by step starting from the bottom.
-
== Resources ==
+
* Backend: JSD1, FirebugService, Debugger
-
* [https://wiki.mozilla.org/Debugger Debugger]
+
* Frontend: JavaScriptTool, ScriptPanel
-
* [https://wiki.mozilla.org/DevTools/Features/Debugger DevTools Debugger]
+
 
-
* [https://wiki.mozilla.org/Remote_Debugging_Protocol Remote Debugging Protocol]
+
=== JSD1 ===
 +
This layer represents the JSD1 platform API. This API allows to implement script debuggers and represents a direct competition to the JSD2 API. Of course this layer should entirely disappear and should be replaced by JSD2.
 +
 
 +
=== Firebug Service ===
 +
The Firebug Service is implemented as [https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Using JavaScript module] on top of the JSD1 layer. The object is called ''FBS'' and its purpose is to wrap the JSD1 APIs, so they are not directly accessed anywhere else. This layer also maintains a list of registered debuggers (usually the <code>Firebug.Debugger</code> module) and fires various events to them (e.g. <code>onToggleBreakpoint</code>, <code>onToggleMonitor</code>, etc.) or execute their callback methods (e.g. <code>onBreak</code>, <code>onFunctionCall</code>, <code>onError</code>, <code>onThrow</code>, <code>onScriptCreated</code>, etc.).
 +
 
 +
Main responsibilities:
 +
* Activate/deactivate JSD in the browser. The activation is global for all current browser windows.
 +
* Hook debugger events (interrupts, break on a breakpoint, etc.)
 +
* Maintains a list of registered debuggers (there is usually just one - Firebug debugger) and sends events to them.
 +
* Manages the nested event loop that is created for the debugger UI when a page's JS execution breaks.
 +
* Sets/removes/enables/disables/saves/loads breakpoints
 +
* Implements debugger stepping over/in/out/runUntil
 +
* Monitors function calls
 +
* Starts/stops profiling
 +
* Tracks exceptions and errors (not working well)
 +
* Tracks compiled scripts (not all of them)
 +
 
 +
=== Debugger ===
 +
This object is derived from <code>Firebug.Module</code> and represents Firebug's debugger. It's also registered as a debugger into the ''Firebug Service'' (FBS). This object should be the only one accessing FBS. It calls FBS API and receives various callbacks and events.
 +
 
 +
This object implements methods that can be used by the [[Script Panel]] or other parts of Firebug (e.g. by those panels which implements [[Script Panel#Break On Next|BON]).
 +
 
 +
Some examples of the API:
 +
* <code>evaluate</code>, <code>evaluateInCallingFrame</code>
 +
* <code>breakNow</code>, <code>getCurrentStackTrace</code>
 +
* <code>rerun</code>
 +
* <code>stepOver</code>, <code>stepInto</code>, <code>stepOut</code>, <code>runUntil, <code>resume</code>
 +
* <code>setBreakpoint</code>, <code>clearBrakpoint</code>, etc.
 +
* <code>monitorFunction</code>, <code>unmonitorFunction</code>
 +
* <code>monitorScript</code>, <code>unmonitorScript</code>
 +
* <code>activateDebugger</code>, <code>deactivateDebugger</code>
 +
 
 +
The implementation of these methods is based on FBS API.
 +
 
 +
There is also <code>Firebug.DebuggerListener</code> that defines the interface used by FBS
 +
* <code>onStop</code>, <code>onResume</code>, <code>onThrow</code>, <code>onError</code>, <code>onScriptCreated</code> (there are more <code>onScriptCreated</code> events)
 +
 
 +
This object is ''observable'' and it activates the underlying JSD only if an observer exists. The observer is currently the Script panel even when it would make a bit more sense if it's actually the ''JavaScriptTool''.
 +
 
 +
=== JavaScriptTool ===
 +
This object is part of the Browser Tools Interface (BTI) and represents a JavaScript debugger tool. This object is like a proxy sitting between <code>Firebug.Debugger</code> and the <code>ScriptPanel</code>. The current implementation is in-process only, so based on direct <code>Firebug.Debugger</code> API calls.
 +
 
 +
API of the tool is as follows:
 +
* <code>setBreakpoint</code>, <code>clearBreakpoint</code>, <code>enableBreakpoint</code>, <code>disableBreakpoint</code>, <code>isBreakpointDisabled</code>, <code>getBreakpointCondition</code>
 +
* <code>rerun</code>
 +
* <code>resumeJavaScript</code>, <code>stepOver</code>, <code>stepInto</code>, <code>stepOut</code>, <code>runUntil</code>
 +
* <code>onConnect</code>, <code>onDisconnect</code>
 +
* <code>onStartDebugging</code>, <code>onStopDebugging</code>
 +
* <code>onCompilationUnit</code>
 +
 
 +
The BTI is used by [[Crossfire]]. Crossfire has the following structure:
 +
 
 +
    Crossfire Panel
 +
        ↓
 +
    Crossfire Module
 +
        ↓
 +
    Crossfire Server
 +
        ↓
 +
    Crossfire Socket Transport
 +
 
 +
When starting up it does the following.
 +
 
 +
# Register tools (e.g. console, inspector, ...)
 +
# Start server
 +
# Create socket transport
 +
# Add BTI listener
 +
# Set connection status
 +
 
 +
=== Script Panel ===
 +
The [[Script Panel]] sits at the top of the whole stack of layers/objects. It represents the debugger UI (a view + a controller). The implementation of this object is quite extensive since it also includes the source code view and viewport (see <code>[https://github.com/firebug/firebug/blob/3118f395a2e7d9845e663b1b6071a4b95be74c7f/extension/content/firebug/js/sourceBox.js firebug/js/sourceBox.js]</code> module).
 +
 
 +
The ''Script'' panel should never accesses the <code>Firebug.Debugger</code> directly. It should always use the <code>JavaScriptTool</code>.
 +
 
 +
So, for example, if the user clicks on the Breakpoint Column, the action is handled by the ''Script'' panel, forwarded to the <code>JavaScriptTool</code>, which forwards it to the <code>Firebug.Debugger</code>, which forwards it to <code>FBS</code>. Finally, <code>FBS</code> is using JSD1 API to set the breakpoint.
 +
 
 +
There are side panels ([[Breakpoints Side Panel|Breakpoints]], [[Watch Side Panel|Watch]] and [[Stack Side Panel|Stack]]), which are synchronized automatically with the ''Script'' panel. They should all use <code>JavaScriptTool</code> to get appropriate data to display.
 +
 
 +
The synchronization happens through basic Firebug mechanisms like <code>updateSelection</code> and <code>updateLocation</code>.
 +
 
 +
== New Debugger Architecture ==
 +
=== Panels ===
 +
The ''Script'' panel needs to be built on top of JSD2, the Remote Debugging Protocol (RDP) and Firebug remoting API. Remoting is already supported by the [[HTTP Monitor]].
 +
 
 +
First of all take a look a the following diagram:
 +
 
 +
[[Image:New-script-panel-architecture.png]]
 +
 
 +
* '''DebuggerClient''' This object comes from the Firefox platform and is responsible for the connection to the server back-end. The object maintains the underlying transport layer, it's responsible for tab attaching/detaching, it implements basic packets send and receive logic. The implementation is currently within ''resource://gre/modules/devtools/dbg-client.jsm'' module.
 +
 
 +
* '''DebuggerClientModule''' This module (singleton, one per Firebug instance) represents a wrapper around ''DebuggerClient''. It implements the connection setup and initialization (e.g. port number). This object represents the ''connection'' in Firebug and should be shared by all ''remotable'' tools (e.g. the Script panel, Net panel, Profiler, etc.). The name of this module probably isn't descriptive enough, so it could be changed. This object (or a related object) could also replace the ''TabWatcher''.
 +
 
 +
* '''DebuggerTool''' This object implements an API related to debugging (typical methods: <code>stepIn</code>, <code>stepOut</code>, <code>setBreakpoint</code>, etc.). The Script panel should always consume the debugger API through this object. ''DebuggerTool'' registers itself as a ''DebuggerClientModule'' and thus gets all necessary events from the back-end (e.g. <code>onConnect</code>, <code>onThreadAttached</code>, etc.)
 +
 
 +
* '''ScriptPanel''' Represents the debugger UI and listens mostly to <code>onStartDebugging</code> and <code>onStopDebugging</code> events. Those are used to properly update the panel's UI.
 +
 
 +
* '''ScriptView''' The main responsibility of this object is displaying the source code (using color syntax highlight). This object is based on CodeMirror.
 +
 
 +
* '''ThreadClient''' This object comes from the Firefox platform and is responsible for tab-thread related logic.
 +
 
 +
* '''CallstackPanel''' This panel displays the list of function call stack frames when the debugger is halted. The frames list is synchronized with the stack frame cache of the ''ThreadClient'' using <code>framesadded</code> and <code>framescleared</code> events.
 +
 
 +
* '''WatchPanel''' This panel displays the list of watch expresssions plus the available DOM properties of the current stack frame when the debugger is halted.
 +
 
 +
* '''BreakpointStore''' The responsibility of this object is the breakpoint persistence across Firefox restarts (in breakpoint.json file).
 +
 
 +
* '''BreakpointPanel''' This panel displays the list of set breakpoints.
 +
 
 +
=== Grips ===
 +
A grip represents a handle to a server side object (see [https://wiki.mozilla.org/Remote_Debugging_Protocol#Grips RDP] for more details).
 +
 
 +
The Firebug framework introduces several objects that deal with these handles. See the next diagram:
 +
 
 +
[[File:Grips-architecture.jpg]]
 +
 
 +
* '''GripFactory''' This is the place where grips instances are created. Every grip object is associated with a '''class''' (see RDP) and uses '''Firebug.registerGrip''' to register itself. This factory uses a list of registered types and instantiates the right one according to the server-side grip class attribute.
 +
 
 +
* '''GripCache''' The cache is the place where panels and other parts of Firebug should ask for grip-objects. If the requested object is not in the cache, a request is sent to the back-end and a promise synchronously returned and asynchronously resolved. If the object is already in the cache, a promise is returned and synchronously resolved. '''GripCache''' is the only place, which uses '''GripFactory'''. See more about [[Remoting Architecture#Asynchronous Data Access|asynchronous data access]].
 +
 
 +
* '''ObjectGrip''' Represents a wrapper for the '''RDP Grip'''
 +
 
 +
* '''Property''' Represents a wrapper for the '''RDP Property'''
 +
 
 +
* '''FunctionGrip''' Represents a wrapper for a function object.
 +
 
 +
* '''ScopeGrip''' Represents a wrapper for a scope object. This object is used by the [[Watch Side Panel|''Watch'' side panel]] to show a chain of the current scopes.
 +
 
 +
=== Breakpoints ===
 +
This section describes the architecture design for debugger breakpoints.
 +
 
 +
[[File:Breakpoints-architecture.png]]
 +
 
 +
* '''BreakpointStore''' The main component implementing a public API for breakpoint creation and removal. This object is also responsible for client-side persistence and initialization. There is one instance of this object per browser window (i.e. per Firebug). So, if a breakpoint is created in one context, it automatically appears in all contexts that are using the same URL.
 +
* '''FunctionMonitor''', '''ScriptPanel''', '''BreakpointPanel''', '''ConsolePanel''' These represent example objects that are dealing with breakpoints. There can be even more such objects in the future. All these objects use the API implemented by the '''BreakpointStore''' object. These objects also register themselves as ''DebuggerTool'' listeners to be notified about breakpoint related changes.
 +
* '''DebuggerTool''' is responsible for breakpoint creation/removal on the server side. '''DebuggerTool''' registers itself as '''BreakpointStore''' listener to be notified about breakpoint creation/removal and sync the server side (i.e. create or remove breakpoints on the server side). There is one instance of this object per context.
 +
 
 +
See an example of a breakpoint creation (using the Script panel UI):
 +
 
 +
[[File:Breakpoint-add-sequence.png]]
 +
 
 +
* The user clicks on the '''ScriptView''' breakpoint column. It's currently based on CodeMirror (previously it was based on Orion).
 +
* '''ScriptView''' notifies the '''ScriptPanel''' about the action.
 +
* '''ScriptPanel''' is using the '''BreakpointStore''' API to create a breakpoint. It calls <code>addBreakpoint</code>. Breakpoint creation is asynchronous, since the server side needs to be notified through TCP/IP. UI objects (e.g. panels) should wait for the <code>onBreakpointAdded</code> event to update themselves.
 +
* '''BreakpointStore''' sends an <code>onAddBreakpoint</code> event to all registered listeners. These listeners are mostly all '''DebuggerTool''' instances (one per context) and also all UI objects that want to display a temporary "waiting" icon at the place where the breakpoint will (asynchronously) appear.
 +
* '''DebuggerTool''' uses its associated '''ThreadClient''' to create a breakpoint on the server side. Each '''DebuggerTool''' instance will do this for its context. This way all contexts will be updated.
 +
* '''ThreadClient''' sends a packet to the server side and waits for the asynchronous response.
 +
* '''DebuggerTool''' has a direct (function) callback that is executed as soon as the response comes back from the server. The breakpoint's location is auto-corrected at this point if needed, in case it is set at a line that doesn't contain executable code.
 +
* '''DebuggerTool''' notifies all registered listeners (usually panels coming from the same context) about the added breakpoint. It sends an <code>onBreakpointAdded</code> event to the ''ScriptPanel'' in this scenario.
 +
* '''ScriptPanel''' calls <code>updateBreakpoint</code>, so the '''ScriptView''' can update itself, i.e. remove the "waiting" icon and display the new breakpoint at the right location. It might also scroll to make sure that auto-corrected location is visible to the user.
 +
 
 +
==== Breakpoint Condition Editor ====
 +
Let's yet see a sequence of actions describing what happens if the '''BreakpointConditionEditor''' is opened at a line with no breakpoint. In such case a breakpoint needs to be automatically created and used if the user actually creates a condition.
 +
 
 +
[[File:Breakpoint-condition-editor.png]]
 +
 
 +
* '''onGutterClick''' The user right clicks on the breakpoint column
 +
* '''startBreakpointConditionEditor''' An event is sent to the '''ScriptPanel'''. Note that the '''ScriptView''' communicates with the '''ScriptPanel''' only through events.
 +
* A helper '''initializeBreakpointConditionEditor''' is executed. This method creates a temporary BP structure with the condition set to an empty string.
 +
* The '''initializeBreakpoint''' method is called to display the breakpoint "waiting" icon. The temporary BP is passed into it.
 +
* The '''ScriptView''' sends an <code>addBreakpoint</code> event back to the '''ScriptPanel'''. This event is faked and mimicking a click on the breakpoint column.
 +
* The '''ScriptPanel''' uses the '''BreakpointStore''' to add a breakpoint just like it always does if the user clicks on the breakpoint column to create (or remove) a breakpoint, but it passes the condition to it, so the breakpoint is not stored immediately (kind of a hack).
 +
* The '''BreakpointStore''' sends an <code>onAddBreakpoint</code> event to all listeners, i.e. to all '''DebuggerTool''' instances.
 +
* The '''DebuggerTool''' sets the breakpoint on the server side. We need to do this before displaying the '''BreakpointConditionEditor''', because we need to know the correct line. Note that the user could have clicked on a non-executable line.
 +
* A <code>onBreakpointAdded</code> event is sent from the '''DebuggerTool''' to the '''ScriptPanel''' and the condition editor can be finally displayed to the user by calling <code>openBreakpointConditionEditor</code>.
 +
 
 +
''Note: The entire feature is currently wired to the current breakpoint architecture. It should be rather implemented as an independent module.''

Revision as of 22:39, 30 August 2013

This page summarizes refactoring the Firebug debugger to use JSD2 and adding support for remote JavaScript debugging. Issue 5421 tracks the actual changes done.

Contents

Resources

Firebug Wiki:

Mozilla Wiki:

Goals

  • Adopt JSD2 (get rid of all JSD1 APIs)
  • Support remote debugging

Related Issues

Related Mozilla Issues

JSD2 API Requirements

The following list summarizes the high-level features that Firebug needs to support. It should be verified that all the features can be re-implemented on top of JSD2 before starting the refactoring.

Not Ready in JSD2

  • There is not source for evals.
  • new Function scripts?
    • As soon as this one is fixed
    • Profiling is unrelated to JSD2. What is the plan here?
    • Tracking (break on) throw/catch/error; also what is the plan?

Current Debugger Architecture

This section describes the current debugger architecture.

Current-debugger-architecture.png

There are several layers/objects, so let's describe them step by step starting from the bottom.

  • Backend: JSD1, FirebugService, Debugger
  • Frontend: JavaScriptTool, ScriptPanel

JSD1

This layer represents the JSD1 platform API. This API allows to implement script debuggers and represents a direct competition to the JSD2 API. Of course this layer should entirely disappear and should be replaced by JSD2.

Firebug Service

The Firebug Service is implemented as JavaScript module on top of the JSD1 layer. The object is called FBS and its purpose is to wrap the JSD1 APIs, so they are not directly accessed anywhere else. This layer also maintains a list of registered debuggers (usually the Firebug.Debugger module) and fires various events to them (e.g. onToggleBreakpoint, onToggleMonitor, etc.) or execute their callback methods (e.g. onBreak, onFunctionCall, onError, onThrow, onScriptCreated, etc.).

Main responsibilities:

  • Activate/deactivate JSD in the browser. The activation is global for all current browser windows.
  • Hook debugger events (interrupts, break on a breakpoint, etc.)
  • Maintains a list of registered debuggers (there is usually just one - Firebug debugger) and sends events to them.
  • Manages the nested event loop that is created for the debugger UI when a page's JS execution breaks.
  • Sets/removes/enables/disables/saves/loads breakpoints
  • Implements debugger stepping over/in/out/runUntil
  • Monitors function calls
  • Starts/stops profiling
  • Tracks exceptions and errors (not working well)
  • Tracks compiled scripts (not all of them)

Debugger

This object is derived from Firebug.Module and represents Firebug's debugger. It's also registered as a debugger into the Firebug Service (FBS). This object should be the only one accessing FBS. It calls FBS API and receives various callbacks and events.

This object implements methods that can be used by the Script Panel or other parts of Firebug (e.g. by those panels which implements [[Script Panel#Break On Next|BON]).

Some examples of the API:

  • evaluate, evaluateInCallingFrame
  • breakNow, getCurrentStackTrace
  • rerun
  • stepOver, stepInto, stepOut, runUntil, <code>resume
  • setBreakpoint, clearBrakpoint, etc.
  • monitorFunction, unmonitorFunction
  • monitorScript, unmonitorScript
  • activateDebugger, deactivateDebugger

The implementation of these methods is based on FBS API.

There is also Firebug.DebuggerListener that defines the interface used by FBS

  • onStop, onResume, onThrow, onError, onScriptCreated (there are more onScriptCreated events)

This object is observable and it activates the underlying JSD only if an observer exists. The observer is currently the Script panel even when it would make a bit more sense if it's actually the JavaScriptTool.

JavaScriptTool

This object is part of the Browser Tools Interface (BTI) and represents a JavaScript debugger tool. This object is like a proxy sitting between Firebug.Debugger and the ScriptPanel. The current implementation is in-process only, so based on direct Firebug.Debugger API calls.

API of the tool is as follows:

  • setBreakpoint, clearBreakpoint, enableBreakpoint, disableBreakpoint, isBreakpointDisabled, getBreakpointCondition
  • rerun
  • resumeJavaScript, stepOver, stepInto, stepOut, runUntil
  • onConnect, onDisconnect
  • onStartDebugging, onStopDebugging
  • onCompilationUnit

The BTI is used by Crossfire. Crossfire has the following structure:

   Crossfire Panel
       ↓
   Crossfire Module
       ↓
   Crossfire Server
       ↓
   Crossfire Socket Transport

When starting up it does the following.

  1. Register tools (e.g. console, inspector, ...)
  2. Start server
  3. Create socket transport
  4. Add BTI listener
  5. Set connection status

Script Panel

The Script Panel sits at the top of the whole stack of layers/objects. It represents the debugger UI (a view + a controller). The implementation of this object is quite extensive since it also includes the source code view and viewport (see firebug/js/sourceBox.js module).

The Script panel should never accesses the Firebug.Debugger directly. It should always use the JavaScriptTool.

So, for example, if the user clicks on the Breakpoint Column, the action is handled by the Script panel, forwarded to the JavaScriptTool, which forwards it to the Firebug.Debugger, which forwards it to FBS. Finally, FBS is using JSD1 API to set the breakpoint.

There are side panels (Breakpoints, Watch and Stack), which are synchronized automatically with the Script panel. They should all use JavaScriptTool to get appropriate data to display.

The synchronization happens through basic Firebug mechanisms like updateSelection and updateLocation.

New Debugger Architecture

Panels

The Script panel needs to be built on top of JSD2, the Remote Debugging Protocol (RDP) and Firebug remoting API. Remoting is already supported by the HTTP Monitor.

First of all take a look a the following diagram:

New-script-panel-architecture.png

  • DebuggerClient This object comes from the Firefox platform and is responsible for the connection to the server back-end. The object maintains the underlying transport layer, it's responsible for tab attaching/detaching, it implements basic packets send and receive logic. The implementation is currently within resource://gre/modules/devtools/dbg-client.jsm module.
  • DebuggerClientModule This module (singleton, one per Firebug instance) represents a wrapper around DebuggerClient. It implements the connection setup and initialization (e.g. port number). This object represents the connection in Firebug and should be shared by all remotable tools (e.g. the Script panel, Net panel, Profiler, etc.). The name of this module probably isn't descriptive enough, so it could be changed. This object (or a related object) could also replace the TabWatcher.
  • DebuggerTool This object implements an API related to debugging (typical methods: stepIn, stepOut, setBreakpoint, etc.). The Script panel should always consume the debugger API through this object. DebuggerTool registers itself as a DebuggerClientModule and thus gets all necessary events from the back-end (e.g. onConnect, onThreadAttached, etc.)
  • ScriptPanel Represents the debugger UI and listens mostly to onStartDebugging and onStopDebugging events. Those are used to properly update the panel's UI.
  • ScriptView The main responsibility of this object is displaying the source code (using color syntax highlight). This object is based on CodeMirror.
  • ThreadClient This object comes from the Firefox platform and is responsible for tab-thread related logic.
  • CallstackPanel This panel displays the list of function call stack frames when the debugger is halted. The frames list is synchronized with the stack frame cache of the ThreadClient using framesadded and framescleared events.
  • WatchPanel This panel displays the list of watch expresssions plus the available DOM properties of the current stack frame when the debugger is halted.
  • BreakpointStore The responsibility of this object is the breakpoint persistence across Firefox restarts (in breakpoint.json file).
  • BreakpointPanel This panel displays the list of set breakpoints.

Grips

A grip represents a handle to a server side object (see RDP for more details).

The Firebug framework introduces several objects that deal with these handles. See the next diagram:

Grips-architecture.jpg

  • GripFactory This is the place where grips instances are created. Every grip object is associated with a class (see RDP) and uses Firebug.registerGrip to register itself. This factory uses a list of registered types and instantiates the right one according to the server-side grip class attribute.
  • GripCache The cache is the place where panels and other parts of Firebug should ask for grip-objects. If the requested object is not in the cache, a request is sent to the back-end and a promise synchronously returned and asynchronously resolved. If the object is already in the cache, a promise is returned and synchronously resolved. GripCache is the only place, which uses GripFactory. See more about asynchronous data access.
  • ObjectGrip Represents a wrapper for the RDP Grip
  • Property Represents a wrapper for the RDP Property
  • FunctionGrip Represents a wrapper for a function object.
  • ScopeGrip Represents a wrapper for a scope object. This object is used by the Watch side panel to show a chain of the current scopes.

Breakpoints

This section describes the architecture design for debugger breakpoints.

Breakpoints-architecture.png

  • BreakpointStore The main component implementing a public API for breakpoint creation and removal. This object is also responsible for client-side persistence and initialization. There is one instance of this object per browser window (i.e. per Firebug). So, if a breakpoint is created in one context, it automatically appears in all contexts that are using the same URL.
  • FunctionMonitor, ScriptPanel, BreakpointPanel, ConsolePanel These represent example objects that are dealing with breakpoints. There can be even more such objects in the future. All these objects use the API implemented by the BreakpointStore object. These objects also register themselves as DebuggerTool listeners to be notified about breakpoint related changes.
  • DebuggerTool is responsible for breakpoint creation/removal on the server side. DebuggerTool registers itself as BreakpointStore listener to be notified about breakpoint creation/removal and sync the server side (i.e. create or remove breakpoints on the server side). There is one instance of this object per context.

See an example of a breakpoint creation (using the Script panel UI):

Breakpoint-add-sequence.png

  • The user clicks on the ScriptView breakpoint column. It's currently based on CodeMirror (previously it was based on Orion).
  • ScriptView notifies the ScriptPanel about the action.
  • ScriptPanel is using the BreakpointStore API to create a breakpoint. It calls addBreakpoint. Breakpoint creation is asynchronous, since the server side needs to be notified through TCP/IP. UI objects (e.g. panels) should wait for the onBreakpointAdded event to update themselves.
  • BreakpointStore sends an onAddBreakpoint event to all registered listeners. These listeners are mostly all DebuggerTool instances (one per context) and also all UI objects that want to display a temporary "waiting" icon at the place where the breakpoint will (asynchronously) appear.
  • DebuggerTool uses its associated ThreadClient to create a breakpoint on the server side. Each DebuggerTool instance will do this for its context. This way all contexts will be updated.
  • ThreadClient sends a packet to the server side and waits for the asynchronous response.
  • DebuggerTool has a direct (function) callback that is executed as soon as the response comes back from the server. The breakpoint's location is auto-corrected at this point if needed, in case it is set at a line that doesn't contain executable code.
  • DebuggerTool notifies all registered listeners (usually panels coming from the same context) about the added breakpoint. It sends an onBreakpointAdded event to the ScriptPanel in this scenario.
  • ScriptPanel calls updateBreakpoint, so the ScriptView can update itself, i.e. remove the "waiting" icon and display the new breakpoint at the right location. It might also scroll to make sure that auto-corrected location is visible to the user.

Breakpoint Condition Editor

Let's yet see a sequence of actions describing what happens if the BreakpointConditionEditor is opened at a line with no breakpoint. In such case a breakpoint needs to be automatically created and used if the user actually creates a condition.

Breakpoint-condition-editor.png

  • onGutterClick The user right clicks on the breakpoint column
  • startBreakpointConditionEditor An event is sent to the ScriptPanel. Note that the ScriptView communicates with the ScriptPanel only through events.
  • A helper initializeBreakpointConditionEditor is executed. This method creates a temporary BP structure with the condition set to an empty string.
  • The initializeBreakpoint method is called to display the breakpoint "waiting" icon. The temporary BP is passed into it.
  • The ScriptView sends an addBreakpoint event back to the ScriptPanel. This event is faked and mimicking a click on the breakpoint column.
  • The ScriptPanel uses the BreakpointStore to add a breakpoint just like it always does if the user clicks on the breakpoint column to create (or remove) a breakpoint, but it passes the condition to it, so the breakpoint is not stored immediately (kind of a hack).
  • The BreakpointStore sends an onAddBreakpoint event to all listeners, i.e. to all DebuggerTool instances.
  • The DebuggerTool sets the breakpoint on the server side. We need to do this before displaying the BreakpointConditionEditor, because we need to know the correct line. Note that the user could have clicked on a non-executable line.
  • A onBreakpointAdded event is sent from the DebuggerTool to the ScriptPanel and the condition editor can be finally displayed to the user by calling openBreakpointConditionEditor.

Note: The entire feature is currently wired to the current breakpoint architecture. It should be rather implemented as an independent module.

Personal tools