This page is intended to summarize basic architecture concepts for Firebug (and Firebug extensions) remote capabilities (such as remote debugging or remote HTTP monitoring).
The proxy object represents a connection between a panel (front end) and a module (back end).
The proxy has essentially two implementations:
- Local - direct API calls
- Remote - communication over TCP/IP protocol
- Panel (a view) should never use Module (a service) directly
- The communication should always be done through the current proxy
- Each Panel should have its own proxy that implements API of the remote module (service)
- There can be also proxy objects used by non-panel objects, e.g. the Profiler
- It should be possible to create new proxies in extensions.
- There should be also something like a default (or global) proxy that can be used for things like getting list of tabs on the remote browser
Here is a more detailed diagram with local and remote proxies.
- Panel object at the top is referencing its proxy object
- The proxy implementation can be local or remote
- Local proxy uses direct API calls (in process) and asynchronous callbacks
- Remote proxy uses TCP/IP connection to communicate with the back-end using JSON packets
- The remote communication is based on RDP
- The server side implements Actor, which is responsible for communication with specific proxy
- The actor is finally consuming back-end Module (service) API
Connection object is responsible for sending properly setup packets to the server and handling all response packets. This object is also responsible for handling any errors (or error packets) that can occur during the communication.
- Remote proxies should always use the connection object to send JSON packets
- The connection to the backend is currently maintained by DebuggerClient object. This object comes from built-in dbg-client.jsm module.
- DebuggerClient object is accessible as Firebug.debuggerClient.
Firebug also needs its server side implementation that will be used if Firebug is running in a server mode (on the remote browser). In such cases only back-end services like: NetMonitor or Debugger modules will be loaded and activated. UI overlays (i.e. browserOverlay.js) shouldn't be loaded at all since it isn't necessary and e.g. Fennec UI doesn't even have expected XUL elements to be overlayed.
Individual services should be always loaded on-demand at the time when appropriate actor is requested by the client side. For example, when the client side requests a net actor then NetMonitor module should be loaded and activated.
- Only actors are registered on the server side by default
- Actors are responsible for loading and activating back-end services
- Extensions should be able to create and register custom actors
- In order not to delay server side browser start-up time, we should delay loading of AMD modules till they are really requested by actors (e.g. the Net and Script panels don't have to be even enabled).
Firebug TabWatcher is responsible for watching browser tabs events (open, close, switch) and firing further events to make sure that Firebug UI is properly updated and showing information related to the current page.
TabWatcher is clearly a server side component and as such it also needs a proxy. In case of JSD2 some events like tabNavigate might be server by API built-in in the platform.
BTI is implementing TabWatchListener (see browser.js file), which is listening to TabWatcher and forwarding all events to Firebug infrastructure. This object should be reused or removed not duplicated.
- Local TabWatcher must be disabled when Firebug is connected to a remote browser instance
This section describes the current BTI (Browser Tools Interface) concepts. These concepts should be reviewed and refactored as necessary.
WebApp represents a browsing context. Browsing context is an environment in which Document objects are presented to the user. Instance of this object is associated with top level page window (it's on the same level as the context object).
This object doesn't implement any logic it's there only to demonstrate the concept.
 Compilation Unit
- list of breakpoints
- reference to the current context
 Asynchronous Data Access
One of the challenges for consuming JSD back-end API through RDP is picking the right way for asynchronous consuming of back-end data. This chapter is suggesting Asynchronous Data Providers to solve the problem.
Working prototype (a web application) showing asynchronously populated tree is available:
- Source https://github.com/janodvarko/prototypes/tree/master/async-provider
- Online http://janodvarko.cz/firebug/jsd2/prototypes/async-provider
The concept is based on:
- Viewer & Provider pattern
- Promise pattern
First of all, see the following diagram that explains how the Watch panel (child of the Scrip panel) is populated with data coming asynchronously from the back-and (as a set of grips) and cached on the client side (orange boxes represent the concept).
- The Watch panel update starts when a selection (or navigation) event is sent to it. It usually happens when JS execution is paused. Note that the current implementation of the panel displays the global object (window) if the execution is not paused so, it's never empty and useless.
- Evaluation of custom expression in the Watch panel is not covered in this scenario, but would be implemented using same technique.
- The Watch panel is essentially a tree and so, it embeds a Tree widget based on Domplate - DomTree. Set of domplate templates in this widget are responsible for HTML generation.
- The widget (a viewer) uses standard Provider interface to get data. See more about Data Providers.
- The provider uses a cache to get all objects. If an object is cached on the client side it's returned synchronously. If it isn't cached it needs to be fetched from the server and so, returns asynchronously.
- Promise object is returned by the cache in both cases (sync and async).
- Children objects are returned immediately, in the synchronous case.
- It's responsibility of the widget to update itself and re-request the object again from the provider. The object is already cached at that point and so, returned synchronously.
- Of course, the cache is part of the Context object that collects all data about the debugging target.