You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.6 KiB


Guide to the code

The control flow starts from chrome/bootstrap.js where we specify window scripts like pepmessengercompose.js and pepmsghdrview.js. These scripts are executed when Thunderbird opens a window with the matching type.

These "window scripts" are in in chrome/content like most of the code. When executed they import other modules with ChromeUtils.import, passing the window and other global objects if needed. Outside of this context we cannot assume that window is accessible.

The three-pane window is always running when Thunderbird is running, so its pepmsghdrview.js script performs most of the application initialisation. One of the main objects to be initialised is the controller within chrome/content/p4tb.js.

The window object

In bootstrap.js we subscribe to notifications from a ww service that calls a paint method when the window object is globally accessible as a global variable, for every open window.

From there we can pass it to all modules that want to access it. Outside of this context we cannot assume that window is accessible. An unpaint notification will be sent when the window is closed.

pEpController and p4tb.js

The pEpController is a module defined in chrome/content/modules/pEp.js and is meant to be independent from Thunderbird. Within p4tb.js, the controller gets augmented with Thunderbird-specific logic.

p4tb.js also contains most of the initialisation logic. There the server gets a detectJsonAdapter which populates the server.connectionInfo on initialisation.

Note that this initialisation sequence is confined to the window where the script has run, so it's executed again for every message composition window when the matching pepmessengercompose.js script is executed.

Types of modules

Above we mentioned the pEpController module. Here we have mainly two types of modules:

  • ChromeUtils modules
  • ChromeUtils & Node modules

Most files in chrome/content define a module of the first type. The files in chrome/content/modules are ChromeUtils and Node modules, so they can be tested with our unit tests.

Our Node modules cannot import other Node modules because then they wouldn't work as ChromeUtils modules. These modules can be imported once and that's it. They can be combined with each other using dependency injection.

Encryption & Decryption

Encryption and decryption are performed via components and factories defined in pEpMimeEncrypt.js and pEpMimeDecrypt.js (branch P4TB-43-2 to be merged).

We define component objects and wrap them with factories that get registered on startup and deregistered on teardown. You might want to deregister factories for testing purposes, since only one factory can be registered for every component id.

Component objects are created in isolation when some events happen, for example the encryption component is created when there is an outgoing message. Since they have this ad-hoc, isolated lifecycle, communication between our components and our views is not straightforward.

Other notes

Environment setup

We suggest to use different profiles, one for your regular mail use and one for extension development.

Testing can be done using one of the test accounts (need VPN access).

Relevant entry points to the engine code

Most of the extension code is written under the assumption that the extension developer can read the engine code as a form of documentation and keep the extension synchronised with the engine's (and adapter's) interfaces.

Many interfaces are defined in the following engine's files:

  • pEpEngine.h
  • message_api.h
  • message.h
  • pEp_internal.h

After that, grep is your friend/enemy (darthmama's quote)


The extension connects to the JSON adapter server using a token which is found in ~/.pEp/json-token. The whole purpose of the token is to be sure that the client runs with the same user rights than the server (Roker's quote).

Updating the options

When we want to change the plugin preferences we can do so by modifying different areas in the code:

  • options.xul the markup, here you define the identifier for the option that will be used also elsewhere
  • prefsFactory.js update getter and setter functions corresponding to the option and the default value
  • options.js update addAll, the window load handler, the dialogAccept handler
  • bootstrap.js update the defaults

Compat and Prefs

Are defined in options.js and globally available