# About Thunderbird

## I Want to Start Hacking

This page has some information on how to get plugged into the Community. If you are ready to start hacking, head over to one of the following sections.

### [Contributing to Thunderbird](/thunderbird-development/getting-started)

Get all the information you need to set up your development environment and get ready to hack on Thunderbird. Note that all contributors are required to follow the [Community Participation Guidelines (CPG)](https://www.mozilla.org/en-US/about/governance/policies/participation/).

### [Add-on Development](/add-ons/about-add-ons)

If you'd like to learn to develop add-ons for Thunderbird, check out our add-on documentation with examples, tips and links to relevant resources.

## Report Bugs and Request Features

### [Bugzilla](https://bugzilla.mozilla.org)

Thunderbird uses Mozilla's Bugzilla platform to report and track bugs. The site can also be used to generate enhancement bugs, which can be used for feature requests. If you want to become a contributor to Thunderbird, you will need an account on Bugzilla as you will submit patches through this platform.

### [Profiling Thunderbird Performance](https://support.mozilla.org/kb/profiling-thunderbird-performance)

Creating a performance profile can be useful for developers to find the causes of high CPU load or slowness in Thunderbird.

### [Thunderbird on GitHub](https://github.com/thunderbird)

If you want to contribute to the Thunderbird website, this documentation, or addons.thunderbird.net - you can find those repositories and their issue trackers on the Thunderbird GitHub page. You'll need a GitHub account to contribute there.

## Getting Plugged into the Community

We have a complete listing of the ways in which you can get involved with Thunderbird [on our website](https://thunderbird.net/get-involved). Below are some quick references from that page that you can use if you are looking to contribute to Thunderbird core right away.

### Mailing Lists

If you want to participate in discussions about Thunderbird development, these are the main channels.

1. [**TB-Planning**](https://wiki.mozilla.org/Thunderbird/tb-planning)**:** This mailing list is higher level topics like: the future of Thunderbird, potential features, and changes that you would like to see happen. It is also used to discuss a variety of broader issues around community and governance of the project.
2. [**Topicbox**](https://thunderbird.topicbox.com/groups/developers)**:** A moderated mailing list for discussing engineering plans for Thunderbird. It is a place where you can raise questions and ideas for core Thunderbird development.
3. [**Add-on Developers**](https://thunderbird.topicbox.com/groups/addons): A list for Thunderbird add-on developers and aspiring add-on developers to ask questions and share knowledge.

### Chat

If you want to ask questions in real-time about how to hack on Thunderbird, you can join our development chat channel at [#maildev:mozilla.org](https://chat.mozilla.org/#/room/#maildev:mozilla.org)


# Getting Started Contributing

New contributor guide on how to contribute in various ways to Thunderbird.

## Getting Started

There are many ways to get involved in Thunderbird. You don't need to be a highly experienced software developer to be part of our welcoming contributor community. There are ways that anyone can participate so find an area that works for you and dive right in!

### Improving documentation

Perfect documentation doesn't exist and there is always room for improvement. If you read through any of our documentation and notice a typo, or something that is unclear, or some section you feel is missing.. feel free to add it or tell us about it.

* [**Source docs**](https://source-docs.thunderbird.net) - Technical documentation on the Thunderbird source code, code architectures, etc. This set of documentation lives alongside the source code so in order to submit changes to it, you'll need a bugzilla account and patches are submitted to phabricator. [Read more about contributing to these docs here](https://source-docs.thunderbird.net/en/latest/documentation/index.html).
* [**Developer docs**](https://developer.thunderbird.net/) - Developer documentation on non-code topics like how to build Thunderbird, the project's roadmap, add-ons, etc. This [developer documentation lives in GitHub](https://github.com/thunderbird/developer-docs/) so in order to submit changes to it, you'll need a GitHub account and submit a pull request ([learn how to submit a GitHub pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
* [**User docs**](https://support.mozilla.org/en-US/) - User support documentation for all Mozilla products, including Thunderbird and Thunderbird for Android. This set of documentation is modified using Markup, directly in the website. [Read more about contributing to knowledge base articles](https://support.mozilla.org/en-US/kb/writing-guide-knowledge-base-articles?redirectslug=support-document-guide).

### User support

Users of all levels will consult the vast set of [Thunderbird knowledge base articles](https://support.mozilla.org/en-US/products/thunderbird). This user documentation location is a great place to learn, start discussions on the forum, or ask a question.

You can help by watching this space to answer questions. If you encounter a topic that is beyond your knowledge but you want to learn more, you can always [join us on Matrix](https://matrix.to/#/#thunderbird:mozilla.org) to access the Thunderbird staff to assist you.

### Beta testing

We always need more beta testers to let us know if something is not working (by filing bugzilla issues) before we release a given version. This is a great place to start contributing if you're not sure where to jump in.

Every month we have a new beta release and a new monthly release ([read more about the release cadence here](https://developer.thunderbird.net/releases/cadence)) so it is important to catch new issues quickly.

Read more about [running Thunderbird beta on our knowledge base article](https://support.mozilla.org/en-US/kb/thunderbird-beta).

### Filing bugs

If you are using any version of Thunderbird and you encounter something you feel is not working properly, let us know in a bugzilla issue.

1. [Search if your issue already exists](https://bugzilla.mozilla.org/query.cgi?format=specific) (select Thunderbird as the product). This helps us minimize the number of duplicate issues filed. If you find that your issue has already been filed by someone else, it is very helpful to us if you just take a quick look at the issue and add a comment that adds any additional information about your issue that might help us solve the problem.
2. If your issue has not been reported yet, [file a new bugzilla issue](https://bugzilla.mozilla.org/enter_bug.cgi?product=Thunderbird).

### Triaging bugs

When a new bug is filed, it's status is UNCONFIRMED. This means the issue has not been triaged (verified that the bug in fact does exist) by someone else other than the bug reporter. If an issue exists only for one person (i.e. the bug reporter), the problem is likely localized to that person's environment in some way and is not an actual Thunderbird code issue. That is why it is very helpful for another person (i.e. yourself) to see if the issue can be reproduced and make sure the issue has **reproducer steps** and **information about the environment** where it was reproduced (version of Thunderbird, operating system, etc.).

[Here is a list of UNCONFIRMED bugs that need triaging](https://bugzilla.mozilla.org/buglist.cgi?product=Thunderbird\&component=Untriaged\&resolution=---\&list_id=17499226).

The number of people triaging new bugs is low and that makes it hard to keep up with the new incoming bugs every day. Your help would be greatly appreciated!

### Code patches

If you want to fix a bug or add a feature, the way to do it is through direct code contributions.

[Read more about fixing bugs here](https://developer.thunderbird.net/thunderbird-development/fixing-a-bug).

Note that if you want to add a feature, it's best to first have a discussion about it with the Thunderbird developers to make sure that it fits with the project's direction. The best place for these discussions is [the developer mailing list on Topicbox](https://thunderbird.topicbox.com/groups/developers).

## I'm stuck and need help!

If you are trying to contribute in any way and find yourself stuck and not sure how to proceed, please [join us on Matrix](https://chat.mozilla.org/#/room/#maildev:mozilla.org) and tell us about it so we can help you.


# Setting Up A Build Environment

This page has all the information you need to get your development environment set up and ready to hack on Thunderbird.

## Build Prerequisites

Before you can build Thunderbird, please follow your platform's build prerequisites page:

* [Windows Build Prerequisites](/thunderbird-development/building-thunderbird/windows-build-prerequisites)
* [Linux Build Prerequisites](/thunderbird-development/building-thunderbird/linux-build-prerequisites)
* [macOS Build Prerequisites](/thunderbird-development/building-thunderbird/macos-build-prerequisites)

## General Information

### Git Version Control

Mozilla uses the [Git version control](https://git-scm.com) software to propose, review, incorporate, and log changes to its code. In order to contribute to Thunderbird, you will need to be able to use this software.

Information for how to install Git is available [via the download page on their wiki](https://git-scm.com/install).

### Source Code

#### Firefox and Thunderbird

The latest Firefox and Thunderbird source code is available from the main branches of the [firefox](https://github.com/mozilla-firefox/firefox) and [thunderbird-desktop](https://github.com/thunderbird/thunderbird-desktop) Git repositories, respectively.

When building locally:

* The root of the Firefox source is often renamed to `source/` but it can be named anything you like.
* The root of the Thunderbird source needs to be placed **inside** the Firefox source code, in a directory that must be named `comm/`.

More detailed can be found on the [Building Thunderbird](/thunderbird-development/building-thunderbird) page.

#### Git and Mercurial

All Firefox and Thunderbird source code development today is performed with Git. However, Mercurial is still used behind the scenes for CI/CD.

Prior to moving to Git, the source code for Firefox and Thunderbird used Mercurial for version control. The Firefox and Thunderbird source code that now lives in the `main` Git branches was historically stored in repositories named mozilla-central and comm-central.

The following table shows the mapping of git branches to mercurial repositories. Note that all of the git branches exist in the [firefox](https://github.com/mozilla-firefox/firefox) and [thunderbird-desktop](https://github.com/thunderbird/thunderbird-desktop) repositories.

| Git Branch | Mercurial Repository |
| ---------- | -------------------- |
| main       | mozilla-central      |
| beta       | mozilla-beta         |
| release    | mozilla-release      |
| esr140     | mozilla-esr140       |
| main       | comm-central         |
| beta       | comm-beta            |
| release    | comm-release         |
| esr140     | comm-esr140          |

For the time being, all CI/CD is run against Mercurial, until that tooling can be migrated to Git branches. You'll notice this on [Treeherder](https://treeherder.mozilla.org/jobs?repo=comm-central).

When code lands in the Firefox and Thunderbird Git repositories, it is mirrored to the corresponding Mercurial repository in order to run CI against it.

### Additional Documentation

Thunderbird is built on the Mozilla platform, the same base that Firefox is built from. As such the two projects share a lot of code and much of the documentation for one will apply, in many ways, to the other. If at any point you are looking for answers that you can't find here, here are some additional useful resources:

* [Thunderbird Source Tree Documentation](https://source-docs.thunderbird.net)
* [Firefox Source Tree Documentation](https://firefox-source-docs.mozilla.org/)
* [Mozilla's Developer site called: **MDN**](https://developer.mozilla.org)

## What's Next

If you have already gone through the relevant build prerequisite steps, then let's build the latest Thunderbird:

{% content-ref url="/pages/-La1fY-i3p06eOeTGUwH" %}
[Building Thunderbird](/thunderbird-development/building-thunderbird)
{% endcontent-ref %}


# Building Thunderbird

How to build and run Thunderbird.

## Hardware Requirements

* At least **4 GB of RAM**. 8 GB or more is recommended. While you can build Thunderbird on older hardware it can take quite a bit of time to compile on slower machines with less RAM.
* 30 GB of free space. The Thunderbird build can use up to 30-40GB of disk space to complete depending on your operating system.
* Good internet connection for the initial source download.

## Build Prerequisites

Depending on your Operating System you will need to carry out a different process to prepare your machine. So first complete the instructions for your OS and then continue following these build instructions.

* [Windows Build Prerequisites](/thunderbird-development/building-thunderbird/windows-build-prerequisites)
* [Linux Build Prerequisites](/thunderbird-development/building-thunderbird/linux-build-prerequisites)
* [macOS Build Prerequisites](/thunderbird-development/building-thunderbird/macos-build-prerequisites)

## Build Configuration

To build Thunderbird, you need a file named `mozconfig` in the root directory of the Firefox checkout that contains the option `comm/mail` enabled. If you do not already have this file, then you can create it with this line by doing this in the `source/` directory:

```
echo 'ac_add_options --enable-project=comm/mail' > mozconfig
```

**If you omit this line, the build system will build Firefox instead**.

Other build configuration options can be added to this file, although it's recommended that you only use options that you fully understand. Here are come recommended options to use and why:

* To create a debug build instead of a release build: `ac_add_options --enable-debug`
* To speed up subsequent builds by caching compilation results for both C++ and Rust: `ac_add_options --with-ccache=sccache`
* To enable some additional checks we have enabled in the CI: `ac_add_options --enable-clang-plugin`
* To enable debug symbols: `ac_add_options --enable-debug-symbols`
* To enable autoclobber: `mk_add_options AUTOCLOBBER=1`

*Each of these ac\_add\_options entries needs to be on its own line.*

For more info on configuration options, see the page [Configuring build options](https://firefox-source-docs.mozilla.org/setup/configuring_build_options.html#configuring-build-options). Note that if you use a MOZ\_OBJDIR it cannot be a sibling folder to your source directory. Use an absolute path to be sure!

## Building

{% hint style="warning" %}
Before you start, make sure that the version you checked out is not busted. For tip, you should see green Bs on <https://treeherder.mozilla.org/#/jobs?repo=comm-central>
{% endhint %}

After you have met the [build prerequisites](#build-prerequisites) for your OS, the build is started in the `source` directory with:

```
./mach build
```

mach is our command-line tool to streamline common developer tasks. See the [mach](https://firefox-source-docs.mozilla.org/mach/) article for more.

Building can take a significant amount of time, depending on your system, OS, and chosen build options. Linux builds on a fast box may take under *15 minutes*, but Windows builds on a slow box may take *several hours*.

### Make Your Build Faster

Follow this guide to rely on [sccache](https://firefox-source-docs.mozilla.org/setup/configuring_build_options.html?highlight=sccache#sccache) and other [Tips for making builds faster](https://firefox-source-docs.mozilla.org/build/buildsystem/slow.html#why-the-build-system-is-slow).

## Running Thunderbird

To run your build, you can use:

```
./mach run
```

There are various command line parameters you can add, e.g. to specify a profile.

Various temporary files, libraries, and the Thunderbird executable will be found in your object directory (under `comm/`), which is prefixed with `obj-`. The exact name depends on your system and OS. For example, a Mac user may get an object directory name of `obj-x86_64-apple-darwin10.7.3/`.

The Thunderbird executable in particular, and its dependencies are located under the `dist/bin` folder under the object directory. To run the executable from your `comm` working directory:

* Windows: `obj-.../dist/bin/thunderbird.exe`
* Linux: `obj-.../dist/bin/thunderbird`
* macOS: `obj-.../dist/Daily.app/Contents/MacOS/thunderbird`

## Update and Build Again

To pull down the latest changes, in the Firefox directory run the following commands:

```
git pull --rebase
cd comm
git pull --rebase
cd ..
```

or to do it via one command:

```
git pull --rebase && git -C comm pull --rebase
```

Then just run the `./mach build` command detailed in the [Building](#building) instructions above. This will only recompile files that changed, but it may still take a long time.

## Rebuilding

To build after changes you can simply run:

```
./mach build
```

### Rebuilding Specific Parts

If you have made many changes, but only want to rebuild specific parts, you may run the following commands.

#### C or C++ Files:

```
./mach build binaries
```

#### JavaScript or XUL Files (Windows Only):

```
./mach build path/to/dir
```

{% hint style="info" %}
Replace `path/to/dir` with the directory with the files changed.

This is the tricky bit since you need to specify the directory that installs the files, which may be a parent directory of the changed file's directory. For example, to just rebuild the Lightning calendar extension:

```
./mach build comm/calendar/lightning
```

{% endhint %}

You can also run a build that does everything except compile (C++/Rust/IDL/Glean) and link code (essentially the same as what an artifact build would do) with

```
./mach build faster
```


# Windows Build Prerequisites

This page has all the information you need to get your Windows development environment set up and ready to hack on Thunderbird.

## The Basics

### 64-bit Windows

You will need to be running a 64-bit version of Windows in order to build Thunderbird. To check this in Windows 10, open the start menu and click on the gear icon on the left-hand side of the menu. This will open up the "settings" window. Click on the "System" option and then scroll down to "About". Click on the "About" option and on the new screen next to "System Type" you should see: "**64-bit operating system"**

![How System type appears in the About Settings page.](/files/-Lc7jdLaV3lNjuQenMDt)

### **Visual Studio**

In order to get the necessary libraries in order to build Thunderbird, you will need to install Visual Studio - an IDE from Microsoft. [Download the free community edition here](https://visualstudio.microsoft.com/downloads/).

During installation make sure the following workloads are checked:

* "Desktop development with C++"
* "Game development with C++"

### MozillaBuild Package

Finally, download the [MozillaBuild Package](https://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe) from Mozilla. Accept the default settings, in particular the default installation directory: `c:\mozilla-build\`. On some versions of Windows an error dialog will give you the option to ‘reinstall with the correct settings’ - you should agree and proceed.

{% hint style="info" %}
Once this is done, creating a shortcut to `c:\mozilla-build\start-shell.bat` on your desktop will make your life easier.
{% endhint %}

{% hint style="success" %}
**NOTE: You will need to run the start-shell.bat to open up the shell and perform the commands listed in other parts of this guide.**
{% endhint %}

### Getting the Code

Once you have run start-shell.bat, you will need to grab the source code if you haven't already.

The latest Firefox and Thunderbird source code is available from the main branches of the [firefox](https://github.com/mozilla-firefox/firefox) and [thunderbird-desktop](https://github.com/thunderbird/thunderbird-desktop) Git repositories, respectively.

When building locally:

* The root of the Firefox source is often renamed to `source/` but it can be named anything you like.
* The root of the Thunderbird source needs to be placed **inside** the Firefox source code, in a directory that must be named `comm/`.

```bash
git clone https://github.com/mozilla-firefox/firefox source/
cd source/
git clone https://github.com/thunderbird/thunderbird-desktop comm/
```

### Mach Bootstrap

In the `source` directory run the following command to get additional dependencies needed to install Thunderbird:

```bash
./mach bootstrap
```

You will be presented with the following options:

```
Please choose the version of Firefox you want to build:
  1. Firefox for Desktop Artifact Mode
  2. Firefox for Desktop
  3. GeckoView/Firefox for Android Artifact Mode
  4. GeckoView/Firefox for Android
```

Please choose option 2 to proceed with a successful build.

This action will install all the remaining libraries and dependencies necessary to build Thunderbird locally.

{% hint style="danger" %}
**Make sure to restart after installing all the requirements, or Thunderbird might encounter a build error.**
{% endhint %}

### Building Thunderbird

Now that you have the prerequisites for Windows, make sure you have the source code via the commands on the "Setting up a build environment" page:

{% content-ref url="/pages/8hECSrZOBsfUuTYg6Otp" %}
[Setting Up A Build Environment](/thunderbird-development/setting-up-a-build-environment)
{% endcontent-ref %}

Then you can follow the instructions on the Building Thunderbird page:

{% content-ref url="/pages/-La1fY-i3p06eOeTGUwH" %}
[Building Thunderbird](/thunderbird-development/building-thunderbird)
{% endcontent-ref %}


# Linux Build Prerequisites

This page has all the information you need to get your GNU/Linux development environment set up and ready to hack on Thunderbird.

## Hardware Requirements

### 64-bit version

You will need to be running a 64-bit version of Linux in order to build Thunderbird. You can check which version you're running by typing this command in your terminal:

```
uname -m
```

if this command returns `x86_64` you can proceed.

### 30 GB of free space

The Thunderbird build can use 30-40GB of disk space to complete depending on your operating system.

{% hint style="warning" %}
Note that while it's not technically required to have an internet connection to build, the default setup has `--enable-bootstrap` so that the toolchains download automatically.
{% endhint %}

{% hint style="warning" %}
The `bootstrap.py` file will create files outside of the current directory. E.g. in `~/.mozbuild`. Ensure you have enough free space in your home directory as well.\
Alternatively to build within the current directory and avoid writing to the home directory run `HOME="$(pwd)" ./bootstrap.py` instead or use a chroot environment.
{% endhint %}

## Build Environment

### Python

You’ll need `Python 3.8` or later installed.

You can check with `python3 --version` to see if you have it already. If not, you can install it with your distribution’s package manager. Make sure your system is up to date!

You will also need `python3-distutils` and `python3-pip` installed from your distribution's package manager.

### Git

As noted in the [Setting up a build environment page](/thunderbird-development/setting-up-a-build-environment), both Firefox and Thunderbird sources are stored in Git repositories. This means you will need to install Git if it is not already available. Here are some quick commands to install on common distributions:

#### Ubuntu/Debian

```
sudo apt install git
```

#### Fedora

```
sudo dnf install git
```

## Getting the Code

Once you have Git installed, you are ready to grab the source code. There are a couple of different methods to do this.

### Scripted

The [`bootstrap.py`](https://raw.githubusercontent.com/thunderbird/thunderbird-desktop/main/python/rocboot/bin/bootstrap.py) script will grab the two source repos you need, run `./mach bootstrap` for you, and sets up a necessary `mozconfig` file. Download this file to the directory where you would like your source code folder to live, either by clicking the link and moving the file to the appropriate location or using `wget`. Then make it executable and run it.

```
mkdir tb-build && cd tb-build
wget https://raw.githubusercontent.com/thunderbird/thunderbird-desktop/main/python/rocboot/bin/bootstrap.py
chmod +x bootstrap.py
./bootstrap.py
```

This will create a `source/` directory with both a `mozconfig` and a `comm/` folder inside.

The `source/` repository contains the Firefox source and defaults to the `main` branch.

The `source/comm` repository also defaults to the `main` branch.

The `mozconfig` file is setup to build Thunderbird and you can verify this with `cat mozconfig`; the `--enable-project` parameter should be `comm/mail`:

```
ac_add_options --enable-project=comm/mail
```

### Manually

If you would rather manually gather the source code, perform the bootstrap, and create your `mozconfig` file, then follow these steps.

#### Checkout the Source Code

Get the latest Firefox source code, and check it out into a local directory `source` (or however you want to call it). Then, get the latest Thunderbird source code. It needs to be placed **inside** the Mozilla source code, in a directory named `comm/`:

```
git clone https://github.com/mozilla-firefox/firefox source/
cd source/
git clone https://github.com/thunderbird/thunderbird-desktop comm/
```

#### Create `mozconfig` file

This step will need to be performed if you manually checked out the code and performed the bootstrap, and it will covered in the next section you follow, [Building Thunderbird](/thunderbird-development/building-thunderbird#build-configuration).

### Mach Bootstrap

In the `source` directory run the following command to get additional dependencies needed to install Thunderbird:

```
./mach bootstrap
```

You will be presented with the following options:

```
Please choose the version of Firefox you want to build:
  1. Firefox for Desktop Artifact Mode
  2. Firefox for Desktop
  3. GeckoView/Firefox for Android Artifact Mode
  4. GeckoView/Firefox for Android
```

Please choose option 2 to proceed with a successful build.

This action should install all the remaining libraries and dependencies necessary to build Thunderbird locally.

### Missing libraries

It could happen that some libraries will not be installed by the `bootstrap` command, specifically those related to the `Rust` programming language. Check whether these packages are available in your system by running these commands in your terminal:

* `which rustc`
* `which cargo`

If one or both commands return an empty output, you need to install them manually:

* Install Rust and cargo (the Rust package manager): `curl https://sh.rustup.rs -sSf | sh`
* Install cbindgen (tool that generates C bindings from Rust code): `cargo install cbindgen`

{% hint style="info" %}
If you get a `command not found` error while running `cargo`, but the command `which cargo` returns the location of the that package, it means you need to update your `PATH` inside your `.bashrc` file to include the `cargo` location:

```
export PATH=$HOME/.cargo/bin:$PATH
```

{% endhint %}

{% hint style="warning" %}
If you still are unable to find rustc and cargo via the ˋwhichˋ command after installing them, you may need to restart your session (log out and back into your user account, or restart your computer) to be able to see them.
{% endhint %}

## You're all set

Go back to the [Building Thunderbird](/thunderbird-development/building-thunderbird#build-configuration) page and continue following the guide.

{% content-ref url="/pages/-La1fY-i3p06eOeTGUwH" %}
[Building Thunderbird](/thunderbird-development/building-thunderbird)
{% endcontent-ref %}


# macOS Build Prerequisites

This page has all the information you need to get your macOS development environment set up and ready to hack on Thunderbird.

## Hardware Requirements

### 30 GB of free space

The Thunderbird build can use 30-40GB of disk space to complete depending on your operating system.

{% hint style="warning" %}
Note that while it's not technically required to have an internet connection to build, the default when building Thunderbird is that `--enable-bootstrap` is set so that the toolchains download automatically.
{% endhint %}

## Build Environment

### Python

You will need `python` (version 3.8 or later) and `pipx` (used to install packages from `pypi`). Both of these can be installed from homebrew. If you have not yet setup homebrew, please see [the homebrew installation instructions](https://brew.sh/).

```
brew install python pipx
```

{% hint style="warning" %}
Note that once homebrew is installed, the macOS SDK headers are installed already and can be found under `/Library/Developer/CommandLineTools/SDKs`. There should be no additional action required to install these SDK headers.
{% endhint %}

#### Use pipx to install mozphab

MozPhab is the tool needed to interface with Mozilla's instance of Phabricator. This step is needed before the bootstrap step. Pipx is the tool that we will use to install MozPhab and then we will make sure the relevant `~/.local/bin` has been added to the PATH envirnoment variable.

```
pipx install MozPhab
pipx ensurepath 
```

### Git

As noted in the [Setting up a build environment page](/thunderbird-development/setting-up-a-build-environment), both Firefox and Thunderbird sources are stored in Git repositories. This means you will need to install Git if it is not already available. Here is a quick command to install it:

```
brew install git
```

## Get the Source

Once you have Git and MozPhab installed, you are ready to grab the source code. There are a couple of different methods to do this.

{% hint style="warning" %}
Firefox can build without Thunderbird present in the `comm/` repo and a few options set. The Thunderbird code base features the additions that turn Firefox into Thunderbird.
{% endhint %}

### Scripted

We have created and host a script that will grab the two source repos you need, run `./mach bootstrap` for you, and sets up a necessary `mozconfig` file. This script is called [`bootstrap.py`](https://hg.mozilla.org/comm-central/raw-file/tip/python/rocboot/bin/bootstrap.py). Download this file to the directory where you would like your source code folder to live, either by clicking the link and moving the file to the appropriate location or using `wget`. Then we will make it executable and run it.

```
mkdir tb-build && cd tb-build
wget https://raw.githubusercontent.com/thunderbird/thunderbird-desktop/main/python/rocboot/bin/bootstrap.py
chmod +x bootstrap.py
./bootstrap.py
```

This will create a `source/` directory with both a `mozconfig` and a `comm/` folder inside.

The `source/` repository contains the Firefox source and defaults to the `main` branch.

The `source/comm` repository also defaults to the `main` branch.

The `mozconfig` file is setup to build Thunderbird and you can verify this with `cat mozconfig`; the `--enable-project` parameter should be `comm/mail`:

```
ac_add_options --enable-project=comm/mail
```

### Manually

If you would rather manually gather the source code, perform the bootstrap, and create your `mozconfig` file, then follow these steps.

#### Checkout the Source Code

Get the latest Firefox source code, and check it out into a local directory `source` (or however you want to call it). Then, get the latest Thunderbird source code. It needs to be placed **inside** the Mozilla source code, in a directory named `comm/`:

```
git clone https://github.com/mozilla-firefox/firefox source/
cd source/
git clone https://github.com/thunderbird/thunderbird-desktop comm/
```

#### Create `mozconfig` file

This step will need to be performed if you manually checked out the code and performed the bootstrap, and it will covered in the next section you follow, [Building Thunderbird](/thunderbird-development/building-thunderbird#build-configuration).

#### Mach Bootstrap

In the `source` directory run the following command to get additional dependencies needed to install Thunderbird:

```
./mach bootstrap
```

You will be presented with the following options:

```
Please choose the version of Firefox you want to build:
  1. Firefox for Desktop Artifact Mode
  2. Firefox for Desktop
  3. GeckoView/Firefox for Android Artifact Mode
  4. GeckoView/Firefox for Android
```

Please choose option 2 to proceed with a successful build.

This action should install all the remaining libraries and dependencies necessary to build Thunderbird locally.

### Missing libraries

It could happen that some libraries will not be installed by the `bootstrap` command, specifically `Rust` and `Go`. Check if these packages are available in your system by running these commands in your terminal:

* `which rustc`
* `which cargo`

If one or both commands return an empty output, you need to install them manually. We recommend using [HomeBrew](https://brew.sh/) to download and install these packages in your system. After that, follow these steps:

* Install Rust: `brew install rust`
* Install C bindings: `cargo install cbindgen`

{% hint style="info" %}
If you get a `command not found` error while running `cargo`, but the command `which cargo` returns the location of the that package, it means you need to update your `PATH` inside your `.bashrc` file to include the `cargo` location:

```
export PATH=$HOME/.cargo/bin:$PATH
```

{% endhint %}

## You're all set

Go back to the [Building Thunderbird](/thunderbird-development/building-thunderbird#build-configuration) page and continue following the guide:

{% content-ref url="/pages/-La1fY-i3p06eOeTGUwH" %}
[Building Thunderbird](/thunderbird-development/building-thunderbird)
{% endcontent-ref %}


# Artifact Builds

… or, How To Build Without Building

{% hint style="warning" %}
This pages assumes you already have some familiarity with the concepts of building Thunderbird. It is probably not suitable for absolute beginners.
{% endhint %}

Building everything can take a long time, and in many cases is completely unnecessary. If the changes you are making are limited to the UI, or tests, or even some back-end things, then compiling and linking the unchanged C++ and Rust code produces the same results every time. So let's get someone else to do it for us.

This the idea behind *artifact builds.* Instead of spending our time doing the building, we instead download pre-built copies of those parts (the "binary components") from the build infrastructure. Then we build everything else (what we're interested in) around them. This can reduce time spent from hours to a few minutes.

There is a number of important caveats, so make sure you understand them before going on.

## When can I use an artifact build?

In the simplest terms, if the code you are changing appears as plain-text in the final built output, you can use an artifact build. This includes:

* Javascript
* CSS
* XUL
* HTML
* Pre-processed versions of any of the above
* Any support files such as images

## When *can't* I use an artifact build?

You can't use an artifact build if the code you are changing includes:

* C or C++
* Rust
* IDL interfaces if the binary components use the interface
* Statically-linked component `.conf` files (even if the component is written in JS)
* Some types of build configuration changes
* … and probably others

## Modifying your build setup

How you set up to build depends on your scenario. Many people will still need to do a complete build sometimes. In this case, make a copy of your existing `mozconfig` file and add these lines:

```
mk_add_options MOZ_OBJDIR=obj-artifact
ac_add_options --enable-artifact-builds
```

Line 1 ensures your artifact build goes in a separate object directory. Don't use the same object directory for both types of builds. Line 2 enables artifact building.

From here, remember to make sure you're using the right `mozconfig` (`export MOZCONFIG=/path/to/artifact.mozconfig`).

If you don't need to do a complete build, you can just add line 2 to your existing `mozconfig`.

## Building the right thing

As stated earlier, the build process downloads the binary components from the build infrastructure. Here's where things can get a little bit messy.

First, things can only be downloaded if they exist. So you need to make sure what you want is available. Mach will download from the *latest successful non-nightly build* that matches your current `comm-central` tree. This might not be the one you expect.

Example: if your platform is 64-bit Linux and your tree was up-to-date at this point, which build will you get?

![](/files/-MHEViNhCc2CoJQd0b1q)

{% hint style="info" %}
This screenshot is from <https://treeherder.mozilla.org/#/jobs?repo=comm-central>. Check what's going on there before updating your build.

**Note that an "opt" artifact build will use "shippable opt" artifacts.** (This has changed since the image above was first created. It's been modified but some of the arrows are now slightly inaccurate. You get the idea.)
{% endhint %}

In this example you have a choice:

* wait for the running build to complete (that is, the `B` to turn green, you don't need to wait for the tests) before building locally
* build now, from the last successful build and hope the changes that have landed since don't affect you
* "update" your trees back to their state when the last successful build happened

## Updating to the right revision

To find the revisions used for a successful build, click on the `B`, and go down to the bottom of the Job Details tab:

![](/files/-LifERhNKZ_juEox6c3c)

Update both comm-central *and* mozilla-central trees using `hg update -r abcdef1234` before building. In general, when a new build begins on the build infrastructure, it uses the latest mozilla-central as well as the latest comm-central, but that may no longer be the case by the time you build.

## Other tips

* The build process logs a lot about what it's doing. This can help you understand exactly what's going on so read it the first few times you build.
* You can in many cases do an even quicker rebuild (for example to remake pre-processed files) by running `./mach build faster` (or `../mach build -C . faster` if your current directory is comm-central). This skips a bunch of steps at the beginning and end of the build process that you might not need. When in doubt, don't build faster.
* Here's [the Firefox Source Docs page on artifact builds](https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.html) with even more information.

## Artifact builds on the Try server

Using an artifact build on the [Try server](https://developer.thunderbird.net/thunderbird-development/fixing-a-bug/try-server) can save you *a lot* of time, and it also reduces our infrastructure costs. The same conditions about when you can and can't do an artifact build apply, plus only 64-bit builds are working.

To do an artifact Try run, add `--artifact` to your try syntax. For example:

```
try: -b o -p linux64,macosx64-shippable,win64 -u all --artifact
```

(Debug builds also work but you'd specify just `macosx64` in that case.)

You can (and should in a lot of cases) specify a mozilla-central revision to build from, by editing the file `.gecko_rev.yml`, replacing `default` with the revision you need. If you don't, the Try server will use the latest mozilla-central revision, which may or may not be the same one used for the latest comm-central build.


# Codebase Overview

A high-level look at the project's architecture and a guide to where to find things.

## Overview of Thunderbird Source Code

The following directories are included in the [Thunderbird](https://github.com/thunderbird/thunderbird-desktop) repository:

**build**\
Miscellaneous files used by the build process.

**calendar**\
The calendar component of Thunderbird (formerly known as the Lightning extension).

[**chat**](/thunderbird-development/codebase-overview/chat)\
Files for the chat component of Thunderbird. There is also related code in `mail/components/im`.

The subdirectories are:

* **components** Various chat features, includes the interfaces that each protocol must implement.
* **content** User interface files which become `chrome://chat/content/…`.
* **locales** The user-visible strings, in US English. Files from this directory become `chrome://chat/locale/…`.
* **modules** JavaScript modules that are specific to chat.
* **protocols** Various protocol implementations. Each of subdirectory implements a protocol to the interfaces found under **components**.
* **themes** Common and platform-specific styling specific to chat. Files from this directory become `chrome://chat/skin/…`.

**mail**\
Thunderbird specific source code. It's no coincidence that this folder is laid out a lot like the `browser` and `toolkit` directories on mozilla-central. Many of the subdirectories follow the same pattern:

* **content** User interface files which become `chrome://messenger/content/…`.
* **modules** Javascript modules which become `resource:///modules/…`.
* **test** Mochitest and/or XPCShell tests.

The subdirectories are:

* **actors** Modules which handle communication between processes. [See the Firefox documentation for information about actors](https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html).
* **app** Configuration and packaging instructions. Contains `all-thunderbird.js`, the default preferences.
* **base** The main mail window and several miscellaneous dialogs. Also, things which are common to many parts of the program.
* **branding** Icons and imagery.
* **components** Various Thunderbird features, including:
  * **accountcreation** The account setup wizard.
  * **addrbook** The address book user front end. (Not the address book back end, which is in `mailnews/addrbook`).
  * **cloudfile** The cloud attachment feature (a.k.a. FileLink).
  * **compose** Email composition window.
  * **extensions** WebExtensions schema and implementation.
  * **im** The chat front end.
  * **preferences** The preferences tab.
* **config** Build instructions, including the automation mozconfig files.
* **extensions**
* **installer** Packaging and installation instructions.
* **locales** The user-visible strings, in US English. Files from this directory become `chrome://messenger/locale/…`.
* **modules** Shared JS modules. Files from this directory become `resource:///modules/….`
* **test** The MozMill user interface tests and the code to run that test suite.
* **themes** Common and platform-specific styling of Thunderbird user interface. Files from this directory become `chrome://messenger/skin/…`.

**mailnews**\
Source code specific to the Mail and Newsgroups part of Thunderbird and SeaMonkey.

**mozharness**\
Files needed for Thunderbird's test infrastructure.

**other-licenses**\
Code that is not under the Mozilla license. See <http://www.mozilla.org/MPL/> for more info.

**suite**\
SeaMonkey-specific source code. Not used in Thunderbird.

**taskcluster**\
Files needed for Thunderbird's build infrastructure.

**testing**\
Files needed for Thunderbird's test infrastructure.

**third\_party**\
Libraries developed elsewhere that are included in Thunderbird


# Account Configuration

This page describes the classes and interfaces involved in configuring mail accounts.

{% hint style="info" %}
Chat accounts use very similar mechanisms, but we won't go into that here to avoid confusion.
{% endhint %}

## The Account Manager

The account manager controls the objects described here. It is defined by `nsIMsgAccountManager`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/public/nsIMsgAccountManager.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/public/nsIMsgAccountManager.idl)) and implemented by `nsMsgAccountManager`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/src/nsMsgAccountManager.cpp), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/src/nsMsgAccountManager.cpp)).

To get to the account manager from JS, use `MailServices.accounts`. To get to it from C++, use `mozilla::components::AccountManager::Service()`. (The rest of this page will describe things in JS terms only for ease of reading.)

## Accounts

Accounts are simple containers for incoming servers and identities. They are defined by `nsIMsgAccount`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/public/nsIMsgAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/public/nsIMsgAccount.idl)) and implemented by `nsMsgAccount`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/src/nsMsgAccount.cpp), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/src/nsMsgAccount.cpp)). If you're looking to use something in a mail account, you'll probably first get a reference to an `nsIMsgAccount`.

Accounts are identified by a `key` property, which is the word `account` and then a number. Preferences for an account have the prefix `mail.accounts.accountX` .

All accounts can be found at `MailServices.accounts.accounts`. To get a particular account, use `MailServices.accounts.getAccount(accountKey)`.

## Incoming Servers

Incoming Server objects describe a connection to a mail server, e.g. an IMAP or POP3 server, or for local mail. They are defined by `nsIMsgIncomingServer`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/public/nsIMsgIncomingServer.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/public/nsIMsgIncomingServer.idl)) and a sub-interface and implementation exists for each type of server Thunderbird can connect to.

Incoming Servers are identified by a `key` property, which is the word `server` and then a number. Preferences for an account have the prefix `mail.servers.serverX` .

There is a 1:1 relationship between an account and an incoming server. To get to the server from an account, use the account's `incomingServer` property. To get the account for a server, use `MailServices.accounts.FindAccountForServer(server)`.

All incoming servers can be found at `MailServices.accounts.allServers`. To get a particular server, use `MailServices.accounts.getIncomingServer(serverKey)`.

## Identities

Identities describe everything about sending mail from an account, such as the user's name and email address, which SMTP server to use, and where to put sent mail. They are defined by `nsIMsgIdentity`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/public/nsIMsgIdentity.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/public/nsIMsgIdentity.idl)) and implemented by `nsMsgIdentity`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/base/src/nsMsgIdentity.cpp), [searchfox link](https://searchfox.org/comm-central/source/mailnews/base/src/nsMsgIdentity.cpp)).

Identities are identified by a `key` property, which is the word `id` and then a number. Preferences for an account have the prefix `mail.identity.idX` .

Accounts can have any number of identities, although removing the last identity generally isn't allowed. Use the account's `identities` and `defaultIdentity` properties to access them.

Typically an identity belongs to only one account, although it's technically possible for it to belong to multiple accounts. Use `MailServices.accounts.getServersForIdentity(identity)` to get the server(s) for an identity, and go from there to get the accounts. (Yes, this *is* weird.)

All identities can be found at `MailServices.accounts.allIdentities`. To get a particular identity, use `MailServices.accounts.getIdentity(identityKey)`.

## SMTP

SMTP breaks some of the pattern you might've noticed in the previous classes. The SMTP service, `MailServices.smtp` or `mozilla::Components::Smtp::Service()`, implements `nsISmtpService`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/compose/public/nsISmtpService.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/compose/public/nsISmtpService.idl)) as `SmtpService`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/compose/src/SmtpService.jsm), [searchfox link](https://searchfox.org/comm-central/source/mailnews/compose/src/SmtpService.jsm)).

SMTP server configuration is kept by objects implementing `nsISmtpServer`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/compose/public/nsISmtpServer.idl), [searchfox link](https://searchfox.org/comm-central/source/mailnews/compose/public/nsISmtpServer.idl)) as `SmtpServer`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/compose/src/SmtpServer.jsm), [searchfox link](https://searchfox.org/comm-central/source/mailnews/compose/src/SmtpServer.jsm)). They also are identified by a `key` property, which is the word `smtp` and then a number. Preferences for an account have the prefix `mail.smtpserver.smtpX` .

Identity objects reference SMTP servers in their `smtpServerKey` attribute.

All SMTP servers can be found at `MailServices.smtp.servers`. To get a particular server, use `MailServices.smtp.getServerByKey(smtpKey)`.


# Address Book

The Address Book code is found in two places: the UI is primarily in `mail/components/addrbook`, while the back end is in `mailnews/addrbook`. This documentation only describes the back end.

## Interfaces and Classes

### Address Book Manager

The address book manager is responsible for organising individual "directories" (address books), including reading the configuration from the preferences at start-up and creating the directory objects. It is defined by the interface `nsIAbManager` and implemented as `AddrBookManager`. You can get a reference to the manager at `MailServices.ab`.

The address book manager can get a reference to a particular directory in a number of ways:

* By URI. Registered directories have a URI defined by the type of directory and any information needed to identify an individual directory. For example `jsaddrbook://abook.sqlite`.
* By unique identifier (UID). For example `70139e91-37a5-47fd-8856-2f0756f43ef1`.
* By location in the preferences. Directories store their preferences at `ldap_2.servers.<some identifier>`.

`nsIAbManager` also defines the `DIRECTORY_TYPE` constants mentioned below.

### Directories

Individual address books are referred to as "directories" or "books" in the code. They implement `nsIAbDirectory`.

There are different implementations:

* A base class `AddrBookDirectory` which handles most of the implementation detail using data provided by a sub-class. It should not be instantiated directly.
* The most common type is `SQLiteDirectory` which, as the name implies, stores data in an SQLite database in the user profile. It inherits from `AddrBookDirectory`. Directories of this type have URIs with the scheme `jsaddrbook`, and are of type `JS_DIRECTORY_TYPE`.
* `CardDAVDirectory` extends `SQLiteDirectory` by adding CardDAV capabilities. Directories of this type have URIs with the scheme `jscarddav`, and are of type `CARDDAV_DIRECTORY_TYPE`.
* `LDAPDirectory` extends `AddrBookDirectory` and adds LDAP capabilities. Directories of this type have URIs with the scheme `moz-abldapdirectory`, and are of type `LDAP_DIRECTORY_TYPE.`
* `nsAbDirProperty` provides a base C++ implementation of `nsIAbDirectory` to OS-specific address books `nsIAbOSXDirectory` and `nsAbOutlookDirectory`. These are both of type `MAPI_DIRECTORY_TYPE`. The OS-specific types are likely to be discontinued.
* WebExtension APIs can provide contact suggestions to the user when composing messages. This is done via a `ASYNC_DIRECTORY_TYPE` directory.

### Contacts

Directories contain contacts or "cards", which implement the interface `nsIAbCard`. The are accessible by the `childCards` property as well as methods for searching, adding, modifying, and deleting contacts. (Confusingly a "card" can also refer to a mailing list, see below.)

Contacts are implemented by `AddrBookCard` for all of the JS code, or `nsAbCardProperty` in C++.

The current implementation stores contact data as key/value pairs. Work is underway to replace this with a more capable data storage based on the [vCard format](https://tools.ietf.org/html/rfc6350).

### Mailing Lists

A mailing list (or just a "list" in many places) is a collection of contacts. To be added to a mailing list, a contact *must* have an email address. A mailing lists can be both `nsIAbDirectory` and `nsIAbCard`, depending on the situation. Directories provide an array of their mailing lists as `nsIAbDirectory` with the `childNodes` property. They also appear as `nsIAbCard` in the `childCards` property and some of the methods that handle cards.

A mailing list can not have any `childNodes` of its own. However in a confusing quirk, it can have the card of another mailing list in its `childCards`.

Mailing lists are implemented in `AddrBookMailingList`.

## Notifications

The address book code fires observer service notifications. The notification names should be self-explanatory.

### Notifications about directories

In all cases the "subject" of the notification is the directory.

* addrbook-directory-created
* addrbook-directory-updated – the "data" contains the property that changed, and only the "DirName" property can change
* addrbook-directory-deleted
* addrbook-directory-invalidated – many contacts changed, anything storing or displaying contacts should be thrown away and reloaded
* addrbook-directory-synced – CardDAV sync succeeded
* addrbook-directory-sync-failed – CardDAV sync failed

### Notifications about contacts

In all cases the "subject" of the notification is the contact, and unless otherwise stated "data" contains the UID of the directory containing the contact.

* addrbook-contact-created
* addrbook-contact-updated
* addrbook-contact-properties-updated – "data" contains a JSON-stringified object containing the old and new values of any properties that changed
* addrbook-contact-deleted

### Notifications about lists

In these cases the "subject" of the notification is the list as `nsIAbDirectory` and "data" is the parent directory's UID.

* addrbook-list-created
* addrbook-list-updated
* addrbook-list-deleted

In these cases the "subject" is the contact in question, and "data" is the list's UID.

* addrbook-list-member-added
* addrbook-list-member-removed

## Transition to vCard Storage

As mentioned above, contact properties are stored as key/value pairs. This has limitations in a modern address book:

* There's a fixed set of keys. Although there's no real restrictions on keys you *could* use, no user interface is available for anything outside of the known keys.
* Multiple values for the same key aren't possible. For example if a contact has three email addresses, the first two can be stored in `PrimaryEmail` and `SecondEmail`, but there's nowhere to store the third.
* Storing meta information about the values is impossible. There's no way to mark an email address as a work address or a home address, for example.

To combat this, from Thunderbird 102 contacts stored in local directories will be converted to use the [industry standard vCard format](https://tools.ietf.org/html/rfc6350). CardDAV directories already use vCard, although the data was converted to key/value pairs so the UI could use it.

LDAP and OS-specific address books will continue to use keys/values.

### New interface properties

`nsIAbCard` gains two new members:

* `supportsVCard` a boolean value indicating support for vCard (or lack thereof). Only `AddrBookCard` objects currently support vCard.
* `vCardProperties` is a `VCardProperties` object if the card supports vCard, or null.

The `VCardProperties` class contains methods for parsing, manipulating, and serialising vCards. Each piece of information in a card is represented by a `VCardPropertyEntry` object. See VCardUtils.sys.mjs([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/addrbook/modules/VCardUtils.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/mailnews/addrbook/modules/VCardUtils.sys.mjs)) for more information.

### Storage changes

In practice, we'll be storing the vCard data as just another key/value pair. The key used will be `_vCard` and the value will be the entire vCard.

When a card is saved to a directory, the following things happen:

* If it supports vCard, the `vCardProperties` member is serialised.
* If it doesn't, the existing key/value pairs are converted to a `VCardProperties` object, then serialised.
* Some properties are collected from the card:
  * display name (`DisplayName`)
  * first and last names (`FirstName` and `LastName`)
  * first and second preference email addresses (`PrimaryEmail` and `SecondEmail`)
  * nick name (`NickName`)
* The serialised vCard, these properties, and any key/value properties which *can't* be stored in a vCard are saved.
* Any key/value properties which *can* be stored in the vCard are abandoned. No information is lost because the values are part of the vCard.

The names and addresses stored separately are for performance reasons. The vCard is considered the true source of information.

{% hint style="info" %}
No migration takes place when the user upgrades to a Thunderbird version that supports vCard. However at this point the version number of the database storage is incremented (from 3 to 4) and a backup is automatically created.

Cards are only migrated when they are saved.
{% endhint %}

### Data conversion between keys/values and vCard

VCardUtils.sys.mjs([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/addrbook/modules/VCardUtils.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/mailnews/addrbook/modules/VCardUtils.sys.mjs)) contains a number of utility functions for converting between the storage types:

* `VCardUtils.abCardToVCard` converts an `nsIAbCard` (any implementation) into a vCard string.
* `VCardUtils.propertyMapToVCard` converts a `Map` of keys and values to a vCard string.
* `VCardUtils.vCardToAbCard` converts a vCard string to an `AddrBookCard`.
* `VCardProperties.fromVCard` converts vCard string to a `VCardProperties` object.
* `VCardProperties.fromPropertyMap` converts a `Map` of keys and values to a `VCardProperties` object.
* `VCardProperties.prototype.toPropertyMap` converts a `VCardProperties` object to a `Map` of keys and values.
* `VCardProperties.prototype.toVCard` converts a `VCardProperties` object to a vCard string.

{% hint style="warning" %}
Conversion from a key/value dictionary to vCard should not result in any data loss. In the other direction data loss is possible.
{% endhint %}

To see exactly what fields are converted and what they are converted to, see `typeMap` in VCardUtils.sys.mjs.


# Chat Core

Chat Core is the code for instant messaging that is used by Thunderbird. It provides a number of functions and capabilities, including:

* [Communications protocols](/thunderbird-development/codebase-overview/chat/chat-core-protocols) - IRC, Matrix, XMPP (and XMPP-based protocols)
* [Message styles](/thunderbird-development/codebase-overview/chat/message-styles)
* Account configuration
* [Contacts storage](/thunderbird-development/codebase-overview/chat/contacts)
* Message logging
* [Emoticon (smileys) handling](broken://pages/-MWcejMPW-yA3myoicPl)

The Chat Core code lives in the chat/ directory of the [Thunderbird](https://github.com/thunderbird/thunderbird-desktop) repository.

#### [Chat Core Protocols](/thunderbird-development/codebase-overview/chat/chat-core-protocols)

The Chat Core code used by Thunderbird has some abstractions to deal with the differences between protocols (e.g. IRC vs. XMPP).

#### [Contacts](/thunderbird-development/codebase-overview/chat/contacts)

Contacts are at the heart of instant messaging, and thus the Chat Core has a way to abstract to a "person" (represented by an `imIContact`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl)) instance), which might connect to multiple networks, etc.

#### [Keyboard shortcuts](/thunderbird-development/codebase-overview/chat/keyboard-shortcuts)

This is a list of the available keyboard shortcuts with brief descriptions of what they do.

#### [Message Styles](/thunderbird-development/codebase-overview/chat/message-styles)

Chat Core uses a message style system based on HTML, JS and CSS that is very similar to the one created for [Adium](https://adium.im/). If you plan to create a message style, reading the Adium documentation on the topic may be helpful -- see this [tutorial](https://web.archive.org/web/20160408094746/https://trac.adium.im/wiki/CreatingMessageStyles/Tutorial) and this [reference sheet](https://web.archive.org/web/20160715205801/https://trac.adium.im/wiki/CreatingMessageStyles).

#### [Notifications](/thunderbird-development/codebase-overview/chat/notifications)

This is a page for documenting the notifications in Thunderbird. This is likely out of date. Notifications are grouped by interface you need to attach the observer to.


# Notifications

This is a page for documenting the notifications from Chat Core in Thunderbird. This is likely out of date. Notifications are grouped by interface you need to attach the observer to.

{% hint style="warning" %}
*Draft: This page is not complete.*
{% endhint %}

### nsIObserverService <a href="#nsiobserverservice" id="nsiobserverservice"></a>

| Topic                             | Subject                                                                                                                                                                                                                                                                                           | Data                     | Added | Comment                                                                                                                                                                                                                |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| account-added                     | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | a new account has been created                                                                                                                                                                                         |
| account-connected                 | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account has connected                                                                                                                                                                                              |
| account-connecting                | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account has started a connection attempt                                                                                                                                                                           |
| account-connect-error             | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account has disconnecting with an error                                                                                                                                                                            |
| account-connect-progress          | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account is attempting to connect                                                                                                                                                                                   |
| account-disconnected              | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account has disconnected                                                                                                                                                                                           |
| account-disconnecting             | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | the account is disconnecting without an error reason                                                                                                                                                                   |
| account-list-updated              | imIAccountsService([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccountsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccountsService.idl))                                             | null                     | <1.0  | the list of accounts has been updated                                                                                                                                                                                  |
| account-removed                   | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  | an account is about to be removed                                                                                                                                                                                      |
| account-updated                   | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | <1.0  |                                                                                                                                                                                                                        |
| app-handler-pane-loaded           | nsIDOMWindow([git link](https://github.com/mozilla-firefox/firefox/blob/main/dom/interfaces/base/nsIDOMWindow.idl), [searchfox link](https://searchfox.org/comm-central/source/mozilla/dom/interfaces/base/nsIDOMWindow.idl))                                                                     | null                     | <1.0  | Applications pane in preferences window is loaded                                                                                                                                                                      |
| autologin-processed               | imICoreService([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imICoreService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imICoreService.idl))                                                         | null                     | <1.0  | imICoreService::processAutoLogin() is complete                                                                                                                                                                         |
| browser-request                   | prplIRequestBrowser([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIRequest.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIRequest.idl))                                                        | null                     | <1.0  | Used by protocol plugins to bring up a browser window (e.g. for an OAuth request)                                                                                                                                      |
| closing-conversation              | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))                                                | null                     | <1.0  |                                                                                                                                                                                                                        |
| contact-moved                     | <p>imIContactsService (<a href="https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl">git link</a>, <a href="https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl">searchfox link</a>)<br>imIContact</p> | null                     | <1.0  | when a contact has been moved (removed from one tag, added to another, or both)                                                                                                                                        |
| contact-tag-added                 | imIContact                                                                                                                                                                                                                                                                                        | tag.id                   | <1.0  | when a tag is added to a contact                                                                                                                                                                                       |
| contact-tag-removed               | imIContact                                                                                                                                                                                                                                                                                        | tag.id                   | <1.0  | when a tag is removed from a contact                                                                                                                                                                                   |
| conversation-closed               | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))                                                | null                     | <1.0  | conversation going away                                                                                                                                                                                                |
| conversation-left-chat            | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))                                                | null                     | <1.0  | the user has left the conversation (but it might remain visible)                                                                                                                                                       |
| conversation-loaded               | nsISupports([git link](https://github.com/mozilla-firefox/firefox/blob/main/xpcom/base/nsISupports.idl), [searchfox link](https://searchfox.org/comm-central/source/mozilla/xpcom/base/nsISupports.idl))                                                                                          | null or details          | <1.0  | subject is a \<browser type="content-conversation">, if fired because an existing conversation is moved from one window to another, the data parameter ("details") will contain the string "imported".                 |
| im-sent                           | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))                                                | message text             | <1.0  | on sending an IM (whether or not it succeeded)                                                                                                                                                                         |
| new-conversation                  | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))                                                | null                     | <1.0  | conversation created                                                                                                                                                                                                   |
| new-ui-conversation               | imIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl))                                      | null                     | <1.0  |                                                                                                                                                                                                                        |
| prpl-quit                         | imICoreService([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imICoreService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imICoreService.idl))                                                         | null                     | 1.2   | Core shutdown, was previously purple-quit                                                                                                                                                                              |
| status-changed                    | imICoreService([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imICoreService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imICoreService.idl))                                                         | status text              | <1.0  | user (not buddy) status change                                                                                                                                                                                         |
| tag-hidden                        | imITag([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imITagsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imITagsService.idl))                                                                 | null                     | <1.0  | fired when a tag is hidden                                                                                                                                                                                             |
| tag-shown                         | imITag([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imITagsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imITagsService.idl))                                                                 | null                     | <1.0  | fired when a tag is set to be shown (unhiding it)                                                                                                                                                                      |
| ui-conversation-closed            |                                                                                                                                                                                                                                                                                                   | null                     | <1.0  |                                                                                                                                                                                                                        |
| unread-im-count-changed           |                                                                                                                                                                                                                                                                                                   | count of unread messages | <1.0  | fired when the number of unread messages changes                                                                                                                                                                       |
| new-text                          | imIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl))                                           | null                     | 1.5   | New message is about to be displayed.                                                                                                                                                                                  |
| new-directed-incoming-message     | imIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl))                                           | null                     | 1.5   | New incoming message that is either a direct message or highlights the user.                                                                                                                                           |
| ui-conversation-replaced          | imIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imICoreService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imICoreService.idl))                                                        | null                     | 91    | The subject UI conversation is about to be replaced by a new instance (with different interfaces but the same ID).                                                                                                     |
| conversation-update-type          | imIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imICoreService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imICoreService.idl))                                                        | null                     | 91    | The type of the underlying prplIConversation has changed. Is always preceded by a ui-conversation-replaced notification that disposes of the old UIConversation instance, while this will have the new one as subject. |
| account-sessions-changed          | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | 91    | The account session list has changed                                                                                                                                                                                   |
| account-encryption-status-changed | imIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))                                                                     | null                     | 91    | The account encryption status info has changed                                                                                                                                                                         |

### imIBuddy <a href="#imibuddy" id="imibuddy"></a>

| Topic                              | Subject                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Data            | Added | Notes                                                                               |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----- | ----------------------------------------------------------------------------------- |
| account-buddy-availability-changed | prplIAccountBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                      | null            | <1.0  | Possibly fired on account buddy status change                                       |
| account-buddy-display-name-changed | <p>imIContactsService (<a href="https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl">git link</a>, <a href="https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl">searchfox link</a>)<br>imIAccountBuddy</p>                                                                                                                                                                                                                                                    | old serverAlias | <1.0  | Fired when setting imIAccountBuddy::serverAlias                                     |
| account-buddy-icon-changed         | imIAccountBuddy                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | null            | <1.0  | Fired when setting imIAccountBuddy::buddyIconFilename                               |
| account-buddy-signed-off           | imIAccountBuddy                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | null            | <1.0  | Possibly fired on account buddy status change                                       |
| account-buddy-signed-on            | imIAccountBuddy                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | null            | <1.0  | Possibly fired on account buddy status change                                       |
| account-buddy-status-changed       | imIAccountBuddy                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | null            | <1.0  | Possibly fired on account buddy status change                                       |
| buddy-added                        | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | In response to account-buddy-added if the buddy previously had no account buddies   |
| buddy-availability-changed         | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | possibly fired on imIBuddy status updates                                           |
| buddy-display-name-changed         | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | old serverAlias | <1.0  | possibly in response to account-buddy-display-name-changed on the preferred account |
| buddy-preferred-account-changed    | <p>imIContactsService (<a href="https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl">git link</a>, <a href="https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl">searchfox link</a>)<br>imIBuddy (<a href="https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl">git link</a>, <a href="https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl">searchfox link</a>)</p> | null            | <1.0  | when setting imIBuddy::preferredAccountBuddy (internally)                           |
| buddy-removed                      | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | In response to account-buddy-removed if the buddy no longer has any account buddies |
| buddy-signed-off                   | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | possibly fired on imIBuddy status updates                                           |
| buddy-signed-on                    | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | possibly fired on imIBuddy status updates                                           |
| buddy-status-changed               | imIBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl))                                                                                                                                                                                                                                                                                                               | null            | <1.0  | possibly fired on imIBuddy status updates                                           |

### imIContact <a href="#imicontact" id="imicontact"></a>

Note: all imIContact notifications also go to any imITag it belongs to, as well as nsIObserverService.

| Topic                           | Subject    | Data | Added | Notes                                                                                                           |
| ------------------------------- | ---------- | ---- | ----- | --------------------------------------------------------------------------------------------------------------- |
| contact-added                   | imIContact | null | <1.0  | When a new buddy is created with no other contact                                                               |
| contact-availability-changed    | imIContact | null | <1.0  | Possibly fired on contact status updates                                                                        |
| contact-display-name-changed    | imIContact | null | <1.0  | Fired on setting imIContact::Alias or imIContact::preferredBuddy, or the preferred buddy's display name changed |
| contact-no-longer-dummy         | imIContact | null | <1.0  | Fired when a contact is being loaded?                                                                           |
| contact-preferred-buddy-changed | imIContact | null | <1.0  | Fired on setting imIContact::preferredBuddy                                                                     |
| contact-removed                 | imIContact | null | <1.0  | When the last buddy for the contact is removed                                                                  |
| contact-signed-off              | imIContact | null | <1.0  | Possibly fired on contact status updates                                                                        |
| contact-signed-on               | imIContact | null | <1.0  | Possibly fired on contact status updates                                                                        |
| contact-status-changed          | imIContact | null | <1.0  | Possibly fired on contact status updates                                                                        |

### imITag <a href="#imitag" id="imitag"></a>

| Topic             | Subject    | Data | Added | Notes                                             |
| ----------------- | ---------- | ---- | ----- | ------------------------------------------------- |
| contact-moved-in  | imIContact | null | <1.0  | When a contact has been moved into the tag        |
| contact-moved-out | imIContact | null | <1.0  | When a contact has been moved out of the tag      |
| tag-hidden        | imIContact | null | <1.0  | fired when a tag is hidden                        |
| tag-shown         | imIContact | null | <1.0  | fired when a tag is set to be shown (unhiding it) |

### prplIConversation <a href="#prpliconversation" id="prpliconversation"></a>

| Topic                  | Subject                                                                                                                                                                                                                                                         | Data                                         | Added | Notes                                                                                                                                       |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| chat-buddy-add         | nsISimpleEnumerator([git link](https://github.com/mozilla-firefox/firefox/blob/main/xpcom/ds/nsISimpleEnumerator.idl), [searchfox link](https://searchfox.org/comm-central/source/mozilla/xpcom/ds/nsISimpleEnumerator.idl))                                    | null                                         | <1.0  | When users join a chat. The enumerator elements are prplIConvChatBuddy.                                                                     |
| chat-buddy-removed     | nsISimpleEnumerator([git link](https://github.com/mozilla-firefox/firefox/blob/main/xpcom/ds/nsISimpleEnumerator.idl), [searchfox link](https://searchfox.org/comm-central/source/mozilla/xpcom/ds/nsISimpleEnumerator.idl))                                    | null                                         | <1.0  | When chat buddies leave a chat. The enumerator elements are nsISupportsString of the user name.                                             |
| chat-buddy-update      | prplIConvChatBuddy([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))             | old name, if the chat buddy is being renamed | <1.0  | data is null if this is not a rename                                                                                                        |
| chat-buddy-topic       |                                                                                                                                                                                                                                                                 | null                                         | <1.0  | The chat topic was updated                                                                                                                  |
| new-text               | prplIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIMessage.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIMessage.idl))                             | null                                         | <1.0  | A new message will be written to a conversation. This can be an incoming, outgoing or system message. Subject was purpleIMessage until 1.2. |
| update-text            | prplIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIMessage.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIMessage.idl))                             | null                                         | 91    | An existing message is updated based on the `remoteId` of the message.                                                                      |
| update-buddy-status    |                                                                                                                                                                                                                                                                 | null                                         | <1.0  | away-ness or online/offline                                                                                                                 |
| update-conv-chatleft   |                                                                                                                                                                                                                                                                 | null                                         | <1.0  | the user has left the chat (it might remain visible)                                                                                        |
| update-conv-title      |                                                                                                                                                                                                                                                                 | null                                         | <1.0  |                                                                                                                                             |
| update-typing          |                                                                                                                                                                                                                                                                 | null                                         | <1.0  |                                                                                                                                             |
| preparing-message      | imIOutgoingMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl)) | null                                         | 1.5   | Outgoing message before it's been prepared for sending by the protocol                                                                      |
| sending-message        | imIOutgoingMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl)) | null                                         | 1.5   | Outgoing message was prepared (and potentially split into multiple messages) and will be sent by the protocol after this observer.          |
| chat-update-type       | null                                                                                                                                                                                                                                                            | null                                         | 91    | Conversation changed between being a chat and a direct message conv (`isChat` toggled).                                                     |
| update-conv-icon       | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))              | null                                         | 91    | The `convIconFilename` of the conversation changed.                                                                                         |
| update-conv-encryption | prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))              | null                                         | 91    | The `encryptionState` of the conversation changed.                                                                                          |

### imIConversation <a href="#imiconversation" id="imiconversation"></a>

| Topic                         | Subject                                                                                                                                                                                                                                                 | Data | Added | Notes                                                                                            |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ----- | ------------------------------------------------------------------------------------------------ |
| received-message              | imIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl)) | null | 1.5   | New message is about to be prepared for display.                                                 |
| new-text                      | imIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl)) | null | 1.5   | New message is about to be displayed. (Before 1.5 probably just passed along the `prplIMessage`) |
| new-directed-incoming-message | imIMessage([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIConversationsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIConversationsService.idl)) | null | 1.5   | New incoming message that is either a direct message or highlights the user.                     |

### imIUserStatusInfo

| Topic                     | Subject                                                                                                                                                                                                                                            | Data                        | Added | Notes |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ----- | ----- |
| status-changed            | imIUserStatusInfo([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIUserStatusInfo.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIUserStatusInfo.idl)) | Status Text (string)        | ?     |       |
| user-icon-changed         | imIUserStatusInfo([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIUserStatusInfo.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIUserStatusInfo.idl)) | New icon file name (string) | ?     |       |
| user-display-name-changed | imIUserStatusInfo([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIUserStatusInfo.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIUserStatusInfo.idl)) | New display name (string)   | ?     |       |
| idle-time-changed         | imIUserStatusInfo([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIUserStatusInfo.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIUserStatusInfo.idl)) | New idle time (number)      | ?     |       |


# Message Styles

[Chat Core](/thunderbird-development/codebase-overview/chat) uses a message style system based on HTML, JS and CSS that is very similar to the one created for [Adium](https://adium.im/). If you plan to create a message style, reading the Adium documentation on the topic may be helpful -- see this [tutorial](https://web.archive.org/web/20160408094746/https://trac.adium.im/wiki/CreatingMessageStyles/Tutorial) and this [reference sheet](https://web.archive.org/web/20160715205801/https://trac.adium.im/wiki/CreatingMessageStyles).

On the other hand, you may prefer to jump right in, using the default message styles as examples: [git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/components/im/messages), [searchfox link](https://searchfox.org/comm-central/source/mail/components/im/messages)

The rest of this page includes a variety of information about the specifics for creating a message theme for Thunderbird.

### Theme files <a href="#theme_files" id="theme_files"></a>

The minimal content of chrome/ is:

* Info.plist which contains metadata about the theme
* main.css
* Incoming/Content.html

The other files are optional:

* Style information in CSS files
  * **main.css** is the stylesheet used for the default variant of the theme, and is also included before the variant-specific stylesheet used for other variants.
  * Variants/*\<variant name>*.css contains the stylesheet for the variant *\<variant name>*.
* Other files used by the stylesheets (e.g. images)
* HTML files: these files are used to build the HTML markup of the conversation.

| File Name                 | Fallback if missing              | Usage                                                                                                                                              |
| ------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Footer.html               | None (will use an empty string)  | Displayed at the bottom of a conversation.                                                                                                         |
| Header.html               | None (will use an empty string)  | Optionally (user preference) displayed at the top of a conversation                                                                                |
| Status.html               | Incoming/Content.html            | Used for status messages                                                                                                                           |
| NextStatus.html           | Status.html                      | Used for status messages directly following another status message added a short time before.                                                      |
| Incoming/Content.html     | None (this file is **required**) | Used for incoming messages.                                                                                                                        |
| Incoming/Context.html     | Incoming/Content.html            | Used for incoming messages in an old conversation displayed to give context.                                                                       |
| Incoming/NextContent.html | Incoming/Content.html            | Used for incoming messages directly following another incoming message added a short time before.                                                  |
| Incoming/NextContext.html | Incoming/NextContent.html        | Used for incoming messages directly following another incoming message added a short time before in an old conversation displayed to give context. |
| Outgoing/Content.html     | Incoming/Content.html            | Used for outgoing messages.                                                                                                                        |
| Outoing/Context.html      | Outoing/Content.html             | Used for outgoing messages in an old conversation displayed to give context.                                                                       |
| Outgoing/NextContent.html | Incoming/NextContent.html        | Used for outgoing messages directly following another outgoing message added a short time before.                                                  |
| Outgoing/NextContext.html | Outgoing/NextContent.html        | Used for outgoing messages directly following another outgoing message added a short time before in an old conversation displayed to give context. |

## HTML Template Replacements <a href="#html_template_replacements" id="html_template_replacements"></a>

These labels will be replaced with message-specific data when used in HTML files bracketed by percentage signs, e.g. %chatName%.

### Replacements in header and footer templates <a href="#replacements_in_header_and_footer_templates" id="replacements_in_header_and_footer_templates"></a>

* **chatName**: Title of the conversation,
* **sourceName**: The account used for this conversation (the local alias is used if it is set, otherwise the account name is used).
* **destinationName**: The name of the conversation,
* **destinationDisplayName**: Title of the conversation,
* **incomingIconPath**: URL to the buddy icon of the person you are talking to. Will fallback to "incoming\_icon.png" if no icon is available,
* **outgoingIconPath**: Should be the URL to the buddy icon of the account used for this conversation. Currently, this always "outgoing\_icon.png",
* **timeOpened**, **timeOpened{format}**: The time when the conversation started, takes an optional format argument.

### Replacement in both messages and status messages <a href="#replacement_in_both_messages_and_status_messages" id="replacement_in_both_messages_and_status_messages"></a>

* **message**: The text of the message. The actual text will be wrapped in a span node, like this:

```
<span class="ib-msg-txt">message</span>
```

* **time**, **time{format}**: The time when the message was sent. Takes an optional format parameter (unfortunately, the formats supported are not the same on all the OSes as this internally calls the native strftime function of the OS),
* **timestamp**: The time when the message was sent, as an integer value (number of seconds since 1970). Useful to compute intervals between messages,
* **datetime**: The date and time when the message was sent.
* **shortTime**: The time when the message was sent,
* **messageClasses**: CSS classes that apply to the message. This can typically be used in a class attribute of an HTML node. Possible values include:
  * "action": message starting with /me,
  * "message": regular message (not status/system messages),
  * "incoming": incoming message,
  * "outgoing": outgoing message,
  * "autoreply": message sent automatically, for example to reply with an away message,
  * "event": system/status message,
  * "nick": chat message that contains your nick,
  * "error": error message (for example "\<user> as cancelled the transfer of \<file>"),
  * "delayed": delayed message (Currently this seems to be used only to show the recent message history when joining a Jabber chat room),
  * "notification": notification message (messages requesting the user's attention).

### Replacements in messages (incoming or outgoing) only <a href="#replacements_in_messages_-incoming_or_outgoing-_only" id="replacements_in_messages_-incoming_or_outgoing-_only"></a>

* **userIconPath**: URL to the buddy icon of the person you wrote the message. Fallbacks to "Incoming/buddy\_icon.png" if the icon is missing. For outgoing messages, it always uses "Outgoing/buddy\_icon.png",
* **sender**: The name of the sender of the message: the alias if one exists, or the username otherwise,
* **senderColor**: The color associated with the sender of the message. In chatrooms, this will contain a color string valid in a CSS rule. In IM conversations, it will be an empty string.
* **senderStatusIcon**: URL of an icon associated with the current status (idle, away, offline) of the sender of the message,
* **messageDirection**: Direction of the message. Always "ltr",
* **senderDisplayName**: Currently identical to **sender**. Should be the server-side alias in the future,
* **senderScreenName**: The username of the sender of the message,
* **service**: Name of the protocol through which the message transited (e.g AIM, MSN, XMPP, Google Talk, ...),
* **textbackgroundcolor**: Should be a color string based on the formatting information included in the message. Currently, this is always "transparent".

### Replacements in status messages only <a href="#replacements_in_status_messages_only" id="replacements_in_status_messages_only"></a>

* **status**: Should be a string indicating the nature of the event that caused this message to appear. Currently this isn't implemented, and the result is always an empty string,
* **statusIcon**: URL of an icon associated with the current status (idle, away, offline) of the other person in the conversation.

## Info.plist Keys <a href="#info.plist_keys" id="info.plist_keys"></a>

The file **Info.plist** is a [property list](http://en.wikipedia.org/wiki/Property_list) file containing metadata about the theme.

The following keys are used by Thunderbird:

* **DefaultVariant**: The name of the default variant. Optional. "default" will be used as the name if this key doesn't exist.
* **MessageViewVersion**: If the version number provided is >= 3, the main.css file will be used for all variants, otherwise it will be used only for the default variant (this is for compatibility with old Adium themes. In new themes, use the value 4).
* **DisplayNameForNoVariant**: The display name for the default variant (that is, when no variant is selected). Optional. If this key doesn't exist, a localized version of the string "Default" will be used for display in the theme selection UI.
* **DisableCombineConsecutive**: This will disable the use of NextContent.html/NextContext.html/NextStatus.html HTML templates. Consécutive messages won't be treated differently from other messages.
* **ActionMessageTemplate**: This is used to specify how action (/me) messages should be displayed. The value of this key will be used to replace %message% in Content.html, before doing the replacement. Optional. If this key doesn't exist, the default value "\* %message% \*" will be used.

The following keys work, but only use them if you really feel you absolutely need them, because they make it impossible for the user to select the font in the usual way via Preferences -> Content:

* **DefaultFontFamily**: the default font family to use for the conversation. Values in CSS stylesheet files can override this.
* **DefaultFontSize**: the default font size to use for the conversation. Values in CSS stylesheet files can override this.

If your theme needs to work with Adium too, you need more keys, see [this page](http://trac.adium.im/wiki/CreatingMessageStyles/SandBox/Tutorial1/MessageStyleInfoPlist) for details.

**Example:**

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ActionMessageTemplate</key>
    <string>&lt;span class="pseudo" style="%senderColor%"&gt;%sender%&lt;/span&gt; %message%</string>
    <key>DefaultVariant</key>
    <string>Normal</string>
    <key>MessageViewVersion</key>
    <integer>4</integer>
</dict>
</plist>
```

## Including JavaScript in message styles <a href="#including_javascript_in_message_styles" id="including_javascript_in_message_styles"></a>

The right file to put JavaScript in a message style is `inline.js`. Some Adium themes use a custom `Template.html` file to include JavaScript in the theme or include JavaScript in other theme files, this is **NOT** supported in Thunderbird.

The code that you insert in the `inline.js` file will be executed as soon as the conversation is loaded. It is a good place to register event listeners.

DOM Mutation events are of particular interest, because they allow you to execute scripts when a new message is added. See the [DOM Events documentation](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers).

**Example:**

This can be placed in the file `inline.js`:

```javascript
var body = document.getElementById("ibcontent");
body.addEventListener("DOMNodeInserted", function(aEvent) {
   if (!(aEvent.originalTarget instanceof HTMLElement))
     return;
   var node = aEvent.originalTarget;
   /* the node variable contains the inserted node,
      do something useful with it */
}, false);
```

## Thunderbird-specific features <a href="#instantbirdthunderbird-specific_features" id="instantbirdthunderbird-specific_features"></a>

Below lists the known differences between message styles in Adium and in Chat Core. Keep in mind that Adium displays conversation using Webkit and that Thunderbird uses Gecko, so the rendering may differ slightly.

### Files <a href="#files" id="files"></a>

* **NextStatus.html** Optional. Used for status messages that quickly follow another status message. If this file doesn't exist, Status.html will be used instead.

### Info.plist keys <a href="#info.plist_keys" id="info.plist_keys"></a>

* **ActionMessageTemplate** This is used to specify how action (/me) messages should be displayed. The value of this key will be used to replace %message% in Content.html, before doing the replacement. If this key is not provided, the default value "\* %message% \*" will be used.
* **NoScript** This theme does not include an `inline.js` file for JavaScript.

### CSS Classes <a href="#css_classes" id="css_classes"></a>

These classes can be used in the CSS files of themes.

* **ib-msg-txt** This class is present on all texts that are actually a message. The %message% replacement in the HTML templates actually adds a span node with this class around all messages.
* **ib-img-smile** This class is present on all img tags that were added in messages by the smiley system.
* **ib-nick** This class is present on participants' nicknames in multi-user chats (MUCs). It carries the "left" attribute when the participant has left the chatroom. You may need an !important if you wish to override the default styling of bold and coloured text. The colour hue of the nick (for use in CSS HSL colour values) is also available stored in the "nickColor" DOM attribute of the DOM element carrying the ib-nick class.
* [Mozilla-specific pseudo-classes](https://developer.mozilla.org/en-US/docs/Mozilla/Chat_Core/docs/Web/CSS/Reference/Mozilla_Extensions#Pseudo-elements_and_pseudo-classes), for example: \*:-moz-any-link

### Unread messages ruler <a href="#unread_messages_ruler" id="unread_messages_ruler"></a>

The ruler that appears between read and unread messages (from Thunderbird 38) is a `hr` element with the id **#unread-ruler**. When styling it, changes which override those in the default style for this element (in conv.css) must be marked `!important`.

Note: it is possible to style the unread messages themselves by using a CSS sibling selector on the unread ruler.

### Element IDs which must not be used by message styles <a href="#element_ids_which_must_not_be_used_by_message_styles" id="element_ids_which_must_not_be_used_by_message_styles"></a>

The following element IDs are used internally by Thunderbird and must not be given to any DOM elements by message styles:

* **insert-before**
* **actual-insert**
* **next-messages-start**
* **next-messages-end**
* **unread-ruler**
* **end-of-split-block**
* **Chat**
* **ibcontent**


# Keyboard shortcuts

## Conversation window <a href="#conversation_window" id="conversation_window"></a>

This is a list of keyboard shortcuts that are available in Thunderbird with brief descriptions of what they do.

{% hint style="info" %}
Depending on your OS, the **Command** key may be **Ctrl**.
{% endhint %}

**Command + F** Find

**Escape** to put the conversation on hold

**Command + W** to close the current tab

**Command + Shift + H** for History (Show Logs)

**Command + \[Plus key], Command + \[Minus key]** - sets the Zoom level

**Command + 0** - resets the zoom level to 100%. (Use the 0 key in the top row of the keyboard, not the 0 key of the numeric keypad.)

## Scrolling the conversation

**Shift + PgUp/PgDn** scrolls a page up or down

**Home/End** or **Alt + PgUp/PgDn** scrolls to the previous/next section. Sections are

* the beginning and end of the conversation
* the beginning or end of a session (in the log viewer)
* the first non-context message
* the unread message ruler.

**When the textbox is empty**, the usual navigation keys (**cursor keys, PgUp/Dn, ...**) will scroll the conversation view also from the textbox.

## Contact list <a href="#contact_list" id="contact_list"></a>

**Command + Arrow Up, Command + Arrow Down** to switch between conversations

**Command + Shift + Arrow Up, Command + Shift + Arrow Down** to switch between unread conversations

More available shortcuts are listed in the menu next to the commands.


# Chat Core Protocols

The [Chat Core](/thunderbird-development/codebase-overview/chat) code used by Thunderbird has some abstractions to deal with the differences between protocols (e.g. IRC vs. XMPP).

### Protocol Interfaces <a href="#protocol_interfaces" id="protocol_interfaces"></a>

Protocols are implemented in chat core using JavaScript.

Protocols must implement the proper interfaces and be registered with the category manager in order to be found. Protocols need to implement the prplI\* interfaces([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/)) (this can mostly be done using jsProtoHelper([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/modules/jsProtoHelper.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/chat/modules/jsProtoHelper.sys.mjs))). The minimum set of interfaces to implement are:

* prplIProtocol([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIProtocol.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIProtocol.idl))
* prplIAccount([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIAccount.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIAccount.idl))
* prplIConversation([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/prplIConversation.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/prplIConversation.idl))

#### Useful Code <a href="#useful_code" id="useful_code"></a>

* imXPCOMUtils([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/modules/imXPCOMUtils.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/chat/modules/imXPCOMUtils.sys.mjs)): Additional XPCOM utilities.
* JavaScript socket([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/modules/socket.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/chat/modules/socket.sys.mjs)): Simplified socket code.
* jsProtoHelper([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/modules/jsProtoHelper.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/chat/modules/jsProtoHelper.sys.mjs)): Includes basic JavaScript implementations of the interfaces and some helper code.
* XML HTTP Request helper([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mozilla/toolkit/modules/Http.jsm), [searchfox link](https://searchfox.org/comm-central/source/mozilla/toolkit/modules/Http.jsm)): Simplified HTTP request code

#### Example Implementations <a href="#example_implementations" id="example_implementations"></a>

The code for the JavaScript protocols we ship by default is here([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols)).

* IRC([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/irc), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/irc)): A full implementation, including private chats and MUCs, etc.
* JavaScript Test Protocol([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/jsTest), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/jsTest)): An extremely simple example meant to serve as test code for the interfaces.
* Matrix([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/matrix), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/matrix)): An implementation that heavily depends on an external SDK.
* XMPP([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/xmpp), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/xmpp)): A full implementation, including private chats and MUCs, etc. There are also other protocols which inherit and customize XMPP:
  * GTalk([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/gtalk), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/gtalk))
  * Odnoklassniki([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/protocols/odnoklassniki), [searchfox link](https://searchfox.org/comm-central/source/chat/protocols/odnoklassniki))
* There are also some stub implementations for protocols that Thunderbird used to support, but no longer does. These exist purely for the icons to show up and for a nice error message to appear when the account tries to connect.

### Useful Code Snippets <a href="#useful_code_snippets" id="useful_code_snippets"></a>

#### Using Services.core.getProtocols() to list all protocols <a href="#using_services.core.getprotocols-_to_list_all_protocols" id="using_services.core.getprotocols-_to_list_all_protocols"></a>

This lists the protocol plugins that the core service knows about. You can copy the code (as it is), paste it in the error console (linebreaks will automatically be ignored) and press "Enter" to run it.

```javascript
var { IMServices } = ChromeUtils.importESModule("resource:///modules/IMServices.sys.mjs");
let protocols = IMServices.core.getProtocols();
let result = "";
for (let p of protocols) {
  let proto = p.QueryInterface(Components.interfaces.prplIProtocol);
  result += proto.name + "\t\t" + proto.id + "\n";
}
console.log(result);
```


# Contacts

Contacts are at the heart of instant messaging, and thus the Chat Core has a way to abstract to a "person" (represented by an `imIContact`([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/chat/components/public/imIContactsService.idl), [searchfox link](https://searchfox.org/comm-central/source/chat/components/public/imIContactsService.idl)) instance), which might connect to multiple networks, etc.

{% hint style="warning" %}
*Draft: This page is not complete.*
{% endhint %}

### Display Name <a href="#display_name" id="display_name"></a>

The display name used in the buddy list window or in conversations can come from several sources, by precedence order:

* User-set alias, stored locally (set when the user renames someone from Thunderbird)
* Server-stored alias (some protocol store the aliases online)
* Display name / Friendly name (set by the remote contact, stored on the server)
* Username, the unique identifier of the buddy for this protocol. Can be numbers (e.g. ICQ, QQ), email addresses (e.g. MSN, XMPP) or some other string.

Possible storage locations:

* mozStorage (the file blist.sqlite)
* chat core (cached copy)
* server read/write (server-stored alias)
* server read only (display names)


# Mail Front-End

A.K.A. the 3-pane tabs and message tabs/windows

In January 2023 the mail front-end was rebuilt from scratch, replacing what evolved from the original Netscape front-end. This is a developers' guide to the new UI.

## Mail tab types

The mail front-end consists of two types of tabs (and a standalone window, more about that later) – the 3-pane tab `mail3PaneTab` and the message tab `mailMessageTab`. These are defined in mailTabs.js([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/base/content/mailTabs.js), [searchfox link](https://searchfox.org/comm-central/source/mail/base/content/mailTabs.js)) and provide the `tabInfo` objects for tabmail to control. Most code from outside the tabs will go through here in some form, although knowing the specific details should be unnecessary.

Each mail tab `tabInfo` object has these read-only properties:

* `chromeBrowser` – This is a XUL `<browser>` object which displays the tab's contents, either `about:3pane` or `about:message`. As with any `<browser>` object you can access the displayed page with the `contentWindow` and `contentDocument` properties.
* `browser` and `linkedBrowser` – Both refer to the XUL `<browser>` currently displaying content (an email message or a web page) to the user, or `null` if there isn't any.
* `message` – The currently displayed message as an `nsIMsgDBHdr`, if there is one.
* `folder` – The folder containing the currently displayed message, an `nsIMsgFolder`.

## Accessing tabs

If the mail tab you're interested in is the current tab, the following properties of tabmail point to it:

* `currentTabInfo` – The `tabInfo` object described above.
* `currentAbout3Pane` – The `window` object of the page displayed in the `chromeBrowser`, if the current tab is a 3-pane tab.
* `currentAboutMessage` – The `window` object of the message display page, which for message tabs is the page displayed in the `chromeBrowser`, and for 3-pane tabs a page within that.

If it's not the current tab, you can get the `tabInfo` object from tabmail and use the properties listed in the previous section to access it.

## Mail windows

The standalone mail window also contains a XUL `<browser>` displaying `about:message`. The browser can be accessed from the window's `messageBrowser` property.

## about:3pane

`about:3pane` is the main UI that users see when Thunderbird starts: the folder pane, the thread pane, and the message pane. It lives in the tree as about3Pane.xhtml([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/base/content/about3Pane.xhtml), [searchfox link](https://searchfox.org/comm-central/source/mail/base/content/about3Pane.xhtml)) and similarly named JS([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/base/content/about3Pane.js), [searchfox link](https://searchfox.org/comm-central/source/mail/base/content/about3Pane.js)), CSS([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/themes/shared/mail/about3Pane.css), [searchfox link](https://searchfox.org/comm-central/source/mail/themes/shared/mail/about3Pane.css)) and Fluent([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/locales/en-US/messenger/about3Pane.ftl), [searchfox link](https://searchfox.org/comm-central/source/mail/locales/en-US/messenger/about3Pane.ftl)) files.

### Folder pane

The folder pane displays the accounts and folders within them. Various modes of display are available.

### Thread pane

The thread pane displays the list of messages in the current folder, and the Quick Filter bar for filtering those messages.

### Message pane

This mesage pane contains more XUL `<browser>`s for displaying various things:

* `webBrowser` – displays web content in a child process as Firefox does.
* `multiMessageBrowser` – displays messages if more than one is selected.
* `messageBrowser` – displays a single message using `about:message`.

Only one is visible at any given time.

### Account central

If an account is selected in the folder pane instead of a folder, yet another `<browser>`, `accountCentralBrowser` – displays Account Central, a page of various things you can do in Thunderbird.

## about:message

`about:message` is all of the UI that displays a single message, including the message headers and attachments. It is used as the message pane in `about:3pane` and by itself as a message tab or window. Like `about:3pane` it lives in the tree as aboutMessage.xhtml([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/base/content/aboutMessage.xhtml), [searchfox link](https://searchfox.org/comm-central/source/mail/base/content/aboutMessage.xhtml)), aboutMessage.js([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/base/content/aboutMessage.js), [searchfox link](https://searchfox.org/comm-central/source/mail/base/content/aboutMessage.js)), messageHeader.css([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/themes/shared/mail/messageHeader.css), [searchfox link](https://searchfox.org/comm-central/source/mail/themes/shared/mail/messageHeader.css)) and about3Pane.ftl([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/locales/en-US/messenger/about3Pane.ftl), [searchfox link](https://searchfox.org/comm-central/source/mail/locales/en-US/messenger/about3Pane.ftl)) files.

Message contents themselves are displayed in a `<browser>` (if you're counting, we're now three deep) which can be accessed by the `content` property of an `about:message` `window`.


# Tutorials and Examples

From Hello World to Thunder Live Development videos, get acquainted with the codebase and learn how to contribute to the Thunderbird project.

## Hello World

{% content-ref url="/pages/-LaLUU92x4zbgiK1mxK8" %}
[Hello World Example](/thunderbird-development/tutorials-and-examples/hello-world)
{% endcontent-ref %}

Learn to do a simple Hello World example that will introduce you to the basics of navigating the Thunderbird code and making simple changes to the application's UI.

## Thunderbird Live Development Videos

{% content-ref url="/pages/-McTgQ6st59gsYA047pj" %}
[Thunderbird Live Development Videos](/thunderbird-development/tutorials-and-examples/thunderbird-live-development-videos)
{% endcontent-ref %}

Prefer watching videos? Check out recordings of live development sessions and learn how to navigate the Thunderbird codebase.


# Hello World Example

How to make a "Hello World" prompt in Thunderbird.

## Making Hello World in Thunderbird!

No project is a real project without a Hello World guide. In this guide we will create a function in JavaScript that triggers an alert that says "Hello World!" - then we will add a menu item that triggers that function. The idea is to give you a chance to dig through the code and make some fun changes beyond this guide, and learn how to change Thunderbird!

### function helloWorld()

The first thing we want to do is create the function that will run and create our JavaScript alert - this alert will create a new window with our message: "Hello World!"

Open up `mailCore.js` - this is the file that we'll be creating our function in. You can find this file in the content folder which is here: `comm -> mail -> base -> content`.

We can put our function almost anywhere in this file, so long as we don't put it within another function. Take a little while to look at this file and see if you can figure out what the other functions in this file are doing. Once you've done that find the function `openAboutSupport()`. If you are new to programming you can find this function easily is most editors by doing a search with **ctrl+f** to open up a search field - you can put `openAboutSupport()` in there and it should highlight that function.

After you find that function place the following code below it:

```javascript
function helloWorld(){
  alert('Hello, World!');
}
```

As noted above, this is a JavaScript function that will create an alert that will appear in the form of a window that Thunderbird generates that will say "Hello World!"

This is what it should look like in context:

{% tabs %}
{% tab title="mailCore.js" %}

```javascript
function openAboutSupport() {
  let tabmail = document.getElementById("tabmail");
  tabmail.openTab("contentTab", {contentPage: "about:support",
                  clickHandler: "specialTabs.aboutClickHandler(event);" });
}

function helloWorld(){
  alert('Hello, World!');
}
```

{% endtab %}
{% endtabs %}

### Triggering Hello World

For this tutorial we are going to create a new menu item in the App Menu (often called the hamburger menu) to call our `helloWorld()` function in `mailCore.js`.

{% hint style="info" %}
For this part of the tutorial we are going to interact with a XHTML file.
{% endhint %}

In the directory: `comm -> mail -> components -> customizableui -> content` - we are going to open the file `panelUI.inc.xhtml` and find the `"appmenu_help"` toolbarbutton (you'll likely want to use **Ctrl+F** again to find it).

Once you found the `appmenu_help` toolbarbutton, insert the following code below it:

```markup
<toolbarbutton id="hello_world"
               class="subviewbutton"
               label="Hello World!"
               oncommand="helloWorld();" />
```

In context:

{% tabs %}
{% tab title="panelUI.inc.xhtml" %}

```markup
<toolbarbutton id="appmenu_help"
               class="subviewbutton subviewbutton-iconic subviewbutton-nav"
               label="&helpMenuWin.label;"
               closemenu="none"
               oncommand="PanelUI.showSubView('appMenu-helpView', this)"/>
<toolbarbutton id="hello_world"
               class="subviewbutton"
               label="Hello World!"
               oncommand="helloWorld();" />
```

{% endtab %}
{% endtabs %}

### Trying it Out

Make sure all your work is saved and then you can build Thunderbird using the `./mach build` command. Once you have built Thunderbird with the changes, we can use `./mach run` to try out our modified version of Thunderbird.

Click to open the App Menu on the right hand side and you should see "Hello World" (pictured below):

![Hello World in the App Menu](/files/-LtVbmLb7VF9Rtgd0CC0)

When you click on the "Hello World" menu item, you should get an alert prompt (pictured below):

![](/files/-LtVbmLddWryEOf2izLL)

If that alert window appears when you click the menu item then it works!

### What's Next

Spend some time playing around with the menu and even try experimenting with the helloWorld function. Most of all have fun and don't worry about messing things up.

If you get in trouble you can reset the repository via the commands below (in the `/comm` directory) - **these will remove all the changes you've made**:

```
git reset --hard
```


# Thunderbird Live Development Videos

Follow along with UX Architect Alessandro as he works on various parts of Thunderbird, fixing bugs, changing the UX/UI, and showing how to participate in Thunderbird's development.

## Bug Fixes and Submitting Patches

In this development session Alessandro creates a patch to fix issues with the dark theme on Linux and explains how to create a patch and submit it for review.

{% embed url="<https://youtu.be/DJDBJqWAjDg>" %}

## UX/UI Design Process

Alessandro walks through the design process and how mockups are made, iterated upon, and the thinking behind the process.

{% embed url="<https://youtu.be/Y7mxJxvx7rs>" %}

## Folder Fixes and Searching the Codebase

In this session Alessandro works on fixing a bug with folders in Thunderbird, he also demonstrates how to search through the code in order to find what you are looking for.

{% embed url="<https://youtu.be/XD8BzmsM-iU>" %}

## Compose Window and Inspecting UI Elements

In this session Alessandro works on fixing some compose window bugs and explains how to work with the compose window interface and inspect the Thunderbird interface elements.

{% embed url="<https://youtu.be/wCaJG0jupE8>" %}

## Changing How Attachments Work

In this session Alessandro works on changing how attachments are displayed when composing in Thunderbird.

{% embed url="<https://youtu.be/V1lQa8bEgls>" %}

## Compose Window and Attachment UI

In this session Alessandro continues improvements to the compose windows and attachment interface. He also dives in-depth on styles and other bits that make up how the UI is built.

{% embed url="<https://youtu.be/fXofonRxCAY>" %}


# Fixing a Bug

Tutorial on how to fix a bug from beginning to end.

All the issues, bugs, work in progress patches, or updates related to Thunderbird, are listed on [Bugzilla](https://bugzilla.mozilla.org), and are properly organized per **Product**, **Component**, and **Status**.

## Create a Bugzilla Account

Creating an account is necessary in order to submit patches, leave comments, and interact with any other aspect of Bugzilla. If you're currently using a username in one of our Matrix chat rooms (e.g. [#maildev:mozilla.org](https://matrix.to/#/#maildev:mozilla.org?web-instance%5Belement.io%5D=chat.mozilla.org)), we recommend saving your profile name with the current format `Firstname Lastname (:username)` in order to be easily searchable and allow the Thunderbird team to offer better support.

## Find a Bug

Use the [Advanced Search](https://bugzilla.mozilla.org/query.cgi?format=advanced) section to find bugs you want to take care of, and be sure that the bug doesn't currently have any user listed as *Assignee* and the *Status* is set to `NEW`.

## Search for Code References

Making sense of the **Thunderbird** source code, and knowing where to look, will take some time. The code base is pretty big and if you never worked with `XBL` or `Custom Elements` it can be overwhelming at first. We recommend using our code search engine, [SearchFox](https://searchfox.org/comm-central/source/), or the [GitHub Source](https://github.com/thunderbird/thunderbird-desktop) to inspect the source code and find snippets and references to help you out while investigating a bug.

## Debugging Core Code

JavaScript code can be debugged using the built-in [developer tools toolbox](https://firefox-source-docs.mozilla.org/devtools-user/browser_toolbox/index.html). Debugging core C++ code requires [external tools](https://firefox-source-docs.mozilla.org/contributing/index.html).

## Creating Patches

### Checkout the Source Code

If you don't need to build Thunderbird, you can just get the Thunderbird source:

```
git clone https://github.com/thunderbird/thunderbird-desktop
```

If you plan to build with your changes, you'll first need to get the latest Firefox source code, and check it out into a local directory `source` (or however you want to call it). Then, get the latest Thunderbird source code. It needs to be placed **inside** the Mozilla source code, in a directory named `comm/`:

```
git clone https://github.com/mozilla-firefox/firefox source/
cd source/
git clone https://github.com/thunderbird/thunderbird-desktop comm/
```

### Configuring Git

To ensure your work is correctly attributed to you, and to make the reviewer's task easier, these options should be set in your Git configuration.

```
git config --global user.name "Your Full Name"
git config --global user.email "you@example.com"
```

### Committing a Patch

Make the changes you need in the codebase.

{% hint style="info" %}
If you are unsure of what changes you need to make, or need help from the mentor of the bug, please don’t hesitate to use the needinfo feature (“Request information from”) on Bugzilla to get the attention of your mentor.
{% endhint %}

After making your changes, visualize your changes to ensure you’re including all the necessary work:

```
# For files changed/added/removed
$ git status

# For detailed line changes
$ git diff
```

Then commit your changes:

```
$ git commit
```

## Commit messages

Using standard forms for commit messages not only looks better when looking at the revision logs, it also helps various automation tools parse the messages.

Commit messages should be of the form:

```
Bug xxxx - Short description of your change. r=reviewer

Optionally, a longer description of the change.
This can span multiple lines.
```

For follow-up commits that fix a problem with a lint test or other failure, the suggested form is:

```
Bug XXXX - Follow-up: Fixed lint failure. r?reviewer
```

When fixing a bug caused by a change made to mozilla-central, often referred to as "porting" a fix to Thunderbird, mention the upstream bug in the commit message like below. Doing so helps identify bugs that need uplifting to beta or release when the ported mozilla-central bug is uplifted.

```
Bug NNNN - Port bug ZZZZ: Useful short description. r?reviewer
```

Prefixing the first line of the commit message with "`WIP:`" marks the patch as a work-in-progress. `moz-phab` (see below) will pick that up and mark it as "Changes Planned".

## Picking reviewers

All changes need to be reviewed before acceptance into the codebase. It can be pretty tricky to figure out who to ask for a review.

Thunderbird code is divided into modules, each with an owner and peers. Generally, these are the best people to review your changes. Here's [the list of module owners, peers, and review groups](https://source-docs.thunderbird.net/en/latest/mots/index.html).

You can also run git log \` on the relevant files, and look who usually is reviewing the actual changes (ie. not reformat, renaming of variables, etc).

## Reviewing before submitting

To review your commit, run:

```
$ git log
```

To review your patch, run:

```
$ git show
```

## Working with a stack of patches

[More information on how to work with stack of patches](https://firefox-source-docs.mozilla.org/contributing/stack_quickref.html#working-with-stack-of-patches-quick-reference)

## Submitting a Patch

The way to submit a patch is via [Phabricator](https://moz-conduit.readthedocs.io/en/latest/phabricator-user.html).

There is a command line tool, `moz-phab`, which makes it easy to submit local changesets as patches.

See the [moz-phab setup and installation](https://moz-conduit.readthedocs.io/en/latest/phabricator-user.html#setting-up-mozphab) docs.

Once you want to submit your patches (make sure you use the right commit message), run:

```
$ moz-phab
```

It will publish all the currently applied patches to Phabricator and inform the reviewer.

If you wrote several patches on top of each other, run:

```
$ moz-phab submit <first_revision>::<last_revision>
```

It'll ask for confirmation before uploading, so don't worry too much about accidental submissions.

`moz-phab` will pick the bug number out of the commit message (`Bug xxxx`), and link back to the bugzilla bug. If there is a reviewer (`r=...`), it will automatically assign them and send them a notification. You can leave the reviewer out, but then one will have to be manually assigned via the phabricator web page. If the commit message starts with "`WIP:`", the patch will be marked "Changes Planned".

You can find more details in the `moz-phab` [README](https://github.com/mozilla-conduit/review/blob/main/README.md#submitting-commits-to-phabricator).

## Updating a submitted patch

It is rare that a reviewer will accept the first version of patch. Moreover, as the code review bot might suggest some improvements, changes to your patch may be required.

If your patch is not loaded in your working directory, you first need to re-apply it:

```
$ moz-phab patch D<revision_id>

# Or you can use the URL of the revision on Phabricator
$ moz-phab patch https://phabricator.services.mozilla.com/D<revision_id>
```

Make your changes in the working folder and run:

```
$ git commit --amend
```

After amending the patch, you will need to submit it using moz-phab again.

{% hint style="warning" %}
Don’t use `git commit --amend -m`.

Phabricator tracks revision by editing the commit message when a revision is created to add a special `Differential Revision: <url>` line.

When `--amend -m` is used, that line will be lost, leading to the creation of a new revision when re-submitted, which isn’t the desired outcome.
{% endhint %}

If you wrote many changes, you can squash or edit commits with the command:

```
$ git rebase -i
```

The submission step is the same as for the initial patch.

[More information on how to work with stack of patches](https://firefox-source-docs.mozilla.org/contributing/stack_quickref.html#working-with-stack-of-patches-quick-reference)

## Update the working directory

If you’re finished with a patch and would like to return to the tip to make a new patch:

```
$ git pull --rebase
```


# Bug Triaging 101

Tutorial on how to dive into triaging bugs.

How to efficiently triage bugs and contribute to a clean and organized Bugzilla

## Expectations

The best way to optimize your time and drive a productive triaging session is to set limited expectations, and only tackle a limited amount of bugs per day. It’s easy to feel overwhelmed after triaging 10 bugs (some of which may be difficult) and seeing that in the meantime 50 more bugs have been reported.

Try to manage your expectations and accept that what you are doing, as small as it can look, is important and vital for the success of Thunderbird.

## Requirements

Not all of these are hard requirements, but some nice to have configurations that will make it easier to triage complex reports.

Have the ability to test on all the Operating Systems (bare metal or VMs), and should not be blocked by technical limitations as much as possible. Run all currently supported versions (Daily, Beta, ESR). Ability to run [moz-regression](https://mozilla.github.io/mozregression/) and familiarity with the tool.

## Rules of the Road

* **Always be respectful** and polite even when faced with a difficult reporter, because a) this is a customer facing environment, b) being empathetic is healthy for both you and the reporter. Doesn’t mean you have to engage with impolite reporters or accept abuse (see 3rd point below). *“Thank you”*, and *“please”*, and similar ideas go a long way to encouraging users to engage and return.
* **Acknowledge the issue**, e.g.: *“I’m sorry you are having some issues, could you please provide me with the following information so that I can look into it…”*
* **Don’t accept harassment** from a reporter, if they violate our community rules mark the comment as abuse and close the bug as invalid.
  * If the bug is a real issue, recreate it in a new bug. Allowing abuse in Bugzilla sets a bad precedent and is unhealthy for everyone involved and the entire community.
  * *E.g.: “You guys are idiots. This is disgusting. The Thunderbird Team/you are stupid”.*
* Do not drive users against developers, and do not argue with other team members in a bug. The MZLA team is to always have a united front on Bugzilla.
  * *E.g. Triager finds the regression and pulls in the developer. Developer says that it was intended that this feature would work differently (or is now unsupported).*
  * *Another core developer joins the conversation and publicly disagrees with the previous developer’s statement.*
  * ***Don’t argue in the bug - take it to another internal forum to discuss, come back with a united front on how to proceed.***
* **Don’t promote your own point of view.** A triager should be unbiased - is it a bug? Is it reproducible? That’s it.
* **Do not try to create a solution to the bug.** Leave the ideation and implementation of a solution to the developers, designers, and product management as there are likely many considerations that go into a resolution.
* **Stay neutral and take a step back when things are hard to handle.** We have 20 million users, lots of bugs and lots of negativity will come in. A bug report comes from an emotional reaction, likely a negative experience. You need to detach from the emotional aspect of any bug and not escalate emotions, but also not take it as a personal attack.

## How to triage

* **Don’t triage bugs that you have no idea what they are.** Stick to your area of expertise!
  * If you’re in the back-end, don’t try to triage front-end bugs, and vice versa.
  * No need to add noise to bugs if you have no clue what the topic or context is.
* **Stick to your strengths** and to the areas you worked on. Participate in the bugs that you know what the reporter is talking about and you have historical knowledge of that area.
* **Point users to KB articles** so they learn to use them, and hopefully are then less likely to report things that are not bugs and more likely to make good bug reports.
  * Examples: [Troubleshoot Mode](https://support.mozilla.org/en-US/kb/cannot-receive-messages), [not receiving messages](https://support.mozilla.org/en-US/kb/cannot-receive-messages) - in general items under <https://support.mozilla.org/en-US/products/thunderbird/fix-slowness-crashing-error-messages-and-other-problems>. (If an existing article is inaccurate, please ping Roland, Heather, or Wayne to get to get it updated)
* Try to first reproduce the issue reported.
* **Try to add all the flags and data points in one go** in order to limit email noise for all the people CC’ed on those bugs.
* **Don’t let your time go to waste**. If you have read a bug but are unable to confirm or add significant info (you can’t reproduce, etc) - at a minimum: assign a component, a better summary, or NI or CC someone who can take a next step.


# Bug Status Classicification

When setting a bug priority and severity classification, note that you can click on Priority in the Category section to go to [the mozilla page with the different levels](https://wiki.mozilla.org/BMO/UserGuide/BugFields#priority) and when to set them.

## The Bug is REPRODUCIBLE

* Move the bug into the **correct component** if it’s not already there.
* Mark it as **NEW** if it’s still **UNCONFIRMED**.
* Add the **triaged** keyword (that will require a severity level).
* Set **Priority** and **Severity** if you feel comfortable, or ask a manager or module owner to help you define those.
* Add **other keywords** if relevant (ux, access, perf, etc.).
* If it’s a **regression**, try to find the bug that regressed that feature and add it along with the REGRESSION keyword. <https://mozilla.github.io/mozregression/> is a good tool for you and reporters.
  * If you can’t find a regression, add the **regressionwindow-wanted** keyword.

## The Bug is NOT REPRODUCIBLE

* Ask the reporter to try in [troubleshoot mode](/thunderbird-development/fixing-a-bug/bug-triaging/bug-status-classification) without any add-on.
* Ask for more info if not already provided, like OS or their particular setup.
* Try to get more clear instructions - users tend to understand better if they are asked to make a list of actions or clicks they used.
* DON’T simply write “It works for me”, that’s not helpful and it’s just noise, and it can increase the frustration of the reporter.
* Issues are very situational, as they might be caused by errors in C++, JavaScript, translation, a custom configuration from the user, or many other things.
  * For instances in which a tab is blank or something doesn’t load as expected, ask the user to check the Error Console and report any messages in there (ctrl+shift+J or cmd+shift+J for macOS)

## The Bug is a DUPLICATE

* Bugzilla does a terrible job of suggesting potential duplicates when the user files a bug, so you will stumble upon the same issue reported by different users.
* Try to identify the duplicates and close them by adding the original bug that we use as a reference. Please give a reason for duping, a) to educate the user, b) so user doesn’t feel like we are just closing bugs and brushing them off. Boilerplate text:
  * This issue is being investigated in bug XXXX. If you think that bug is missing important information that will help lead to fixing or reproducing it, please add it to that bug.


# Bug Types

A bug report can be of 3 different types

1. **Enhancement** Bugs that are requests for new features, or modifications of existing features. Reports that the user thinks might be incorrect behavior could be classified as bugs, but if that feature behaves as intended, the request is not a bug but a RFE (Request for Enhancement).
2. **Bug/Defect** Broken functionality, not working properly, crashes, and pretty much anything that is not working as expected.
3. **Task** Limit the use of Task to meta bugs or internal efforts related to code clean up, architectural restructuring, tests or telemetry implementation. If it affects a user of a current release, it is better categorized as a Bug.


# Garbage Collection

You will see bugs that have been around for years and received very little activity, or bugs that were originally opened as means to track new features, but went out of scope as the overall direction of the project changed.

Closing those bugs will help clean up our Bugzilla searches and keep things more organized.

* Features request (RFE) that are outside our current or future scope.
* Bugs that don’t apply anymore (E.g. old address book issues, UI that has been replaced).
* Bugs left open with the intention of one day fixing it, but do not align with our scope.


# Narrow the Scope

* **Set an accurate version number**, the earliest possible/likely version where it reproduces is ideal (eg. the version # of the bug causing the regression). “Trunk” or “unspec” should be changed to an actual number.
* **OS** should be set if the issue is OS specific. (also add to the bug summary as a keyword, eg. “Mac”)
* **Summary is key.** Improving the summary speeds up understandability and searchability - your improvement can save everyone valuable time. 1) remove extraneous words. 2) add key words that express how the user experiences the issue. 3) add technical/coding info if appropriate, but never remove user symptoms from the summary.


# Lint and Format Code

How to lint and format code.

Thunderbird's source code is linted and formatted using automated tools, which provides several benefits that include:

* ensuring a consistent formatting style across the code base
* preventing formatting issues from taking up developer time in code review
* catching problems that might otherwise go unnoticed
* making it easier for developers to write well-formatted code

Mozlint is a library that standardizes linter configuration and provides an interface for running all linters at once. It is run via mach and is run automatically by Taskcluster.

## Running Linters Locally

You can run all the various linters in the tree using the `mach commlint` command. Simply pass in the directory or file you wish to lint (defaults to current working directory):

```
../mach commlint path/to/files
```

It works the same as regular `mach lint`, however it has Thunderbird configs pre-applied. If you're not familiar with using `mach lint` on Firefox code, see the Firefox [Linting](https://firefox-source-docs.mozilla.org/code-quality/lint/index.html) documentation to get started.

## Configuring Mozlint for Thunderbird

### Suite code

The Mozlint configuration files in `comm/tools/lint` are written so that they can be shared with the Seamonkey project. Thunderbird developers may want to set `MOZLINT_NO_SUITE=1` in their environment so `mach commlint` will not check `comm/suite/`. Taskcluster will also set `MOZLINT_NO_SUITE` when running lint checks.

## Taskcluster

Several mozlint-based checks run automatically on Taskcluster, and more are being added. The Firefox [Linting](https://firefox-source-docs.mozilla.org/code-quality/lint/index.html) documentation is a valuable resource if you're seeing errors on your try builds. If you are not sure how to run a check locally, the mach commands run by Taskcluster are in `comm/taskcluster/ci/source-test/mozlint.yml`.


# Using ESLint to Format Javascript Code

Using ESLint to Format Javascript Code

For JavaScript code we use both:

* [eslint](https://eslint.org/) - linting tool
* [Prettier](https://prettier.io/) - formatting tool

These tools can be used via the command line or right in your code editor.

## Via the Command Line

After editing some JavaScript code, navigate to the `comm/` directory. (The following commands need to be run from the `comm/` directory so that Prettier will use the `comm/.prettierignore` file, and not the `.prettierignore` file in the directory just above `comm/`. See [Prettier issue 4081](https://github.com/prettier/prettier/issues/4081).)

For a single file, run this command, which will attempt to automatically fix any linting or formatting problems:

```
$ ../mach commlint path/to/a/file.js --fix
```

Or for all the files in a given directory:

```
$ ../mach commlint path/to/a/directory/ --fix
```

To simply report any problems but not attempt to automatically fix them, just omit the `--fix` flag:

```
$ ../mach commlint path/to/a/file.js
```

## In a Code Editor

Most popular code editors offer plugins for eslint and Prettier. We highly recommend installing a plugin for eslint and a plugin for Prettier so you can lint and format your code as you are editing it. Issues will be highlighted as you type and you can have Prettier format your code with a few key strokes.

Here are links to plugins for various editors:

* [eslint plugins](https://eslint.org/docs/user-guide/integrations) for various editors
* [Prettier plugins](https://prettier.io/) for various editors

Some of us on the Thunderbird team use the VS Code editor with these plugins:

* [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)

  VS Code plugin by Esben Petersen
* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)

  VS Code plugin by Dirk Baeumer

## More Details

* ['Linting' on firefox-source-docs](https://firefox-source-docs.mozilla.org/tools/lint/index.html)
* ['Formatting JS Code With Prettier and eslint' on MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style/Formatting_JS_Code_With_Prettier_and_eslint)


# Try Server

The Thunderbird try server works in exactly the same way to the [Firefox try server](https://wiki.mozilla.org/ReleaseEngineering/TryServer) with a few minor differences. The automation is based on the same hardware and tools, so there should be few differences.

{% hint style="info" %}
Thunderbird's Try server is often referred to as "try-comm-central", or "try-cc", but usually it's just known as Try. Use an upper-case T if it makes things clearer.
{% endhint %}

## Getting access to the Try Server

To use the try server, you'll need [Level 1 Commit Access](http://www.mozilla.org/hacking/commit-access-policy/). You can learn more about Mozilla's commit access policies and start the process of signing up for an account here: [Becoming a Mozilla Contributor](http://www.mozilla.org/hacking/committer/).

### Configure your SSH Host

You need to make sure your `~/.ssh/config` is properly configured to use the correct SSH username for the Mozilla Mercurial repository.

Your SSH config file should have something like this:

```bash
Host hg.mozilla.org
  User nemo@thunderbird.net
```

Your **User** should match the SSH username that has been granted [Level 1 Commit Access](http://www.mozilla.org/hacking/commit-access-policy/).

## Adding Try to your Mercurial configuration

This is only required if your pushing to try from Mercurial.

Try server has a separate repository based upon comm-central. You'll need to add the address to your Mercurial configuration file at `path/to/comm-central/.hg/hgrc`:

```
[paths]
default = https://hg.mozilla.org/comm-central
try-cc = ssh://hg.mozilla.org/try-comm-central
```

You can of course [access the repository via HTTP](https://hg.mozilla.org/try-comm-central/), but not push to it, hence the ssh:// address.

{% hint style="info" %}
The name of the comm-central try server must be different from `try`. There's\
an alias in the Mozilla Mercurial configuration that will override this\
configuration if the server is named `try`. The instructions below assume the\
name `try-cc`.
{% endhint %}

## Pushing to Try

Having gained level 1 access and configured Mercurial, you can push to Try. In general, it's just a matter of applying your patch(es), selecting which tasks to run, and pushing to try-comm-central.

### Pushing to Try (New process for Git)

Since moving Thunderbird source code to Git, we now support running `../mach try`, aligning Thunderbird’s try submission workflow with Firefox.

One important difference is that `../mach try` must be run from the `comm` repository when submitting to Thunderbird try, whereas Firefox submissions are made from the `source` repository.

For complete details on using `../mach try`, refer to the Firefox documentation for [Pushing to Try](https://firefox-source-docs.mozilla.org/tools/try/index.html).

While reading the Firefox documentation, note the following Thunderbird-specific differences:

* The GitHub repository for Thunderbird is `https://github.com/thunderbird/thunderbird-desktop`
* The unified Mercurial repository for Thunderbird is `comm-unified`
* The Thunderbird try repository is `https://hg-edge.mozilla.org/try-comm-central`

#### Choosing what tasks to run

You can (and should) control what testing tasks you want to run on your push. Several task selection methods are available, including:

* `../mach try fuzzy` where an interactive interface opens that allows you to select what tasks to run ([see more details](https://firefox-source-docs.mozilla.org/tools/try/selectors/fuzzy.html#understanding-the-interface))
* `../mach try fuzzy --query <query>` where the query selects the tasks rather than opening an interactive interface ([see more details](https://firefox-source-docs.mozilla.org/tools/try/selectors/fuzzy.html#writing-queries))
  * `../mach try fuzzy --and -q "mochitest" -q "windows"` is an example of running all windows mochitest tasks
* `../mach try chooser` is similar to `../mach try fuzzy` except you can choose tasks from a web interface ([see more details](https://firefox-source-docs.mozilla.org/tools/try/selectors/chooser.html))
* `../mach try again` to re-push your last try push or a previous push - ([see more details](https://firefox-source-docs.mozilla.org/tools/try/selectors/again.html))
* `../mach try empty` to push to try but not schedule any additional tasks - ([see more details](https://firefox-source-docs.mozilla.org/tools/try/selectors/empty.html))

### Pushing to Try (Old process for Mercurial)

In general, it's just a matter of applying your patch(es) and running `hg push -r . try-cc` if you're planning to manually trigger tasks from the **Taskcluster** web interface.

For pushes via the command line, we recommend using the `push-to-try` extensions in order to simplify the commands required to automatically trigger jobs and tasks on the try server.

An example of a command to trigger all *mochitest* jobs for an artifact build will look like this:

{% code overflow="wrap" lineNumbers="true" %}

```bash
hg push-to-try -s ssh://hg.mozilla.org/try-comm-central -m "try: -b o -p all -u mochitest --artifact"
```

{% endcode %}

Read the full list of [try syntax commands](#try-syntax) to write in your commit message.

{% hint style="warning" %}
Pushing to try-comm-central will create builds using the **most recent** mozilla-central code, which may or may not be a good idea at the time. Generally it's okay, but there may be unresolved problems between the two repositories. If you strike a problem, ask in the [#maildev Matrix chat room](https://chat.mozilla.org/#/room/#maildev:mozilla.org).\
\
You can also work with a **specific** mozilla-central revision, see "Testing mozilla-central patches" below.
{% endhint %}

#### Choosing what tasks to run

You can (and should) control what testing tasks you want to run on your push. There are several methods to do so:

**Try syntax**

This is the easiest and most common way. A special code (known as Try syntax) is put in the commit message of the tip-most revision being pushed, for example `try: -b o -p linux64 -u all` creates only an "opt" build on 64-bit Linux, and runs all of the tests on that build.

Here is the Try syntax try-comm-central understands:

* `-b` Build type. Use `o` for an opt build (most common), `d` for a debug build, or `do` for both.
* `-p` Platform. There are five platforms. Each also has a `-shippable` variant, which is a complication you probably don't need to think about.
  * `linux` 32-bit Linux
  * `linux64` 64-bit Linux
  * `macosx64` Mac OS (if you don't need a debug build, specify `macosx64-shippable` instead)
  * `win32` 32-bit Windows
  * `win64` 64-bit Windows
  * `all` for all platforms
* `-u` Unit test suites.
  * `mochitest-thunderbird`
  * `xpcshell`
  * `marionette`
  * `all`
* `--artifact` Artifact builds. See the [Artifact Builds page](/thunderbird-development/building-thunderbird/artifact-builds) for more information.

**Try task configuration**

For more control, a special file named `try_task_config.json` and containing a list of the tasks to run is included in one of the pushed revisions.

The contents of the file look like this:

```
{
  "version": 1,
  "use-artifact-builds": true,
  "tasks": [
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-1",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-2",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-3",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-4",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-5",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-6",
    "test-linux1804-64-qr/opt-mochitest-thunderbird-e10s-7",
    "test-linux1804-64-qr/opt-xpcshell-e10s-1",
    "test-linux1804-64-qr/opt-xpcshell-e10s-2",
    "test-linux1804-64-qr/opt-xpcshell-e10s-3",
    "test-linux1804-64-qr/opt-xpcshell-e10s-4"
  ]
}
```

`use-artifact-builds` tells the Try server to do an artifact build. Set to false or remove it for a full build. See the [Artifact Builds page](/thunderbird-development/building-thunderbird/artifact-builds) for more information.

`tasks` is a list of tasks to run. In this example it's all of the 64-bit Linux tests. A 64-bit Linux build will also run, because it is required by the tasks specified.

A copy of the file with all available tests is maintained [here](https://github.com/darktrojan/mozconfigs/blob/master/try_task_config.json). A typical workflow would be to copy the file into your working directory, remove the tasks you don't want to run, and commit it. For efficiency you could export the commit as a patch and import it again when needed.

{% hint style="info" %}
Task configurations and names change over time. If you're not getting the tasks you requested, this may be why.
{% endhint %}

To find the name of any particular task, click on existing instance in Treeherder, then look for the "job name" in the lower-left corner of the page.

**Adding tasks to an empty Try run**

If you commit with neither Try syntax nor a `try_task_config.json` file (or you want to add to an existing run), you can one or more tasks using Treeherder. Once the decision (D) task has completed, click the drop-down arrow to the right of it, and choose "Add new jobs".

## Get an installable build from a Try run

When the build at `https://treeherder.mozilla.org/jobs?repo=try-comm-central` is complete (normally takes 1-2 hours):

* Click the green "B" (for build) or "Ba" (for artifact build) next to one of the following: "Windows opt", "Windows AArch64 opt" (for Windows ARM computers), "Linux opt", "macOS opt", "macOS AArch64 opt" (for non-Intel Macs only), or any variant with "opt" replaced with "Shippable" (or "debug" if instructed to use a debug build).\
  ![](/files/UxYfHO8PCKCxqgxZKyWh)\\
* In the black header below click "Artifacts and Debugging Tools".
* In the Artifacts section, to download the install file click on `target.installer.exe` (Windows), `target.tar.bz2` (Linux), or `target.dmg` (Mac).\
  \
  ![](/files/j5ytxvJV32PI9Q0qe0eS)\
  \\
* Install the downloaded file.

## Testing Firefox patches

If you have changes that affect Firefox main, you may wish to do a Try run to check Thunderbird isn't broken. Here's how:

1. In the root directory of your Firefox repository, apply your patch. Then run `./mach try empty` to push to the mozilla-central Try repository. You'll need to know the revision number of your push, which will be in the message printed to the console.
2. Move to the root directory of your Thunderbird repository.
3. Modify the file `.gecko_rev.yml` – change `GECKO_HEAD_REPOSITORY` to [`https://hg.mozilla.org/try`](https://hg.mozilla.org/try), and `GECKO_HEAD_REV` to point to the revision from step 1.
4. Now push to try-comm-central as per usual.

You can change `.gecko_rev.yml` to point to any revision on the mozilla-\* trees to test your comm-central patch against them.

{% hint style="info" %}
It's not required, but you *should* base your Thunderbird patch on a known good revision of Thunderbird main (probably the tip), and your Firefox patch on the corresponding Firefox revision (also probably the tip). Otherwise changes made to one tree but not the other (such as build configuration changes) can cause problems.

To find the matching revision, open the log of the comm-central decision (D) task and search for "built from mozilla-central revision".
{% endhint %}

## Testing Beta, Release, or ESR patches

When doing a Try run for patches to Beta, Release, or ESR, the steps are the same as above. (For example, you do not need to change anything in your `hgrc` file or git origin.) The try server is smart enough to automatically detect which one to build and test. This works because of the `.gecko_rev.yml` file. Note that some things might not work the same way as on `comm-central` (e.g. the `--artifact` option only works on `comm-central`).


# Landing a Patch

Tutorial on how to land approved and reviewed patches to Thunderbird

## Getting access to land patches

To land patches to Thunderbird, you'll need [Level 3 Commit Access](http://www.mozilla.org/hacking/commit-access-policy/). You can learn more about Mozilla's commit access policies and start the process of signing up for an account here: [Becoming a Mozilla Contributor](http://www.mozilla.org/hacking/committer/)

## Before landing patches

**Is the tree open?** Thunderbird main is synced to comm-central where CI is run. Check [TreeHerder](https://treeherder.mozilla.org/#/jobs?repo=comm-central) – the name of the tree in the top-left corner shows you the status of the tree. Usually it's "open" (a green circle is displayed), which means you can land. Other statuses are "approval required" (yellow padlock) and "closed" (red X) which mean you can't land without permission, and in fact the server will prevent you from doing so.

**Is it a good time to land patches?** The best time to land patches is shortly after Mozilla updates the Firefox main branch. Since Thunderbird builds on top of Firefox, landing at this time will ensure that the build will get the latest changes.

Mozilla usually updates the Firefox main branch around **0400**, **1000**, **1600**, and **2200** **UTC** on weekdays and **1000 and 2200 UTC** on weekends (give or take an hour). If one of these times is approaching, it's probably not a good time to land. You can check the [Firefox main branch on GitHub](https://github.com/mozilla-firefox/firefox/commits/main/) or [mozilla-central on TreeHerder](https://treeherder.mozilla.org/#/jobs?repo=mozilla-central) to see when they last pushed.

{% hint style="warning" %}
Coordinate with others in the [#maildev Matrix chat room](https://chat.mozilla.org/#/room/#maildev:mozilla.org) as they may already be planning to land. A Firefox push is usually followed by an assigned person landing something to check the build is not broken.
{% endhint %}

**Is there a build in progress already?** If there is, please wait until you're reasonably sure the first build is not broken. In most cases this means that the Linux and OS X builds (B) are complete and tests (bct, X) are starting to turn green (free from major failures).

**Is the tree green?** If it isn't, do not land. Landing patches on top of an already broken build wastes resources (both computing and human).

{% hint style="warning" %}
Landing patches to Thunderbird will create builds using the **most recent** Firefox main code, which may or may not be a good idea at the time. Generally it's okay, but there may be unresolved problems between the two repositories. If you strike a problem, ask for help in the [#maildev Matrix chat room](https://chat.mozilla.org/#/room/#maildev:mozilla.org).
{% endhint %}

## Landing patches to Thunderbird main

Lando is used to land phabricator patches into the Thunderbird main branch. All patches must land via Lando.

The [Lando User Guide](https://moz-conduit.readthedocs.io/en/latest/lando-user.html) has details on how to land patches.

Having gained level 3 access, you can land code to the Thunderbird main branch.

{% hint style="danger" %}
You should not land patches without having completed a successful try build [Try Server](/thunderbird-development/fixing-a-bug/try-server).
{% endhint %}

### Steps before landing patches

These are a series of recommended steps to always go through before landing patches.

Be sure your commit message is clear and has been approved during review. The standard syntax of a commit message is `Bug 000000 - Description of the patch and fix. r=reviewer`.

{% hint style="warning" %}
Always check the reviewer in a commit message matches the person who actually reviewed the patch. A patch could be reviewed by someone other than the originally intended person, or it could have been sent to a group of reviewers.
{% endhint %}

As a new contributor, you probably don't need to land your own patches. Instead add the keyword `checkin-needed-tb` to the Bugzilla bug, and one of our friendly sheriffs will land it for you.

After landing to the Thunderbird main branch, the code will sync to Mercurial where CI will run. You can take a look at [TreeHerder](https://treeherder.mozilla.org/#/jobs?repo=comm-central) to see the changes show up at the top of the list.

{% hint style="warning" %}
If your patch is faulty (i.e. it breaks the build or fails tests), it may be backed out without any warning. It's up to you to fix it.

The tree is monitored for failures by a team of people and although they are nice people they are not expected to tolerate your broken patch. A tree without failures is much easier to work with for everybody concerned.
{% endhint %}

### Commit message magic words

Adding some magic words to the commit message of the tip-most commit will cause the build system to do different things. Getting it wrong or making a typo will not get the desired result.

* `DONTBUILD` tells the build system not to build on this patch. Only the decision and linting tasks will happen, unless another process comes along and starts a build, such as the Daily automatic build.
* `CLOSED TREE` allows you to land to a closed tree. I hope you have permission!
* `a=approver` You must specify who approved the changes on some trees (not Thunderbird main).

## After landing

If you worded your commit message correctly, a bot will post a message in your bug with a link to the changes you made, and close the bug. To prevent the bot from closing a bug, add the `leave-open` keyword to the bug before landing. The bot will automatically remove the `checkin-needed-tb` flag if it is set.

At this point you should set the Target Milestone field in the bug to the current version, which is generally, but not always, the last option for that field.

## Landing patches to beta, release, or ESR

You do not need to land patches on beta, release, or ESR. We have authorised people to do that for you.

Request approval on Bugzilla in the same way you request review, using the `approval-comm-beta` and `approval-comm-esrXX` flags. At an appropriate point approval will be granted (or denied!) and your patch will be landed for you. Filling out the request form, especially the "Regression bug" and "Risk" fields, will help the approver prioritize the patch.

When requesting uplift approval for a bug with multiple patches, a single request is sufficient. It's helpful to specify in the request that multiple patches are to be uplifted. Likewise, if a bug has dependencies on another bug, specifying those dependencies and what order they should be uplifted can save a lot of time.

Uplifting patches to earlier versions is for fixes to major bugs, and regressions that break the user interface. It should not be used as a shortcut to get new features to users earlier (some exceptions apply). The release channels ensure that changes are exposed to a test audience for a period of time before being shipped to all users.

The esr branch in particular can be difficult to uplift patches to because of code-churn since the repository was created. Sometimes it's necessary to create a patch specifically for esr. In these cases, attach the patch in Bugzilla rather than Phabricator. The fix still needs to be applied to main and beta branches first unless the bug really only affects esr. (Bugs that only affect esr are actually quite rare. At some point the bug most likely was present on main and was fixed.)


# Care and Feeding of the Tree

## Landing "checkin-needed-tb" patches

Patches can land on Thunderbird's main branch at any time, but in general we try to organise this around when Firefox main changes. Since the Firefox code can break Thunderbird in any number of ways, and they won't stop to wait for us to catch up, we try to meet every Firefox push to main with a push of our own.

{% hint style="info" %}
Firefox main mirrors to mozilla-central, and Thunderbird main mirrors to comm-central. CI/CD is run against mozilla-central and comm-central.
{% endhint %}

Sheriffs aim to merge changes to Firefox main at (very roughly) 0400, 1000, 1600 and 2200 UTC. After a merge, something should land on Thunderbird main to start a new build. Land something of your own or check for bugs flagged [checkin-needed-tb](https://bugzilla.mozilla.org/buglist.cgi?keywords=checkin-needed-tb).

## Closing or Opening the Tree

In extreme circumstances the tree can to be closed to prevent further pushes. Some members of the Thunderbird team (Daniel, Geoff, Magnus, Stephen, Corey) have authorisation to do this, or you can request the help of a MoCo sheriff in #sheriffs.

## Existing failures

Our code is not perfect. You might have noticed. :-)

Some things fail occasionally (we call these intermittent failures) or most of the time (permanent failures). If a test fails every now and then, it's not generally a problem – usually it's a sign that a test hasn't accounted for something, like some asynchronous code happening in a different order than expected.

### Marking intermittent failures

We file bugs about intermittent failures in the hope that one day we'll have time to investigate and fix them. Bugs with the `intermittent-failure` keyword are displayed in the Failure Summary section if they are a close match with the error message in the log:

![](/files/-M18P8Omdw0n_pqoK78q)

If you're sure a bug description is the right one, click the pin icon next to it. This adds the current task to the pinboard and the bug number to the classification. If you're logged in you can click save to associate the bug with the task. The task gains a little star icon to mark that somebody's looked at it and understood it.

If a *new* intermittent failure appears, you can click the blue bug icon next to the error message to file a new bug. This does a lot of the legwork for you, all you have to do is choose the right Bugzilla component so that it gets some attention.

{% hint style="info" %}
Usually new intermittent failures get ignored for a while and they go away, or are so infrequent it's not worth the hassle. Don't feel the need to file a bug about every one you see.

Some things aren't usually starred, like out-of-memory failures on debug builds or failures from Firefox tests (ours all start with `comm/`). There's little benefit to doing so.
{% endhint %}

The pinboard can be used for other things. Collect tasks there with the pin icon or ctrl+clicking on them, and you can mark tasks as expected failures or to say they've been fixed by something that has landed since.

## New failures

If the build fails, we've got a serious problem that should be dealt with before anything else lands. Landing more patches on top of a broken tree just makes things worse and harder to debug. Unfortunately, Firefox developers don't stop if we have a problem.

If a test starts failing on multiple platforms, something is broken and should be dealt with. Some failures can hide other failures, and it's not nice to fix one only to find another has appeared in the meantime.

{% hint style="info" %}
History lesson: a long time ago, when bug numbers had five digits, TreeHerder's predecessor was Tinderbox, a huge grid showing the status of every build and test machine. Statuses were displayed in various colours: green, yellow, red, and black with flames (an animated GIF!). If you see the phrase "\[xyz] is burning", that's where it comes from.
{% endhint %}

### Debugging

This is really not an easy thing to describe. Much of it comes down to intuition and experience.

#### What happened?

Check the Failure Summary of TreeHerder for the most basic details. Check the task log for more information. Mochitests usually produce a screenshot of the first failure in a task. This is linked from the Job Details section named mozilla-test-fail-screenshot\_XxYyZz.png.

#### What changed in Firefox?

The most likely cause of an unexpected failure is a change to Firefox. Sometimes we are warned in advance of things we need to do, sometimes not. To figure out what has changed in Firefox main, get the revision from the last build of mozilla-central before the problem and the first build with the problem, by clicking on the decision task (D or Nd) and looking at the Artifacts tab:

![](/files/aZeezSLxdgUqXDVgeJyJ)

You can then view the mozilla-central pushlog by copying the revisions into the URL:\
<https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=**\\[good> revision]**\&tochange=**\[bad revision]\*\*

Or, install [this simple add-on](https://github.com/darktrojan/treeherder/releases), which will add buttons to TreeHerder to do the above steps for you.

From there it's a matter of finding the most likely candidates and figuring out if they are the cause of the problem. In many cases there's a change that needs to be copied into comm-central.

### Remedies

#### Retrigger a task

Is that failed task really a failure? In some cases simply starting it again can help, such as if there's network problems. Click the retrigger button (looks just like the Firefox reload button) to restart a failed task.

#### Fixing a test

Great, you've figured out what's wrong and how to fix it. Get that fix reviewed and landed! If the failure is sufficiently serious, you can land the fix and then get it reviewed later. If you do this, use `rs=bustage-fix` in your commit message to say why you landed it without review.

#### Disabling a test

To disable a test, add the appropriate `skip-if` notation to the test manifest. `skip-if = true` disables the test on all platforms. Other options are available, check the tree for examples. **If you disable a test there must be a bug filed about it.** Use the magic words `[thunderbird-disabled-test]` in the bug whiteboard and make sure appropriate developers are notified so the test can be fixed and re-enabled.

#### Backout

If it looks like a Thunderbird developer is responsible for causing a problem, contact them or their reviewer. If neither can be found and there's a serious failure, consider backing out their changes. Check whether you're right first – finding out your work has been backed out overnight is not the nicest way to start a day.

When performing a backout, use the `git revert` command.

For backing out a single revision, use `git revert <sha1>`. This will open an editor with a commit message started. Update the commit message as follows:

```
Backed out changeset def0af88e262 (bug 1359017) for mochitest failures. r=backout
```

To condense the backout of multiple changes to a single commit, you can use `git revert <oldest-sha>^..<newest-sha>` for contiguous changes, or `git revert <sha1> <sha2> <sha3>` for non-contiguous changes.

After pushing the backout, update the bug in Bugzilla:

* Mention the reason for the backout.
* Link to the backout commit starting with <https://github.com/thunderbird/thunderbird-desktop>
* Link to the push in Treeherder
* Set the NEEDINFO flag in Bugzilla to make sure the patch author sees it.


# Testing


# Running Tests

How to run Thunderbird's automated tests.

## XPCShell

XPCShell tests test Thunderbird's components, without opening the user interface. Firefox also runs this type of test, and much of the [information about Firefox's XPCShell tests](https://firefox-source-docs.mozilla.org/testing/xpcshell/index.html) also apply to Thunderbird.

To run an XPCShell test, or a directory of them, use mach:

```shell
mach xpcshell-test comm/mail/components/extensions/test/xpcshell
```

`mach` must point to mach. The path argument is always relative to the mozilla-central root, so include `comm/` at the start.

If more than one test runs, just a summary of the results will be displayed. You can pass `--verbose` to get the full output if necessary.

## Mochitest

Like XPCShell tests, [mochitests are a type of test used on Firefox](https://firefox-source-docs.mozilla.org/testing/mochitest-plain/index.html). The main difference is that mochitest runs with the full UI, in the context of the main mail window.

To run a mochitest, or a directory of them, use mach:

```shell
mach mochitest mail/components/extensions/test/browser
```

`mach` must point to mach. Any test path that matches the path given runs, for example instead of a path you could just put `browser_foo.js` and every test with that name in any directory would run.

By default, these tests will launch Thunderbird windows, making your computer largely unusable while they're running. If you are running a lot of tests, you can pass `--headless` to prevent this behavior.

## Marionette

Marionette is another Firefox testing tool, specifically intended for remote control of the UI or internal JS. See [Firefox's Marionette documentation](https://firefox-source-docs.mozilla.org/testing/marionette/Intro.html) for full details. Its mostly useful for any tests where the behavior needs to be controlled externally.

To run Marionette tests, use mach:

```shell
mach marionette-test mail/test/marionette/test_commandline.py
```

`mach` must point to mach. The path argument is relative to the current working directory. Like mochitests, you can pass `--headless` to prevent actually launching windows.


# Adding Tests

How to add your own tests for Thunderbird.

Generally, tests live near the code they are testing, however some old tests are in a separate test directory.

This document doesn't cover actually writing tests, for that see this page for Mochitests:

* [Writing Mochitest Tests](https://developer.thunderbird.net/testing/writing-mochitest-tests)

And also these pages:

* [Writing xpcshell-based unit tests](https://firefox-source-docs.mozilla.org/testing/xpcshell/index.html)
* [Mochitest](http://devdoc.net/web/developer.mozilla.org/en-US/docs/Mochitest.html) (archived MDN content)

(Just note that these pages are Firefox-centric and include some ancient ideas and practices.)

## XPCShell & Mochitest

Tests should be added to a directory near the code they are located. For example, code in `mail/components/extensions` is tested by tests in `mail/components/extensions/test`. Inside the `test` directory is a subdirectory named after the type of test: `browser` for mochitests (as in Firefox terms they are "browser-chrome" mochitests), and `xpcshell` or `unit` for XPCShell tests.

A new directory needs a test manifest:

### XPCShell test manifest (xpcshell.toml)

The default section isn't even necessary here, but you probably want to add a `head.js` file if you're going to have more than one test.

{% code title="xpcshell.toml" %}

```
[default]
prefs = [
  "calendar.timezone.local=UTC",
]

["test_firstTest.js"]
```

{% endcode %}

The calendar preferences in line 3 is unnecessary outside of the calendar tests. Calendar tests always run in UTC.

### Mochitest manifest (browser.toml)

Mochitest needs some prefs set, or automated testing will fail.

{% code title="browser.toml" %}

```
[default]
prefs = [
  "calendar.timezone.local=UTC",
  "calendar.week.start=0",
  "mail.spotlight.firstRunDone=true",
  "mail.winsearch.firstRunDone=true",
  "mailnews.start_page.override_url=about:blank",
  "mailnews.start_page.url=about:blank",
]
subsuite = "thunderbird"

["browser_firstTest.js"]
```

{% endcode %}

The calendar preferences in lines 3-4 are unnecessary outside of the calendar tests. Calendar tests always run in UTC with the week starting on Sunday.

### Linking to manifests

The next thing you need to do is tell mach about your new test manifest. In the nearest `moz.build` file, add these lines as appropriate:

{% code title="moz.build" %}

```python
BROWSER_CHROME_MANIFESTS += [
    'test/browser/browser.toml',
]
XPCSHELL_TESTS_MANIFESTS += [
    'test/xpcshell/xpcshell.toml',
]
```

{% endcode %}


# Writing Mochitest Tests

Tips on writing Mochitest tests for Thunderbird.

This document offers some basic tips for writing Mochitest tests. (See also this [Mochitest](https://firefox-source-docs.mozilla.org/testing/mochitest-plain/index.html) page in the Firefox docs, which is Firefox-centric but may still be useful.)

## Adding a New Test

You may be writing a new test in an existing test file, or you may have set up a new test file as described in [Adding Tests](https://developer.thunderbird.net/testing/adding-tests). Either way, add a new test with the `add_task` function:

```javascript
add_task(async () => {
  // The code for the test goes here.
});
```

## Helper Functions

Many essential functions live in these files.

* EventUtils([git link](https://github.com/mozilla-firefox/firefox/blob/main/testing/mochitest/tests/SimpleTest/EventUtils.js), [searchfox link](https://searchfox.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/EventUtils.js))
* BrowserTestUtils([git link](https://github.com/mozilla-firefox/firefox/blob/main/testing/mochitest/BrowserTestUtils/BrowserTestUtils.sys.mjs), [searchfox link](https://searchfox.org/mozilla-central/source/testing/mochitest/BrowserTestUtils/BrowserTestUtils.sys.mjs))
* MailTestUtils([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mailnews/test/resources/MailTestUtils.sys.mjs), [searchfox link](https://searchfox.org/comm-central/source/mailnews/test/resources/MailTestUtils.sys.mjs))

`EventUtils` and `BrowserTestUtils` do not need to be imported as they are already available in Mochitest files. `mailTestUtils` requires importing:

```javascript
const { mailTestUtils } = ChromeUtils.import(
  "resource://testing-common/mailnews/MailTestUtils.jsm"
);
```

This document is a basic introduction. To go further, explore these files (particularly the docstrings of the various functions in them) and look at existing tests.

## Assertion Functions

Use the `is` and `ok` functions to make test assertions. `is` compares two values (using JavaScript's `===` equality comparison). `ok` asserts that a single value is truthy (in JavaScript's sense of truthy).

The last argument to each is a message printed to the console to identify the assertion. It is optional but is a good practice for more understandable test logs.

```javascript
let sum = 3 * 4;
is(sum, 12, "multiplication appears to still be working");

let truthy = true;
ok(truthy, "thing is not falsy");
```

## Mouse Clicks

Do a single click on a DOM element:

```javascript
let element = document.getElementById("some-element-id");

EventUtils.synthesizeMouseAtCenter(element, { clickCount: 1 });
```

To double-click change the `clickCount` from 1 to 2. There is a shorthand for a single click:

```javascript
EventUtils.synthesizeMouseAtCenter(element, {});
```

If the test is interacting with a window that is not the main one, pass the relevant window as the (optional) third argument:

```javascript
EventUtils.synthesizeMouseAtCenter(element, {}, anotherWindow);
```

## Keyboard Keys

Type some text with the keyboard, or type a single key, even a non-character one like the tab key:

```javascript
EventUtils.sendString("some text");

EventUtils.sendKey("TAB");
```

Some other valid keys for `sendKey` include: RETURN, BACK\_SPACE, DELETE, HOME, END, UP, DOWN, LEFT, RIGHT, PAGE\_UP, PAGE\_DOWN, SHIFT, CONTROL, ALT, ESCAPE, F1, F2, etc. (Not an exhaustive list.)

If the test is interacting with a window that is not the main one, pass the relevant window as the (optional) second argument:

```javascript
EventUtils.sendString("some text", anotherWindow);

EventUtils.sendKey("TAB", anotherWindow);
```

## Modifier Keys (Ctrl, Alt, Shift)

To press a key along with one or more modifier keys:

```javascript
// Ctrl+A:
EventUtils.synthesizeKey("a", { accelKey: true });

// Ctrl+Alt+B (no shift key):
EventUtils.synthesizeKey("b", {
  accelKey: true,
  altKey: true,
  shiftKey: false
});
```

Some other options are `altKey`, `shiftKey`, and `ctrlKey` (a non-exhaustive list).

Similar to `sendString` and `sendKey`, there is an optional third `window` argument to use when interacting with a specific window.

## Waiting for Events

Tests move faster than users. Sometimes too fast. The test may need to wait for an event to occur before doing the next thing.

```javascript
let element = document.getElementById("some-element-id");

let event = await BrowserTestUtils.waitForEvent(element, "focus");

is(event.type, "focus", "element is focused");
```

## Letting Thunderbird Respond Before Proceeding

Sometimes the test needs to let the application respond to something the test did before moving on to the next step, and there is not an event to listen for. Here is a simple way to do this:

```javascript
// Do something here involving the UI.

// Let the application finish responding to what the test just did.
await new Promise(resolve => setTimeout(resolve));

// Do the next thing.
```

## Interacting with Regular Windows

Some tests will need to interact with windows that are not the main window. For example, below is a function that opens the address book window. It returns the `window` (`nsIDOMWindow`) object for the address book window, which can then be used when calling functions like `EventUtils.sendKey`.

The key point is the use of `BrowserTestUtils.domWindowOpened`, but this example also demonstrates some of the other tips found in this document. (See below for dialog windows which are handled differently.)

```javascript
async function openAddressBookWindow() {
  // Set up a watcher for "domwindowopened". When DOM windows are opened the
  // function supplied as a second argument is called. When it returns true the
  // promise is resolved as the nsIDOMWindow object for the window.
  let addressBookWindowPromise = BrowserTestUtils.domWindowOpened(
    null,
    async win => {
      // win is the nsIDOMWindow object for a window that is opening.

      // Wait until the "load" event has happened for the window.
      await BrowserTestUtils.waitForEvent(win, "load");

      // Return true when we have the right window.
      return (
        win.document.documentURI ==
        "chrome://messenger/content/addressbook/addressbook.xul"
      );
    }
  );

  // Open the address book window.
  const addressBookButton = document.getElementById("button-address");
  EventUtils.synthesizeMouseAtCenter(addressBookButton, { clickCount: 1 });

  // Wait for the promise to resolve. abWindow is a nsIDOMWindow object.
  let abWindow = await addressBookWindowPromise;

  // There is no event that fires when the JavaScript that is initially loaded
  // in the window has finished running, so let it finish by using a setTimeout.
  await new Promise(resolve => abWindow.setTimeout(resolve));

  // Assert that the window was opened successfully.
  ok(abWindow && abWindow instanceof Window, "address book window was opened");

  return abWindow;
}
```

## Interacting with Dialog Windows

Interact with dialog windows by using `BrowserTestUtils.promiseAlertDialog`.

```javascript
// The third argument is a function that interacts with the dialog window.
let dialogWindowPromise = BrowserTestUtils.promiseAlertDialog(
  null,
  "chrome://path/to/the/dialog.xul",
  async dialogWindow => {
    // dialogWindow is an nsIDOMWindow object.
    let doc = dialogWindow.document;
    let dialogElement = doc.querySelector("dialog");

    // More code to interact with the dialog goes here.

    // Usually click a button to close the dialog.
    dialogElement.getButton("accept").click();
  }
);

// Code to open the dialog window goes here.

// Wait for the dialog to close.
await dialogWindowPromise;
```

## Interacting with Trees

Trees are not like other DOM elements and require special handling.

```javascript
// Get the text from a given cell in a tree:

let tree = document.getElementById("some-tree");
let rowNumber = 1;
let columnNumber = 1;
let cellText = tree.view.getCellText(rowNumber, tree.columns[columnNumber]);

// Click on a given cell in a tree:

const { mailTestUtils } = ChromeUtils.import(
  "resource://testing-common/mailnews/MailTestUtils.jsm"
);

mailTestUtils.treeClick(EventUtils, window, tree, rowNumber, columnNumber, {
  clickCount: 1,
});
```


# Roadmaps

Planned work for the 2026 releases of Thunderbird products.

We have beautiful public roadmaps available at roadmaps.thunderbird.net:

<a href="https://roadmaps.thunderbird.net/" class="button primary">2026 Thunderbird Roadmaps Dashboard</a>

Alternatively, here are the links to each product roadmap:

<a href="https://roadmaps.thunderbird.net/en-US/desktop/" class="button primary">2026 Desktop Roadmap</a>

<a href="https://roadmaps.thunderbird.net/en-US/android/" class="button primary">2026 Android Roadmap</a>

<a href="https://roadmaps.thunderbird.net/en-US/ios/" class="button primary">2026 iOS Roadmap</a>

<a href="https://roadmaps.thunderbird.net/en-US/services/" class="button primary">2026 Services Roadmap</a>


# Supported Standards

This list collects standards that the Thunderbird family of applications currently at least partially implements or supports (in our code base, ignoring things like TCP which we inherit from upstream

**Legend**

* ✔ = This line item is supported for the given platform
* ✖ = This line item is not (yet) supported for the given platform
* *empty* = We have not collected the standards information for the given platform yet
* N/A = The standard does not apply to the given platform

### Mail

| Spec Name |                         Spec URLs                        | Desktop Status | Android Status | Standards Involvement | Comment |
| --------- | :------------------------------------------------------: | :------------: | :------------: | --------------------- | ------- |
| POP3      | [rfc1939](https://datatracker.ietf.org/doc/html/rfc1939) |        ✔       |        ✔       |                       |         |
| IMAP 4r1  | [rfc3501](https://datatracker.ietf.org/doc/html/rfc3501) |        ✔       |        ✔       |                       |         |
| IMAP IDLE | [rfc2177](https://datatracker.ietf.org/doc/html/rfc2177) |        ✔       |        ✔       |                       |         |
| SMTP      |  [rfc821](https://datatracker.ietf.org/doc/html/rfc821)  |        ✔       |        ✔       |                       |         |
| NNTP      |  [rfc977](https://datatracker.ietf.org/doc/html/rfc977)  |        ✔       |        ✔       |                       |         |

### RSS Feeder

| Spec Name |                                    Spec URLs                                   | Desktop Status | Android Status | Standards Involvement | Comment |
| --------- | :----------------------------------------------------------------------------: | :------------: | :------------: | --------------------- | ------- |
| RSS 0.9   |                 [rss-0-9-0](https://www.rssboard.org/rss-0-9-0)                |        ✔       |       N/A      |                       |         |
| RSS 2.0   |             [rss-spec](https://www.rssboard.org/rss-specification)             |        ✔       |       N/A      |                       |         |
| Atom 0.3  | [atom-0-3](https://datatracker.ietf.org/doc/html/draft-ietf-atompub-format-00) |        ✔       |       N/A      |                       |         |
| Atom 1.0  |            [atom-1-0](https://datatracker.ietf.org/doc/html/rfc4287)           |        ✔       |       N/A      |                       |         |

### Mailing Lists

| Spec Name               |                                                      Spec URLs                                                     | Desktop Status | Android Status | Standards Involvement | Comment |
| ----------------------- | :----------------------------------------------------------------------------------------------------------------: | :------------: | :------------: | --------------------- | ------- |
| List Management Headers | [rfc2369](https://datatracker.ietf.org/doc/html/rfc2369), [rfc5064](https://datatracker.ietf.org/doc/html/rfc5064) |        ✔       |        ✔       |                       |         |

### Security and Authentication

| Spec Name       |                                                                                   Spec URLs                                                                                  | Desktop Status | Android Status | Standards Involvement        | Comment                                                     |
| --------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------: | :------------: | ---------------------------- | ----------------------------------------------------------- |
| MIME            | [rfc2045](https://datatracker.ietf.org/doc/html/rfc2045), [rfc2046](https://datatracker.ietf.org/doc/html/rfc2046), [rfc2047](https://datatracker.ietf.org/doc/html/rfc2047) |        ✔       |        ✔       |                              | All 3 are supported for both Desktop and Android platforms. |
| OpenPGP         |                                                           [rfc4880](https://datatracker.ietf.org/doc/html/rfc4880)                                                           |        ✔       |        ✔       | Kai Engert actively involved |                                                             |
| S/MIME version? |                                                                                                                                                                              |        ✔       |        ✖       |                              |                                                             |
| oAuth 2.0       |                                                           [rfc6749](https://datatracker.ietf.org/doc/html/rfc6749)                                                           |        ✔       |        ✔       |                              | See also <https://oauth.net/2/>                             |

### Address Book

| Spec Name                             |                                                                                                    Spec URLs                                                                                                    | Desktop Status | Android Status | Standards Involvement | Comment                                             |
| ------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------: | :------------: | --------------------- | --------------------------------------------------- |
| vCard                                 | <p><a href="https://datatracker.ietf.org/doc/html/rfc6350">rfc6350</a><br><a href="https://datatracker.ietf.org/doc/html/rfc2425">rfc2425</a><br><a href="http://www.imc.com/pdi/vcard-21.txt">vcard-21</a></p> |        ✔       |        ✖       |                       | Version 4 used by default                           |
| CardDAV                               |                                                                             [rfc6352](https://datatracker.ietf.org/doc/html/rfc6352)                                                                            |        ✔       |       N/A      |                       | See also <https://devguide.calconnect.org/CardDAV/> |
| Collection Synchronization for WebDAV |                                                                             [rfc6578](https://datatracker.ietf.org/doc/html/rfc6578)                                                                            |        ✔       |       N/A      |                       |                                                     |
| LDAP                                  |                                                                             [rfc4511](https://datatracker.ietf.org/doc/html/rfc4511)                                                                            |        ✔       |       N/A      |                       | See also <https://ldap.com/>                        |
| LDIF                                  |                                                                             [rfc2849](https://datatracker.ietf.org/doc/html/rfc2849)                                                                            |        ✔       |       N/A      |                       | Supported for import and export                     |

### Calendar

| Spec Name                             |                         Spec URLs                        | Desktop Status | Android Status | Standards Involvement | Comment                                            |
| ------------------------------------- | :------------------------------------------------------: | :------------: | :------------: | --------------------- | -------------------------------------------------- |
| iCalendar                             | [rfc5545](https://datatracker.ietf.org/doc/html/rfc5545) |        ✔       |        ✖       |                       |                                                    |
| CalDAV                                | [rfc4791](https://datatracker.ietf.org/doc/html/rfc4791) |        ✔       |       N/A      |                       | See also <https://devguide.calconnect.org/CalDAV/> |
| Collection Synchronization for WebDAV | [rfc6578](https://datatracker.ietf.org/doc/html/rfc6578) |        ✔       |       N/A      |                       |                                                    |
| CalDAV Scheduling                     | [rfc6638](https://datatracker.ietf.org/doc/html/rfc6638) |                |       N/A      |                       |                                                    |
| iTIP                                  | [rfc2446](https://datatracker.ietf.org/doc/html/rfc2446) |        ✔       |        ✖       |                       |                                                    |

### Instant Messaging

| Spec Name                    |                                                                                                      Spec URLs                                                                                                     | Desktop Status | Android Status | Standards Involvement                                                                      | Comment                                                                                                                                                                                               |
| ---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------: | :------------: | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IRC                          |                                                 [rfc1459](https://datatracker.ietf.org/doc/html/rfc1459), [rfc2812](https://datatracker.ietf.org/doc/html/rfc2812)                                                 |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC TLS Port                 |                                                                              [rfc7194](https://datatracker.ietf.org/doc/html/rfc7194)                                                                              |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC CTCP                     |                                                                                  [irc-ctcp](http://www.alien.net.au/irc/ctcp.txt)                                                                                  |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC DCC                      |                                                                             [irc-dcc](http://www.irchelp.org/irchelp/rfc/dccspec.html)                                                                             |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC SUPPORT                  |   [irc-00](https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00), [irc-03](https://datatracker.ietf.org/doc/html/draft-brocklesby-irc-isupport-03), [irc-05](http://www.irc.org/tech_docs/005.html)   |        ✔       |       N/A      |                                                                                            | **TODO**: What about [ISUPPORT 01](https://datatracker.ietf.org/doc/html/draft-brocklesby-irc-isupport-01) and [ISUPPORT 02](https://datatracker.ietf.org/doc/html/draft-brocklesby-irc-isupport-02)? |
| IRC NAMESX                   |                                                                              [irc-namesx](https://docs.inspircd.org/2/modules/namesx/)                                                                             |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC MONITOR                  |                                                                   [irc-monitor](https://github.com/atheme/charybdis/blob/master/doc/monitor.txt)                                                                   |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRC WATCH                    |                                     [irc-watch](http://www.stack.nl/~jilles/cgi-bin/hgwebdir.cgi/irc-documentation-jilles/raw-file/tip/reference/draft-meglio-irc-watch-00.txt)                                    |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRCv3 Capability Negotiation |                                                                     [ircv3-cap-neg](https://ircv3.net/specs/extensions/capability-negotiation)                                                                     |        ✔       |       N/A      |                                                                                            | See also the [IRCv3 Support Table](https://ircv3.net/software/clients#desktop-clients)                                                                                                                |
| IRCv3 SASL 3.2               |                                                                              [ircv3-sasl](https://ircv3.net/specs/extensions/sasl-3.2)                                                                             |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRCv3 Message Tags           |                                                                        [ircv3-message-tags](https://ircv3.net/specs/extensions/message-tags)                                                                       |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRCv3 Echo Message           |                                                                      [ircv3-echo-message](https://ircv3.net/specs/extensions/echo-message-3.2)                                                                     |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRCv3 Multi-Prefix           |                                                                      [ircv3-multi-prefix](https://ircv3.net/specs/extensions/multi-prefix-3.1)                                                                     |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| IRCv3 Server Time            |                                                                       [ircv3-server-time](https://ircv3.net/specs/extensions/server-time-3.2)                                                                      |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XMPP                         | [rfc3920](https://datatracker.ietf.org/doc/rfc3920), [rfc3921](https://datatracker.ietf.org/doc/rfc3921), [rfc6120](https://datatracker.ietf.org/doc/rfc6120), [rfc6121](https://datatracker.ietf.org/doc/rfc6121) |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0030                     |                                                                                [xep-0030](https://xmpp.org/extensions/xep-0030.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0045                     |                                                                                [xep-0045](https://xmpp.org/extensions/xep-0045.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0054                     |                                                                                [xep-0054](https://xmpp.org/extensions/xep-0054.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0059                     |                                                                                [xep-0059](https://xmpp.org/extensions/xep-0059.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0078                     |                                                                                [xep-0078](https://xmpp.org/extensions/xep-0078.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0085                     |                                                                                [xep-0085](https://xmpp.org/extensions/xep-0085.html)                                                                               |        ✔       |       N/A      |                                                                                            | Typing notifications                                                                                                                                                                                  |
| XEP-0092                     |                                                                                [xep-0092](https://xmpp.org/extensions/xep-0092.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0199                     |                                                                                [xep-0199](https://xmpp.org/extensions/xep-0199.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0203                     |                                                                                [xep-0203](https://xmpp.org/extensions/xep-0203.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0245                     |                                                                                [xep-0245](https://xmpp.org/extensions/xep-0245.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0249                     |                                                                                [xep-0249](https://xmpp.org/extensions/xep-0249.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-0280                     |                                                                                [xep-0280](https://xmpp.org/extensions/xep-0280.html)                                                                               |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| XEP-xxxx MUC Avatars         |                                                                          [muc-avatars](https://xmpp.org/extensions/inbox/muc-avatars.html)                                                                         |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| Matrix                       |                                                                             [matrix](https://spec.matrix.org/latest/client-server-api/)                                                                            |        ✔       |       N/A      | Thunderbird is an [Ecosystem Member](https://matrix.org/support/) of the Matrix Foundation | UI only partially supports the full body of the specificaiton, not tracking on an MSC basis at this time                                                                                              |
| Olm/Megolm                   |                             [olm](https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md), [megolm](https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/megolm.md)                             |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |
| OTR                          |                                           [otr-3-1-0](https://otr.cypherpunks.ca/Protocol-v2-3.1.0.html), [otr-4-1-1](https://otr.cypherpunks.ca/Protocol-v3-4.1.1.html)                                           |        ✔       |       N/A      |                                                                                            |                                                                                                                                                                                                       |


# Introduction

What you need to know about making add-ons for Thunderbird.

Current Thunderbird add-ons are based on the WebExtension technology, which is also used by many web browsers. Browser vendors usually refer to their add-ons as *WebExtensions.* Thunderbird however has a lot of features not available in Browsers, and add-ons written for Thunderbird will most likely not work in Browsers. They are therefore sometimes referred to as *MailExtensions* instead.

WebExtensions are a collection of files that change the way Thunderbird looks and behaves. They can add user interface elements, alter content, or perform background tasks. Thunderbird add-ons are created using standard JavaScript, CSS and HTML.

<figure><img src="/files/-MJ9EkC-5PKv1pJUalzj" alt="Structure of a MailExtension (a Thunderbird add-on based on WebExtension technology) and how it interacts with Thunderbird."><figcaption><p>Structure of a WebExtension and how it interacts with Thunderbird.</p></figcaption></figure>

Key features of WebExtensions:

* They use stable APIs and do not need to be updated when a new version of Thunderbird is released.
* They use a permission mechanism that requires users to confirm any permission requested by add-ons before they can be installed. These permission requests enable users to know what an add-on is actually doing.
* Advanced add-on developers (with in-depth knowledge of Thunderbird source code) can extend the current available set of APIs by creating their own APIs and ship them with the add-ons. These APIs are called [Experiments](/add-ons/mailextensions#experiment-apis), and they interact directly with Thunderbird's internal APIs, allowing add-ons to use additional features not yet available via built-in WebExtension APIs.

An add-on can either be an **extension** (adding functionality or changing the way Thunderbird works) or a **theme** (changing the way Thunderbird looks).

## Getting Started

There's a lot of information out there when it comes to add-on development, and finding the most relevant one can be time-consuming. We have therefore put together a tutorial that explains step by step how to create your first extension for Thunderbird:

{% content-ref url="/pages/-LacNER74p3iflqjbg74" %}
[A "Hello World" Extension Tutorial](/add-ons/hello-world-add-on)
{% endcontent-ref %}

## Add-On Guides

For more detailed information on the two add-on types supported by Thunderbird, see their respective guides:

{% content-ref url="/pages/-MJ6xtDSW60GvgKtAgsG" %}
[A Guide to Extensions](/add-ons/mailextensions)
{% endcontent-ref %}

{% content-ref url="/pages/-Lk5PmfF8C876zhlVjRW" %}
[A Guide to Themes](/add-ons/web-extension-themes)
{% endcontent-ref %}


# What's new?

The latest developments for Add-On developers.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Modernized update guides and Experiment examples</strong></td><td>With the experience of the last few years, we have <a href="/pages/yZA85K2LV0iDZP4ZGQxY">revised our guides for converting legacy extensions</a> to WebExtensions. The information is now more compact and the examples have also been made clearer.</td><td></td><td></td></tr><tr><td><strong>Introducing the menu typed action button</strong></td><td>Since Thunderbird 115, all our action buttons can be defined as menus:</td><td><p><img src="/files/WH5RmU5StcPgb6Oo7l2S" alt="" data-size="original"></p><p><br>See our <a href="https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton">example</a> for more details.</p></td><td></td></tr><tr><td><strong>Support for Manifest Version 3</strong></td><td>Thunderbird 128 ESR is the first Thunderbird release to officially support <a href="/pages/j3VtznS7ocPpy3Wd92OO">Manifest V3</a>. A guide to convert extensions from Manifest V2 to Manifest V3 can be found in our <a href="https://webextension-api.thunderbird.net/en/mv3/guides/manifestV3.html">WebExtension API documentation</a>.</td><td><br></td><td></td></tr><tr><td><strong>Updated sample extensions to Manifest Version 3</strong></td><td>We updated our <a href="https://github.com/thunderbird/webext-examples">sample extensions</a> to be compatible with MV3. Each extension includes comments to highlight the required changes.</td><td></td><td></td></tr></tbody></table>


# Manifest Version 3

> Manifest V3 is the next iteration of WebExtensions, and offers the opportunity to introduce improvements that would otherwise not be possible due to concerns with backward compatibility. MV2 had architectural constraints that made some issues difficult to address; with MV3 we are able to make changes to address this.
>
> source: [Mozilla Add-on Community Blog](https://blog.mozilla.org/addons/2022/05/18/manifest-v3-in-firefox-recap-next-steps/)

One of the key concepts of Manifest V3 is to deprecate persistent background pages. The original proposal from Google meant to replace background pages completely by service workers, which however resulted in criticism from the community. Most add-ons would have to be rewritten to use service workers, and some add-ons could not have been converted at all.

Mozilla introduced [Limited Event Pages](https://github.com/w3c/webextensions/issues/134#issue-1049674790) to allow add-on developers to keep using the known concept of background pages. These event pages are terminated when they become idle, and restarted whenever an event for which they are registered occurs.

In addition to these fundamental operational changes, some APIs have been changed to remove inconsistencies. Also, all methods and properties marked as deprecated have been removed.

A guide to convert extensions from Manifest V2 to Manifest V3 can be found in our [WebExtension API documentation](https://webextension-api.thunderbird.net/en/mv3/guides/manifestV3.html)


# A "Hello World" Extension Tutorial

All the information you need to get your first Thunderbird extension up and running.

In the first part of this tutorial, we will create a very simple extension, which adds a "*Hello World*" button to Thunderbird's main toolbar and a click on it will show a `Hello, World!` popup.

![](/files/YecOxBogv6T741x9kCtR)

## Creating a manifest.json

First, we create an empty `hello-world` project folder for our extension and navigate to it.

Extensions require a `manifest.json` file that tells Thunderbird a few basic information about the add-on. Let's place the following `manifest.json` file directly in the `hello-world` project folder.

{% code title="manifest.json" %}

```json
{
    "manifest_version": 2,
    "name": "Hello World Example",
    "description": "A basic Hello World example extension!",
    "version": "1.0",
    "author": "Thunderbird Team",
    "browser_specific_settings": {
        "gecko": {
            "id": "helloworld@yoursite.com",
            "strict_min_version": "128.0"
        }
    },
    "browser_action": {
        "default_popup": "mainPopup/popup.html",
        "default_title": "Hello World",
        "default_icon": "images/internet-32px.png"
    },
    "icons": {
        "64": "images/internet.png",
        "32": "images/internet-32px.png",
        "16": "images/internet-16px.png"
    }
}
```

{% endcode %}

You can grab the icons we use for this example from the [example repository](https://github.com/thunderbird/webext-examples/tree/master/hello-world/images). Make sure to create an `images` directory in the `hello-world` project folder for them.

## Using a `browser_action`

The above manifest includes the definition for a `browser_action`. That is the button we want to add to Thunderbird's main toolbar. The reference to a *browser* in its name is inherited from the Firefox Browser.

The allowed keys for the `browser_action` button are described in our [API documentation](https://webextension-api.thunderbird.net/en/mv2/browserAction.html). We define a popup HTML page, which should be loaded if the button is clicked, a title and an icon.

### popup.html

The location of the HTML file loaded by the popup of our `browser_action` is defined in the `browser_action.default_popup` key. Let's create a `mainPopup` directory in the `hello-world` project folder for everything related to that popup and start with the following `popup.html` .

{% code title="popup.html" %}

```html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Hello World</title>
    <link rel="stylesheet" type="text/css" media="screen" href="popup.css">
</head>

<body>
    <div class="popup-page">
        Hello, World!
    </div>
    <script type="module" src="popup.js"></script>
</body>

</html>
```

{% endcode %}

{% hint style="info" %}
The default [**content security policy**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy#Inline_JavaScript) disallows JavaScript placed directly in `<script>` tags and inline event handlers like `onclick`. Place all JavaScript code into a separate file (like popup.js in this example) and use [addEventListener()](https://developer.mozilla.org/de/docs/Web/API/EventTarget/addEventListener) instead of inline event handlers.
{% endhint %}

{% hint style="warning" %}
The `script` tag to include `popup.js` is setting `type="module"`, which loads the script as a top level ES6 module. This enables the script to use the await keyword in file scope code and to load other ES6 modules.

We will not use these features in this step of the tutorial, but we still use the modern ES6 module approach here to introduce it as a standard programming practice.
{% endhint %}

### popup.js

We're going to create the following file called `popup.js` and place it in the same folder as the `popup.html` file.

{% code title="popup.js" %}

```javascript
// Below is what we'll log to the console.

console.log('Hello, World! - from popup.js');
```

{% endcode %}

What our little script does is sending a message to the console each time we click on our "*Hello World*" toolbar button. We'll take a look at that in a moment when we try out our add-on. The first line is just a comment, so we can remember what our code is doing.

### popup.css

Now we want to create the CSS file referenced in our HTML file. We'll call it `popup.css`. This is just for decoration of the page, we'll put it in the same folder as the `popup.html` file.

{% code title="popup.css" %}

```css
.popup-page {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    font-weight: bold;
    height: 60px;
    padding: 10px;
}
```

{% endcode %}

## Testing the Extension

First, let's double-check that we created [the correct files](https://github.com/thunderbird/webext-examples/commit/230eba3fd9f5c633cb30c0a83d5500e532c283c4?diff=unified?diff=unified) and have them in the right places:

```
hello-world/
  ├── manifest.json
  ├── images/
      ├── internet.png
      ├── internet-16px.png
      └── internet-32px.png
  └── mainPopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
```

### Installing

To install the add-on we created, we are going to load it temporarily. Let's start by opening the Add-ons Manager:

![](/files/lqK70bLCrSeRqGxl5cUG)

This will open up the Add-ons Manager, make sure "Extensions" is selected on the left-hand side and click the gear to select "Debug Add-ons".

![](/files/H3VEDq12ji7zz5HDGKy2)

Click on the "Load Temporary Add-on..." button:

![](/files/Dhro0VgeqgdKNUBIgS1P)

Select the `manifest.json` file from within our `hello-world` project folder:

![](/files/aZrxpFLnYu08kvob1IVz)

This should install the add-on for this session only:

![](/files/JvIl6a87T6FOSAcgBHrv)

### Opening the Error Console

Our extension will print messages to the error console using `console.log()`, so we need to open the error console first, in order to see those log entries. Hit the "Inspect" button under the add-on's listing (pictured above), this will open the Developer Tools window.

Make sure the "Console" tab is selected in the Developer Tools. Click the "Persist Logs" checkbox in the top right-hand corner of the Developer Tools window so that we can see the output from the add-on after we've interacted with it (otherwise, it only shows output as it is happening).

![](/files/XrhDdJB7Ci0Rx39IQ1al)

### Trying it Out

Now we can give our new add-on a whirl. Head to the home tab and find the new "*Hello World*" button in the main toolbar in the top right-hand corner. Click on it to see a popup with the `Hello, World!` message.

![](/files/YecOxBogv6T741x9kCtR)

Now, if you look at the Developer Tools window, you should see something like the following in the console:

![](/files/sX1AqnjuW6DljTwKopgH)

## Creating the add-on file

Once the add-on is ready for release or if you want to share it with others, you need to create a single add-on file. Simply zip the content of the add-on's project folder and use the `xpi` file extension. That file can be installed from the gear menu in the Thunderbird Add-ons Manager.


# Using WebExtension APIs

Extending the simple example extension to make use of WebExtension APIs.

In the second part of the Hello World Extension Tutorial, we will add a "*Details*" button to the message header toolbar. A click on it will show some information about the currently displayed message, which we retrieve using Thunderbird's WebExtension APIs.

![](/files/voH6CiOu7M0S0XgciVlD)

## Using a `message_display_action`

Similar to adding the `browser_action` in the first part of the Hello World Extension Tutorial, we have to extend the `manifest.json` to add the `message_display_action`.:

```json
"message_display_action": {
    "default_popup": "messagePopup/popup.html",
    "default_title": "Details",
    "default_icon": "images/internet-32px.png"
},
```

### popup.html

The HTML file for our popup needs some place-holders, which we can later fill using JavaScript and Thunderbird's WebExtension APIs. We create a `messagePopup` folder inside the `hello-world` project folder and place the following `popup.html` file in the newly created folder:

{% code title="popup.html" %}

```html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Details</title>
    <link rel="stylesheet" type="text/css" media="screen" href="popup.css">
</head>

<body>
    <div class="grid-container">
        <div class="header">Subject:</div>
        <div id="subject" class="content"></div>
        <div class="header">From:</div>
        <div id="from" class="content"></div>
        <div class="header">Received-Header:</div>
        <div id="received" class="content"></div>
    </div>
    <script type="module" src="popup.js"></script>
</body>

</html>
```

{% endcode %}

### popup.css

We place the following `popup.css` file in the same folder as the `popup.html` file.

{% code title="popup.css" %}

```css
.grid-container {
    display: grid;
    grid-template-columns: 1fr 6fr;
}

.header {
    font-weight: bold
}

.grid-container div {
    margin: 1ex;
}
```

{% endcode %}

Instead of tables, we use modern CSS styling to format our HTML into a tabular view. The `display: grid` container defines how our 6 DIV elements inside the container DIV are aligned. Check the [grid documentation](https://developer.mozilla.org/de/docs/Web/CSS/CSS_Grid_Layout) or this [grid guide](https://css-tricks.com/snippets/css/complete-guide-grid/) for more details.

### popup.js

All WebExtension API functions return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) instead of an actual value. This aims to simplify the handling of asynchronous functions.

The author of this example prefers [the `async`/`await` syntax](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) over the `.then()` approach for handling Promises. This allows us to avoid the so called callback-hell of asynchronous functions and instead keep writing sequential code by simply awaiting all the returned Promises.

{% code title="popup.js" lineNumbers="true" %}

```javascript
// The user clicked our button, get the active tab in the current window using
// the tabs API.
let tabs = await messenger.tabs.query({ active: true, currentWindow: true });

// Get the message currently displayed in the active tab, using the
// messageDisplay API. Note: This needs the messagesRead permission.
// The returned message is a MessageHeader object with the most relevant
// information.
let message = await messenger.messageDisplay.getDisplayedMessage(tabs[0].id);

// Update the HTML fields with the message subject and sender.
document.getElementById("subject").textContent = message.subject;
document.getElementById("from").textContent = message.author;

// Request the full message to access its full set of headers.
let full = await messenger.messages.getFull(message.id);
document.getElementById("received").textContent = full.headers.received[0];

```

{% endcode %}

{% hint style="warning" %}
The `popup.js` file was loaded as a top level ES6 module by specifying `type="module"` in its `script` tag. This allows us to use the `await` keyword directly in file scope code. Otherwise we would need to use an asynchronous wrapper function:\
\
`async function load() {`\
`let tabs = await messenger.tabs.query({`\
`active: true,`\
`currentWindow: true,`\
`});`\
`...`\
`}`\
`load();`
{% endhint %}

{% hint style="info" %}
In Thunderbird, all WebExtension API can be accessed through the `browser.*` namespace, as with Firefox, but also through the `messenger.*` namespace, which is a better fit for Thunderbird.
{% endhint %}

#### messenger.tabs.query()

The [tabs API](https://webextension-api.thunderbird.net/en/mv2/tabs.html) provides access to Thunderbird's tabs. We need to get hold of the current active tab to learn which message is displayed there. We use the [query](#adding-a-message_display_action) method to find it in line `3`.

{% hint style="info" %}
Using `messenger.tabs.getCurrent()` will not work, as that always returns the tab in which it is being called from. In our case, the call is executed from inside the popup of the `message_display_action` and not from inside the tab we are looking for.
{% endhint %}

#### messenger.messageDisplay.getDisplayedMessage()

The [getDisplayedMessage](https://webextension-api.thunderbird.net/en/mv2/messageDisplay.html#getdisplayedmessage-tabid) method of the [messageDisplay API](https://webextension-api.thunderbird.net/en/mv2/messageDisplay.html) provides access to the currently viewed message in a given tab. It returns a Promise for a [MessageHeader](https://webextension-api.thunderbird.net/en/mv2/messages.html#messageheader) object from the [messages API](https://webextension-api.thunderbird.net/en/mv2/messages.html) with basic information about the message in line `9`.

At this stage we are interested in the subject (line `12`) and the author (line `13`).

{% hint style="warning" %}
The [getDisplayMessage](https://webextension-api.thunderbird.net/en/mv2/messageDisplay.html#getdisplayedmessage-tabid) method requires the <mark style="color:red;">`messagesRead`</mark> permission, which needs to be added to the `permissions` key of our `manifest.json` file.
{% endhint %}

```json
"permissions": [
    "messagesRead"
],
```

#### messenger.messages.getFull()

We also want to get the `received` header from the message. That information is not part of the general `MessageHeader` object, so we have to request the full message.

The [getFull](https://webextension-api.thunderbird.net/en/mv2/messages.html#getfull-messageid-options) method in line `16` returns a Promise for a [MessagePart](https://webextension-api.thunderbird.net/en/mv2/messages.html#messagepart) object, which relates to messages containing multiple MIME parts. The `headers` member of the part returned by `getFull` includes the headers of the message (excluding headers which are part of nested MIME parts available through the `parts` member).

## Testing the Extension

Let's double-check that we made the [correct changes](https://github.com/thunderbird/webext-examples/commit/73f5b3776b0d4f9c0e9281168de8f9313cc474f8?diff=unified) and have all the files in the right places:

```
hello-world/
  ├── manifest.json
  ├── images/
      ├── internet.png
      ├── internet-16px.png
      └── internet-32px.png
  ├── mainPopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
  └── messagePopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
```

This is how our `manifest.json` should now look like:

{% code title="manifest.json" %}

```json
{
    "manifest_version": 2,
    "name": "Hello World Example",
    "description": "A basic Hello World example extension!",
    "version": "2.0",
    "author": "Thunderbird Team",
    "browser_specific_settings": {
        "gecko": {
            "id": "helloworld@yoursite.com",
            "strict_min_version": "128.0"
        }
    },
    "browser_action": {
        "default_popup": "mainPopup/popup.html",
        "default_title": "Hello World",
        "default_icon": "images/internet-32px.png"
    },
    "message_display_action": {
        "default_popup": "messagePopup/popup.html",
        "default_title": "Details",
        "default_icon": "images/internet-32px.png"
    },
    "permissions": [
        "messagesRead"
    ],
    "icons": {
        "64": "images/internet.png",
        "32": "images/internet-32px.png",
        "16": "images/internet-16px.png"
    }
}
```

{% endcode %}

### Installing

As described in the [first part of the Hello World Extension Tutorial](/add-ons/hello-world-add-on#installing), go to the Add-ons Manager to open the Debug Add-on Page and temporarily install the extension.

### Trying it Out

Open any message and you will find a "*Details*" button in the message header toolbar. A click on it will show you the `subject`, the `author` and the first found `received` header of the currently viewed message.


# Using a Background Page

Extending the example extension to use a background page.

In the third part of the Hello World Extension Tutorial, we will introduce the concept of the WebExtension background page.

We will keep track of incoming mails, add a menu entry to the tools menu and also a context menu entry to our button in Thunderbird's main toolbar and a click on both will open notifications with the collected information from the last 24 hours.

![](/files/OGJl89kWwAzUAMD7XSPq)

## Background Page and Background Scripts

In the first two parts of the Hello World Extension Tutorial, we used well-defined UI hooks to load HTML pages when the user opened one of our popups. In contrast, the background page - if defined - is automatically loaded when the add-on is enabled during Thunderbird start or after the add-on has been manually enabled or installed. It is automatically destroyed when the add-on is shutting down.

The background page is a standard HTML page, supporting the same technologies as ordinary HTML pages, but it is never shown to the user. Its main purpose is to load one or more JavaScript files into the background. Those background scripts can be used to listen for events or to initialize and properly set up the add-on. As [described in the MailExtension guide](/add-ons/mailextensions#background-page), there are two ways to load background scripts:

1. Actually defining a background HTML page, that uses `script` tags to load the JavaScript files.
2. Just defining the to-be-loaded JavaScript files and let Thunderbird create a background page on-the-fly.

The author of this example prefers the first option. It allows to use the `await` keyword in file scope code and it allows to load other [ES6 modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). We therefore add the following section to our `manifest.json` file:

```json
"background": {
    "page": "background.html"
},
```

We place the following `background.html` file into our `hello-world` project folder:

{% code title="background.html" %}

```html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <script type="module" src="background.js"></script>
</head>

</html>
```

{% endcode %}

Let's also create an empty `background.js` script file in the `hello-world` project folder.

### Listening for New Messages

In order to listen for new messages, we have to add a listener for the [onNewMessageReceived](https://webextension-api.thunderbird.net/en/mv2/messages.html#onnewmailreceived) event to our background script:

{% code title="background.js" lineNumbers="true" %}

```javascript
// Import all functions defined in the messageTools module.
import * as messageTools from '/modules/messageTools.mjs';

messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
    let { messageLog } = await messenger.storage.local.get({ messageLog: [] });

    for await (let message of messageTools.iterateMessagePages(messages)) {
        messageLog.push({
            folder: folder.name,
            time: Date.now(),
            message: message
        })
    }

    await messenger.storage.local.set({ messageLog });
})
```

{% endcode %}

{% hint style="info" %}
The above code is using an inline arrow function to define the callback function for the event listener (which is called for each `onNewMailReceived` event). This is identical to the following implicit function definition:

\
`async function onNewMailReceivedCallback(folder, messages) {`\
`...`\
`}`\
`messenger.messages.onNewMailReceived.addListener(`\
`onNewMailReceivedCallback`\
`);`
{% endhint %}

The callback function of the `onNewMailReceived` event receives two parameters: `folder` being a [MailFolder](https://webextension-api.thunderbird.net/en/mv2/folders.html#mailfolder) and `messages` being a [MessageList](https://webextension-api.thunderbird.net/en/mv2/messages.html#messagelist). The defined event listener stores the folder and the message information of the new received mail in the extensions storage.

{% hint style="warning" %}
The `onNewMessageReceived` event requires the <mark style="color:red;">`accountsRead`</mark> permission, which needs to be added to the `permissions` key in our `manifest.json` file.
{% endhint %}

#### messenger.storage.local.get()

In line `5` of the shown background script, we request the current `messageLog` entry from the WebExtensions local storage. The used syntax allows to define a default value of `[]`(an empty Array), if there is currently no `messageLog` entry stored.

We could also request multiple values from the local storage:

```javascript
let rv = await messenger.storage.local.get({
  messageLog: [],
  aBoolValue: true,
  aStringValue: "none"
});
console.log(rv);
```

The call to `storage.local.get()` returns a Promise for a single object with the requested entries, for example the above `console.log(rv)` could produce the following output:

```json
{
  messageLog: [],
  aBoolValue: false,
  aStringValue: "The sky is the limit."
}
```

To access the content of the `messageLog` member, one would have to use `rv.messageLog`. That is sometimes not the desired behaviour, and instead we store the requested value directly in a variable. This is called *object destructuring,* and it maps the content of the `messageLog` member of the returned object to the `messageLog` variable. Any other non-matching returned member is ignored.

{% hint style="warning" %}
Access to the local storage requires the <mark style="color:red;">`storage`</mark> permission, which needs to be added to the `permissions` key in our `manifest.json` file.
{% endhint %}

#### messageTools.iterateMessagePages()

The listener is using a helper function to be able to loop over the received messages. The `iterateMessagePages` function is defined in an ES6 module, which is loaded in line `2` of the background script shown above. So, we create a `modules` subfolder into our `hello-world` project folder. Then, we place the `messageTools.mjs` file within. Here follows what it should contain.

{% code title="messageTools.mjs" %}

```javascript
// A wrapper function returning an async iterator for a MessageList.
export async function* iterateMessagePages(page) {
    for (let message of page.messages) {
        yield message;
    }

    while (page.id) {
        page = await messenger.messages.continueList(page.id);
        for (let message of page.messages) {
            yield message;
        }
    }
}
```

{% endcode %}

To identify the script file as an ES6 module, which does not include file scope code, but only defines functions, we use the `*.mjs` file extension.

Since Thunderbird's WebExtension API potentially has to handle a lot of messages, the [MessageList](https://webextension-api.thunderbird.net/en/mv2/messages.html#messagelist) data type is paginated. Please check the [Working with Message Lists](https://webextension-api.thunderbird.net/en/mv2/examples/messageLists.html) tutorial for more information.

The provided `iterateMessagePages()` wrapper function is doing most of the heavy lifting and allows to asynchronously loop over the returned messages in line `7` of the shown background script. For each received message, we subsequently push a new entry into the `messageLog` Array.

#### messenger.storage.local.set()

In line `15` we store the updated `messageLog` Array back into the local storage. We use the object shorthand notation, which allows leaving out the actual value definition, if the value is stored in a variable with the same name as the object's member name. If the shorthand notation is unwanted, one could instead write the following:

```javascript
await messenger.storage.local.set({ messageLog: messageLog });
```

### Adding Menu Entries and their Actions

Let's add the following code to our background script, which will add two menu entries and will react to them being clicked:

{% code lineNumbers="true" %}

```javascript
// Create the menu entries.
let menu_id = await messenger.menus.create({
    title: "Show received email",
    contexts: [
        "browser_action",
        "tools_menu"
    ],
});

// Register a listener for the menus.onClicked event.
messenger.menus.onClicked.addListener(async (info, tab) => {
    if (info.menuItemId == menu_id) {
        // Our menu entry was clicked
        let { messageLog } = await messenger.storage.local.get({ 
           messageLog: []
        });

        let now = Date.now();
        let last24h = messageLog.filter(e => (now - e.time) < 24 * 60 * 1000);

        for (let entry of last24h) {
            messenger.notifications.create({
                "type": "basic",
                "iconUrl": "images/internet.png",
                "title": `${entry.folder}: ${entry.message.author}`,
                "message": entry.message.subject
                });                
        }
    }
});
```

{% endcode %}

#### messenger.menus.create()

In line `2` we create a new menu entry. We use the title `Show received email` and we add it to the `browser_action` context and to the `tools_menu` context. A list of other available contexts can be found on the [Supported UI Elements](/add-ons/mailextensions/supported-ui-elements#menu-items) page.

The [menus.create()](https://webextension-api.thunderbird.net/en/mv2/menus.html#create-createproperties-callback) function returns the `id` of the new menu, which we can use to identify our menu, or - for example - add submenus by using the `id` as the `parentId` for other menu entries.

{% hint style="warning" %}
Using the `menus` API requires the <mark style="color:red;">`menus`</mark> permission, which needs to be added to the `permissions` key in our `manifest.json` file.
{% endhint %}

#### messenger.menus.onClicked()

In order to do something when our menu is clicked, we add a listener for the [onClicked](https://webextension-api.thunderbird.net/en/mv2/menus.html#onclicked) event. We check the `id` of the clicked menu to see which of our menus was clicked (we only added one, but checking here anyhow).

#### messenger.notifications.create()

After we have retrieved the current `messageLog` from the local storage, we loop over all entries and [create a notification](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/notifications) for each entry in line `20`.

{% hint style="warning" %}
Using the `notifications` API requires the <mark style="color:red;">`notifications`</mark> permission, which needs to be added to the `permissions` key in our manifest.json file.
{% endhint %}

## Testing the Extension

Let's double-check that we made the [correct changes](https://github.com/thunderbird/webext-examples/commit/bb719ee48994101c52a3d8849d7c07ac6a46db01?diff=unified) and have all the files in the right places:

```
hello-world/
  ├── background.html
  ├── background.js
  ├── manifest.json
  ├── images/
      ├── internet.png
      ├── internet-16px.png
      └── internet-32px.png
  ├── mainPopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
  ├── messagePopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
  └── modules/
      └── messageTools.mjs
```

This is how our `manifest.json` should now look like:

{% code title="manifest.json" %}

```json
{
    "manifest_version": 2,
    "name": "Hello World Example",
    "description": "A basic Hello World example extension!",
    "version": "3.0",
    "author": "Thunderbird Team",
    "browser_specific_settings": {
        "gecko": {
            "id": "helloworld@yoursite.com",
            "strict_min_version": "128.0"
        }
    },
    "browser_action": {
        "default_popup": "mainPopup/popup.html",
        "default_title": "Hello World",
        "default_icon": "images/internet-32px.png"
    },
    "message_display_action": {
        "default_popup": "messagePopup/popup.html",
        "default_title": "Details",
        "default_icon": "images/internet-32px.png"
    },
    "permissions": [
        "messagesRead",
        "accountsRead",
        "storage",
        "menus",
        "notifications"
    ],
    "background": {
        "page": "background.html"
    },
    "icons": {
        "64": "images/internet.png",
        "32": "images/internet-32px.png",
        "16": "images/internet-16px.png"
    }
}
```

{% endcode %}

Our background script should look as follows:

{% code title="background.js" %}

```javascript
// Import all functions defined in the messageTools module.
import * as messageTools from '/modules/messageTools.mjs';

// Add a listener for the onNewMailReceived events.
messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
    let { messageLog } = await messenger.storage.local.get({ messageLog: [] });

    for await (let message of messageTools.iterateMessagePages(messages)) {
        messageLog.push({
            folder: folder.name,
            time: Date.now(),
            message: message
        })
    }

    await messenger.storage.local.set({ messageLog });
})

// Create the menu entries.
let menu_id = await messenger.menus.create({
    title: "Show received email",
    contexts: [
        "browser_action",
        "tools_menu"
    ],
});

// Register a listener for the menus.onClicked event.
await messenger.menus.onClicked.addListener(async (info, tab) => {
    if (info.menuItemId == menu_id) {
        // Our menu entry was clicked
        let { messageLog } = await messenger.storage.local.get({ messageLog: [] });

        let now = Date.now();
        let last24h = messageLog.filter(e => (now - e.time) < 24 * 60 * 1000);

        for (let entry of last24h) {
            messenger.notifications.create({
                "type": "basic",
                "iconUrl": "images/internet.png",
                "title": `${entry.folder}: ${entry.message.author}`,
                "message": entry.message.subject
            });
        }
    }
});
```

{% endcode %}

### Installing

As described in the [first part of the Hello World Extension Tutorial](/add-ons/hello-world-add-on#installing), go to the Add-ons Manager to open the Debug Add-on Page and temporarily install the extension.

### Trying it Out

After you have received one or more new messages, while the add-on has been active, open the context menu of our `browser_action` button in Thunderbird's main toolbar and click on "Show received emails". For each received message, you should see a notification.

![](/files/MapAXs5Ed7jsoVY7yHeM)


# Using Content Scripts

Extending the example extension to use a content script.

In the fourth part of the Hello World Extension Tutorial, we will introduce the concept of content scripts.

We will add a banner to the top of the message display area, displaying some information about the currently viewed message. The banner will also include a button to mark the currently viewed message as unread.

![](/files/eT6wTwjhwhRGeo6ZVg3B)

## Using a Message Display Script

Content Scripts are JavaScript files that are loaded and executed in content pages. This technology was mainly developed for browsers, where it is used to interact with the currently viewed web page.

In addition to [standard content scripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts), Thunderbird supports the following special types of content scripts:

* compose scripts loaded into the editor of the message composer
* message display scripts loaded into rendered messages when displayed to the user

We will be using a message display script in this example. In order to register one, we use the [messageDisplayScripts](https://webextension-api.thunderbird.net/en/mv2/messageDisplayScripts.html) API and add the following code to our background script:

```javascript
// Register the message display script for all newly opened message tabs.
messenger.messageDisplayScripts.register({
    js: [{ file: "messageDisplay/message-content-script.js" }],
    css: [{ file: "messageDisplay/message-content-styles.css" }],
});

// Inject script and CSS in all already open message tabs.
let openTabs = await messenger.tabs.query();
let messageTabs = openTabs.filter(
    tab => ["mail", "messageDisplay"].includes(tab.type)
);
for (let messageTab of messageTabs) {
    // Make sure the tab is displaying a single message. The mail tab could also
    // display a content page or multiple messages, which will cause an error.
    if (messageTab.type == "mail") {
        let messages = await browser.messageDisplay.getDisplayedMessages(
            messageTab.id
        );
        if (messages.length != 1) {
            continue;
        }
    }
    browser.tabs.executeScript(messageTab.id, {
        file: "messageDisplay/message-content-script.js"
    });
    browser.tabs.insertCSS(messageTab.id, {
        file: "messageDisplay/message-content-styles.css"
    });
}
```

{% hint style="warning" %}
The `messageDisplayScripts` API requires the <mark style="color:red;">`messagesModify`</mark> permission, which needs to be added to the `permissions` key in our `manifest.json` file.
{% endhint %}

Whenever a message is displayed to the user, the registered CSS file will be added and the registered JavaScript file will be injected and executed.

Let's create a `messageDisplay` directory inside our `hello-world` project folder with the following two files:

<pre class="language-css" data-title="message-content-styles.css"><code class="lang-css"><strong>.thunderbirdMessageDisplayActionExample {
</strong>  background-color: #d70022;
  color: white;
  font-weight: 400;
  padding: 0.25rem 0.5rem;
  margin-bottom: 0.25rem;
  display: flex;
}

.thunderbirdMessageDisplayActionExample_Text {
  flex-grow: 1;
}
</code></pre>

{% code title="message-content-script.js" lineNumbers="true" %}

```javascript
async function showBanner() {
    let bannerDetails = await browser.runtime.sendMessage({
        command: "getBannerDetails",
    });

    // Get the details back from the formerly serialized content.
    const { text } = bannerDetails;

    // Create the banner element itself.
    const banner = document.createElement("div");
    banner.className = "thunderbirdMessageDisplayActionExample";

    // Create the banner text element.
    const bannerText = document.createElement("div");
    bannerText.className = "thunderbirdMessageDisplayActionExample_Text";
    bannerText.innerText = text;

    // Create a button to display it in the banner.
    const markUnreadButton = document.createElement("button");
    markUnreadButton.innerText = "Mark unread";
    markUnreadButton.addEventListener("click", async () => {
        // Add the button event handler to send the command to the
        // background script.
        browser.runtime.sendMessage({
            command: "markUnread",
        });
    });

    // Add text and button to the banner.
    banner.appendChild(bannerText);
    banner.appendChild(markUnreadButton);

    // Insert it as the very first element in the message.
    document.body.insertBefore(banner, document.body.firstChild);
};

showBanner();
```

{% endcode %}

{% hint style="danger" %}
Content scripts cannot yet be loaded as top level ES6 modules. They cannot load other ES6 modules. They can also not use the `async` keyword in file scope code, we therefore have to create the async wrapper function `showBanner()`.
{% endhint %}

The main purpose of the `message-content-script.js` file is to manipulate the rendered message and add a banner at its top. We use basic DOM manipulation techniques.

What is special however is how the displayed information is retrieved. In the second part of this tutorial, we used the `tabs` API and the `messageDisplay` API from our background page, to learn which message is currently displayed and then used the `messages` API to get the required information. This does not work for content scripts, as [their access is limited](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#webextension_apis). Instead, we have to request this information from the background script using runtime messaging.

#### Sending a runtime message

The [sendMessage()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage) method of the [runtime](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime) API will send a message to each active page, including the background page, the options page, popup pages and other HTML pages of our extension loaded using [windows.create()](#using-a-message-display-script) or [tabs.create()](#using-a-message-display-script). The message itself can be a string, an integer, a boolean, an array or an object. It must abide to the [structured clone algorithm](#testing-the-extension).

In line `2` of `message-content-script.js`, we send the message object `{command: "getBannerDetails"}`, to request the display details from the background page. In line `24` we send the message object `{command: "markUnread"}`, to request the background page to mark the currently viewed message as unread.

#### Receiving a runtime message

The background page can listen for runtime messages, by registering the following listener:

```javascript
/**
 * Add a handler for the communication with other parts of the extension,
 * like our message display script.
 *
 * Note: It is best practice to always define a synchronous listener
 *       function for the runtime.onMessage event.
 *       If defined asynchronously, it will always return a Promise
 *       and therefore answer all messages, even if a different listener
 *       defined elsewhere is supposed to handle these.
 * 
 *       The listener should only return a Promise for messages it is
 *       actually supposed to handle.
 */
messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // Check what type of message we have received and invoke the appropriate
    // handler function.
    if (message && message.hasOwnProperty("command")) {
        return commandHandler(message, sender);
    }
    // Return false if the message was not handled by this listener.
    return false;
});

// The actual (asynchronous) handler for command messages.
async function commandHandler(message, sender) {
    // Get the message currently displayed in the sending tab, abort if
    // that failed.
    const messageHeader = await messenger.messageDisplay.getDisplayedMessage(
        sender.tab.id
    );

    if (!messageHeader) {
        return;
    }

    // Check for known commands.
    switch (message.command) {
        case "getBannerDetails":
            // Create the information we want to return to our message display
            // script.
            return { text: `Mail subject is "${messageHeader.subject}"` };
        case "markUnread":
            // Mark the message as unread.
            messenger.messages.update(messageHeader.id, {
                read: false,
            });
            break;
    }
}
```

{% hint style="info" %}
The `messages.update()` function requires the <mark style="color:red;">`messagesUpdate`</mark> permission, which needs to be added to the `permissions` key in our `manifest.json` file.
{% endhint %}

The `message` passed to the `onMessage` listener will be whatever has been sent using `sendMessage().` The `sender` is of type [MessageSender](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender) and will include the sending tab.

In this example, we check if the runtime message includes our command and based on its value either return the banner details or mark the viewed message as unread.

{% hint style="danger" %}
**Note**: The `onMessage` listener has a third parameter `sendResponse`, which is a callback function to send a synchronous response back to the sending tab. For Thunderbird however the preferred way is to return an asynchronous response using a Promise instead.

**Note**: It is best practice to always define a synchronous listener function for the `runtime.onMessage` event. If defined asynchronously, it will always return a Promise and therefore answer all messages, even if a different listener defined elsewhere is supposed to handle these.

The listener should only return a Promise for messages it is actually supposed to handle.
{% endhint %}

## Testing the Extension

Let's double-check that we made the [correct changes](https://github.com/thunderbird/webext-examples/commit/65df906647525b85fa0d2367bd20dc8c3599558d?diff=unified) and have all the files in the right places:

```
hello-world/
  ├── background.html
  ├── background.js
  ├── manifest.json
  ├── images/
      ├── internet.png
      ├── internet-16px.png
      └── internet-32px.png
  ├── mainPopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
  ├── messageDisplay/
      ├── message-content-script.js
      └── message-content-styles.css
  ├── messagePopup/
      ├── popup.css
      ├── popup.html
      └── popup.js
  └── modules/
      └── messageTools.mjs
```

This is how our `manifest.json` should now look like:

{% code title="manifest.json" %}

```json
{
    "manifest_version": 2,
    "name": "Hello World Example",
    "description": "A basic Hello World example extension!",
    "version": "4.0",
    "author": "Thunderbird Team",
    "browser_specific_settings": {
        "gecko": {
            "id": "helloworld@yoursite.com",
            "strict_min_version": "128.0"
        }
    },
    "browser_action": {
        "default_popup": "mainPopup/popup.html",
        "default_title": "Hello World",
        "default_icon": "images/internet-32px.png"
    },
    "message_display_action": {
        "default_popup": "messagePopup/popup.html",
        "default_title": "Details",
        "default_icon": "images/internet-32px.png"
    },
    "permissions": [
        "messagesRead",
        "messagesUpdate",
        "accountsRead",
        "storage",
        "menus",
        "notifications",
        "messagesModify"
    ],
    "background": {
        "page": "background.html"
    },
    "icons": {
        "64": "images/internet.png",
        "32": "images/internet-32px.png",
        "16": "images/internet-16px.png"
    }
}
```

{% endcode %}

Our background script should look as follows:

```javascript
// Import all functions defined in the messageTools module.
import * as messageTools from '/modules/messageTools.mjs';

// Add a listener for the onNewMailReceived events.
messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
    let { messageLog } = await messenger.storage.local.get({ messageLog: [] });

    for await (let message of messageTools.iterateMessagePages(messages)) {
        messageLog.push({
            folder: folder.name,
            time: Date.now(),
            message: message
        })
    }

    await messenger.storage.local.set({ messageLog });
})

// Create the menu entries.
let menu_id = await messenger.menus.create({
    title: "Show received email",
    contexts: [
        "browser_action",
        "tools_menu"
    ],
});

// Register a listener for the menus.onClicked event.
await messenger.menus.onClicked.addListener(async (info, tab) => {
    if (info.menuItemId == menu_id) {
        // Our menu entry was clicked
        let { messageLog } = await messenger.storage.local.get({ messageLog: [] });

        let now = Date.now();
        let last24h = messageLog.filter(e => (now - e.time) < 24 * 60 * 1000);

        for (let entry of last24h) {
            messenger.notifications.create({
                "type": "basic",
                "iconUrl": "images/internet.png",
                "title": `${entry.folder}: ${entry.message.author}`,
                "message": entry.message.subject
            });
        }
    }
});

// Register the message display script.
messenger.messageDisplayScripts.register({
    js: [{ file: "messageDisplay/message-content-script.js" }],
    css: [{ file: "messageDisplay/message-content-styles.css" }],
});

/**
 * Add a handler for the communication with other parts of the extension,
 * like our message display script.
 *
 * Note: It is best practice to always define a synchronous listener
 *       function for the runtime.onMessage event.
 *       If defined asynchronously, it will always return a Promise
 *       and therefore answer all messages, even if a different listener
 *       defined elsewhere is supposed to handle these.
 * 
 *       The listener should only return a Promise for messages it is
 *       actually supposed to handle.
 */
messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // Check what type of message we have received and invoke the appropriate
    // handler function.
    if (message && message.hasOwnProperty("command")) {
        return commandHandler(message, sender);
    }
    // Return false if the message was not handled by this listener.
    return false;
});

// The actual (asynchronous) handler for command messages.
async function commandHandler(message, sender) {
    // Get the message currently displayed in the sending tab, abort if
    // that failed.
    const messageHeader = await messenger.messageDisplay.getDisplayedMessage(
        sender.tab.id
    );

    if (!messageHeader) {
        return;
    }

    // Check for known commands.
    switch (message.command) {
        case "getBannerDetails":
            // Create the information we want to return to our message display
            // script.
            return { text: `Mail subject is "${messageHeader.subject}"` };
        case "markUnread":
            // Mark the message as unread.
            messenger.messages.update(messageHeader.id, {
                read: false,
            });
            break;
    }
}
```

### Installing

As described in the [first part of the Hello World Extension Tutorial](/add-ons/hello-world-add-on#installing), go to the Add-ons Manager to open the Debug Add-on Page and temporarily install the extension.

### Trying it Out

Open a message. There should be a red banner added at the top of it with its subject and a button labelled *"Mark unread"*. Clicking that button should mark the message as unread


# A Guide to Extensions

How to create extensions for Thunderbird.

An extension is a Thunderbird add-on, that provides additional functionality by adding new user interface elements, alter content, or perform background tasks.

## The `manifest.json` file

The main configuration file of an extension is called `manifest.json`, also referred to as the *manifest*. Besides defining some of the extension's basic properties like name, description and ID, it also defines how the extension hooks into Thunderbird:

{% code title="manifest.json" %}

```json
{
    "manifest_version": 2,
    "name": "Hello World",
    "description": "A basic Hello World extension!",
    "version": "1.0",
    "author": "[Your Name Here]",
    "browser_specific_settings": {
        "gecko": {
            "id": "helloworld@yoursite.com",
            "strict_min_version": "128.0"
        }
    },
    "icons": {
        "64": "images/icon-64px.png",
        "32": "images/icon-32px.png",
        "16": "images/icon-16px.png"
    },
    "background": {
        "page": "background.html"
    },
    "options_ui": {
      "page": "options/options.html",
      "open_in_tab": false,
      "browser_style": true
    },
    "permissions": [
      "storage"
    ]
}
```

{% endcode %}

A list of all manifest keys supported by Thunderbird can be found in the following document:

{% content-ref url="/pages/-MLqVkNaIImUFDlVNhSd" %}
[Supported Manifest Keys](/add-ons/mailextensions/supported-manifest-keys)
{% endcontent-ref %}

The following manifest keys define basic properties:

* `manifest_version`: A *mandatory key* defining the Manifest version used by the extension. Supported versions are `2` and `3` (since Thunderbird 128). The Manifest defines the basic rules how a WebExtension needs to be crafted and how it can interact with Thunderbird.
* `name` : A *mandatory key* to set the name of the extension.
* `version` : A *mandatory key* to define a number that denotes the version of the extension.
* `description` : A brief description of what the extension does.
* `author` : The name of a person or company representing the extension developer.

{% hint style="info" %}
The `name` and the `description` of the shown example are only in English. [This MDN article about **Localization**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json) explains how to use the WebExtension i18n API to localize these keys.
{% endhint %}

The `browser_specific_settings.gecko` manifest key defines the following properties:

* `strict_min_version`: Defines the lowest targeted version of Thunderbird.
* `strict_max_version`: Defines the highest targeted version of Thunderbird. It can be set to a specific version or a broader match to limit it to a branch (for example `128.*`). Usually only needed if Experiments are included.
* `id`: The id serves as a unique identifier for the extension and is mandatory in order upload an extension to ATN or to be able to install it from an XPI file.

{% hint style="info" %}
Best practice is to use an "email-address-style" id (but not a real email address) on a domain you control, for example `name-of-your-addon@example.com`, if you own `example.com`. As the id of your add-on cannot be changed once it is published, it is highly recommended to use a domain that you plan to keep for the forseeable future. If you don't have a domain to use, feel free to use:\
`<atn-user-name>.<add-on-name>@addons.thunderbird.net`\\

Alternatively, you may use an UUID enclosed in curly braces, for example:\
`{e4aa2097-8ee9-49a4-9ec7-c633b1e8dfda}`
{% endhint %}

### Extension Icons

The `icons` manifest key tells Thunderbird the location of icons, which should be used to represent the MailExtension. Thunderbird supports basic image types like PNG files, but also SVG files. Thunderbird uses different file icon sizes in different places and allows registering a dedicated file for each size. The MailExtension will use the standard puzzle icon, if no icons have been defined.

```json
    "icons": {
        "64": "images/icon-64px.png",
        "32": "images/icon-32px.png",
        "16": "images/icon-16px.png"
    }
```

### Background Page

The extension's background page is loaded in a hidden window when the add-on is started and can be used to load additional JavaScript files.

```json
    "background": {
        "page": "background.html"
    }
```

The background page specifies the JavaScript files to be loaded:

```markup
<html lang="en">
    <head>
        <meta charset="utf-8">
        <script src="common.js"></script>
        <script type="module" src="background.js"></script>
    </head>
    <body>
    </body>
</html>
```

{% hint style="success" %}
The `type` attribute in the shown `script` tag of `background.js` defines it as a top level module and enables it to use modern [ES6 modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
{% endhint %}

#### Defining one or more background scripts directly

Instead of defining a background page, the extension can specify a simple list of JavaScript files. This will auto-generate a background page and then load the JavaScript files.

```json
    "background": {
        "scripts": [
            "common.js",
            "background.js"
        ],
        "type": "module"
    }
```

{% hint style="success" %}
The optional `type` property in the shown `background` definition loads all specified scripts as modules, allowing all of them to use [ES6 modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
{% endhint %}

### Options Page

The `options_ui` manifest key defines the standard MailExtension options page. The defined page will be displayed in the add-on manager by default.

```json
    "options_ui": {
      "page": "options/options.html",
      "open_in_tab": false,
      "browser_style": true
    }
```

The appearance of the options page can be configured as follows:

* `open_in_tab` : Open the options page in a tab instead of inline in the add-on manager.
* `browser_style`: Use default browser styles for the options page (recommended).

An inline options page may look as follows:

![](/files/-MJ8zlX8_gXUXTGEmgwh)

### User Interface Elements

Some UI elements Thunderbird WebExtensions can use are controlled by manifest keys, for example

* `browser_action` (renamed to `action` in Manifest v3)
* `compose_action`
* `message_display_action`

Further information about these UI elements can be found in the following document:

{% content-ref url="/pages/-MLqd1VC7wTIxLseizyt" %}
[Supported UI Elements](/add-ons/mailextensions/supported-ui-elements)
{% endcontent-ref %}

### Permissions

A core principle of the WebExtension technology is the use of permissions, so users can see which areas of Thunderbird an add-on wants to access. Add-on developers can predefine all requested permissions in the `permissions` manifest key:

```json
    "permissions": [
      "storage"
    ]
```

Information about required permissions can be found in the following document:

{% content-ref url="/pages/-MLqqwPYJM0EpemxiifU" %}
[Supported WebExtension APIs](/add-ons/mailextensions/supported-webextension-api)
{% endcontent-ref %}

For most permissions, the user must either accept all of the requested permissions during add-on install, or abort the install. Some permissions however can be requested as [optional permissions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/optional_permissions), which can be managed by the user during runtime.

## WebExtension Scripts

All JavaScript files loaded by an extension have access to:

* Standard JavaScript methods
* [Web API](https://developer.mozilla.org/docs/Web/API) (if the browser compatibility chart lists Firefox, the API also works in Thunderbird)
* WebExtension APIs (see restrictions for content scripts and cloudFile scripts below)

A list of all WebExtension APIs supported by Thunderbird can be found in the following document:

{% content-ref url="/pages/-MLqqwPYJM0EpemxiifU" %}
[Supported WebExtension APIs](/add-ons/mailextensions/supported-webextension-api)
{% endcontent-ref %}

### Content Scripts

Content scripts (including [compose scripts](https://webextension-api.thunderbird.net/en/mv2/composeScripts.html) and [message display scripts](https://webextension-api.thunderbird.net/en/mv2/messageDisplayScripts.html)) can only use the following WebExtension APIs:

* [runtime.connect()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/connect)
* [runtime.getManifest()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getManifest)
* [runtime.getURL()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getURL)
* [runtime.onConnect](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onConnect)
* [runtime.onMessage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)
* [runtime.sendMessage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage)
* [i18n.getMessage()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getMessage)
* [i18n.getAcceptLanguages()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getAcceptLanguages)
* [i18n.getUILanguage()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getUILanguage)
* [i18n.detectLanguage()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/detectLanguage)
* [menus.getTargetElement](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus/getTargetElement)
* [storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage).\*

Content scripts can [communicate with background scripts](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Communicating_with_background_scripts) using runtime messaging, and thereby can indirectly access the WebExtension APIs. See our [messageDisplayScript example extension](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/messageDisplayScript).

### CloudFile Management Scripts

A script loaded from a CloudFile [management\_url](https://webextension-api.thunderbird.net/en/mv2/cloudFile.html#manifest-file-properties) can only use the following WebExtension APIs:

* [cloudFile](https://webextension-api.thunderbird.net/en/mv2/cloudFile.html).\*
* [extension](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/extension).\*
* [i18n](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n).\*
* [runtime](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime).\*
* [storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage).\*

CloudFile management scripts can [communicate with background scripts](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Communicating_with_background_scripts) using runtime messaging, and thereby can indirectly access the WebExtension APIs.

## Experiment APIs

The currently available WebExtension APIs are not yet sufficient, as some areas of Thunderbird are not accessible through these APIs. We are working on improving the situation.

Currently and for the foreseeable future Thunderbird supports Experiment APIs (a.k.a. Experiments), which are WebExtension APIs that are bundled and shipped together with a MailExtension. They interact directly with Thunderbird's internal APIs and allow add-ons to use additional features not yet available via built-in WebExtension APIs.

These additional APIs can be registered in the `manifest.json` file by defining an implementation script and a schema file describing the interface:

```json
    "experiment_apis": {
        "LegacyPrefs": {
            "schema": "api/LegacyPrefs/schema.json",
            "parent": {
                "scopes": ["addon_parent"],
                "paths": [["LegacyPrefs"]],
                "script": "api/LegacyPrefs/implementation.js",
                "events": ["startup"]
            }
        }
    }
```

{% hint style="danger" %}
Experiment APIs have full access to Thunderbird's core functions and can bypass the WebExtension permission system entirely. Including one or more Experiment APIs will therefore disable the individual permission prompt and instead prompt the user only for the [*Have full, unrestricted access to Thunderbird, and your computer*](https://support.mozilla.org/kb/permission-request-messages-thunderbird-extensions) permission.

The use of optional permissions is not supported for the same reason.
{% endhint %}

If you'd like to learn more about experiments, check out this detailed introduction:

{% content-ref url="/pages/-MAkO\_iyb2AT8xfUCXK3" %}
[Introducing Experiments](/add-ons/mailextensions/experiments)
{% endcontent-ref %}

### Sharing Experiment APIs

Developers can share and re-use Experiments, if their add-ons have similar needs. Before starting to work on your own Experiment, check if any of the following APIs could already provide the functionality you need. Using them and providing feedback to their developers will help to improve these APIs.

<table><thead><tr><th width="289.3333333333333">Name</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://github.com/thunderbird/webext-experiments/tree/main/calendar">Calendar</a></td><td>Draft for calendar-related APIs in Thunderbird.</td></tr><tr><td><a href="https://github.com/gruemme/tb-api-compose_message_headers">ComposeMessageHeaders</a></td><td><p>Adds missing functionality to add headers to a newly composed message.<br></p><p><em>Note: Adding <code>X-</code> headers is supported by the compose API since Thunderbird 102</em>.</p></td></tr><tr><td><a href="https://github.com/rsjtdrjgfuzkfg/thunderbird-experiments/tree/master/experiments/customui">CustomUI</a></td><td>A generic UI extension framework based on iframes registered at fixed extension points.<br><br><em>Note: Does not yet fully support Thunderbird Supernova</em></td></tr><tr><td><a href="https://github.com/thunderbird/webext-support/tree/master/experiments/FileSystem">FileSystem</a></td><td>An API to access files in the users profile folder. Until Mozilla has made a final decision about including the <a href="https://web.dev/file-system-access/">Chrome FileSystem API</a>, this API can be used as an interim solution.</td></tr><tr><td><a href="https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyCSS">LegacyCSS</a></td><td>Load custom CSS files into Thunderbird windows.</td></tr><tr><td><a href="https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs">LegacyPrefs</a></td><td>Access Thunderbird system preferences.</td></tr><tr><td><a href="https://github.com/thunderbird/webext-experiments/tree/main/NotificationBox">NotificationBox</a></td><td>Show notifications inside Thunderbird.</td></tr><tr><td><a href="https://github.com/rsjtdrjgfuzkfg/thunderbird-experiments/tree/master/experiments/runtime">Runtime.onDisable</a></td><td>Permit WebExtensions to perform (time-limited) cleanup tasks after the add-on is disabled or uninstalled.</td></tr><tr><td><a href="https://github.com/rsjtdrjgfuzkfg/thunderbird-experiments/tree/master/experiments/tcp">TCP</a></td><td>TCP support based on ArrayBuffers (currently client side only).</td></tr></tbody></table>

If you have created an Experiment API which you think could be beneficial to other developers, please [tell us about it](https://github.com/thunderbird/webext-experiments/issues/new), so we can include it here.

### Proposing APIs to be included in Thunderbird

Creating a good WebExtension API for Thunderbird is not an easy task. New APIs need to be generic and distinct from other APIs. Their interfaces have to be designed with foresight as we should avoid scenarios, where we have to make backward incompatible changes later because we have missed something.

If you want to propose and maybe collaborate on a new API, the following process is suggested:

1. Announcing the idea and a first outline of the suggested API on [discuss.thunderbird.net](https://discuss.thunderbird.net/groups/addons). An actual implementation is not yet needed, but a general concept of how the API is supposed to work is helpful. This allows the add-on developer community to provide feedback and to make sure the design will cover their needs as well.
2. Publishing a detailed API description, which can be viewed and commented. Google docs have worked quite well (see [here](https://docs.google.com/document/d/15awbKiVfdOTmsRpgD1dxm3gvOt08EQZDSnMl8QRBFoY/edit?usp=sharing) and [here](https://docs.google.com/document/d/1mTwVozOiEcDCw3QQKxVz-N5yHY8SVFC9KUL4_x0IN68/edit)), but any platform that allows to collaborate will be sufficient. The goal is to shape out the event, function and type definitions of the API.
3. Creating a [tracking bug on bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?product=Thunderbird\&component=Add-Ons%3A+Extensions+API), referencing the API description, so the core development team is notified and can comment as well.
4. Publishing a working implementation, so add-on developers can use it and provide feedback.
5. Adding a patch to the tracking bug and request review.


# Supported Manifest Keys

Descriptions of all manifest keys supported by Thunderbird.

*Several manifest keys in the following table are common to Thunderbird and Firefox and link to MDN description pages. Please be aware, that MDN is dedicated to browsers and of course to Firefox. Some information listed on MDN may not apply to Thunderbird.*

The two columns *MV2* and *MV3* specify whether the manifest key is supported in Manifest V2 and/or Manifest V3.

<table><thead><tr><th width="258">Manifest Key</th><th width="71" data-type="checkbox">MV2</th><th width="71" data-type="checkbox">MV3</th><th>Description</th></tr></thead><tbody><tr><td>action</td><td>false</td><td>true</td><td>A <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#browser-action-button">browser action is a button</a> that your extension can add to Thunderbird's main mailTab toolbar. The button has an icon, and may optionally have a popup whose content is specified using HTML, CSS, and JavaScript.<br><br><strong>Note:</strong> This key has been renamed from <code>browser_action</code> to <code>action</code> in Manifest V3.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings#firefox_gecko_properties">applications</a></td><td>true</td><td>false</td><td><p>Deprecated. Use</p><p><code>browser_specific_settings</code></p><p>instead.</p></td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/author">author</a></td><td>true</td><td>true</td><td>Defines the extension's author. If the <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/developer"><code>developer</code></a> key is supplied and it contains the <code>name</code> property, it will override the <code>author</code> key. There is no way to specify multiple authors. This is a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json">localizable property</a>.</td></tr><tr><td><a href="https://developer.thunderbird.net/add-ons/mailextensions#background-page">background</a></td><td>true</td><td>true</td><td>Use the <code>background</code> key to include one or more background scripts, and optionally a background page in your extension. Background scripts are loaded as soon as the extension is loaded and stay loaded until the extension is disabled or uninstalled.</td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/browserAction.html">browser_action</a></td><td>true</td><td>false</td><td>A <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#browser-action-button">browser action is a button</a> that your extension can add to Thunderbird's main mailTab toolbar. The button has an icon, and may optionally have a popup whose content is specified using HTML, CSS, and JavaScript.<br><br><strong>Note:</strong> This key has been renamed from <code>browser_action</code> to <code>action</code> in Manifest V3.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings">browser_specific_settings</a></td><td>true</td><td>true</td><td><p>Defines properties that are specific to a particular host application. Information for Thunderbird are stored in:</p><p><code>browser_specific_settings.gecko</code></p><p>More details can be found in our <a href="https://developer.thunderbird.net/add-ons/mailextensions#basic-extension-properties">hello world example</a>.</p></td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/cloudFile.html">cloud_file</a></td><td>true</td><td>true</td><td>Defines a file link provider, which can be used to upload large attachments to a server, instead of attaching them directly to the email.</td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/commands.html">commands</a></td><td>true</td><td>true</td><td>Use the commands API to add keyboard shortcuts that trigger actions in your extension, for example, an action to open a browser action popup.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts">content_scripts</a></td><td>true</td><td>false</td><td>Instructs Thunderbird to load <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts">content scripts</a> into web page tabs and windows, whose URL matches a given pattern.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_security_policy">content_security_policy</a></td><td>true</td><td>false</td><td>The default policy restricts the sources from which a content script can load <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"><code>&#x3C;script></code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object"><code>&#x3C;object></code></a> resources, and disallows potentially unsafe practices such as the use of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><code>eval()</code></a>. See <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_Security_Policy#Default_content_security_policy">Default content security policy</a> to learn more about the implications of this.</td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/composeAction.html">compose_action</a></td><td>true</td><td>true</td><td>A <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#compose-action-button">compose action is a button</a> that your extension can add to the toolbar of Thunderbird's message compose tabs. The button has an icon, and may optionally have a popup whose content is specified using HTML, CSS, and JavaScript.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/default_locale">default_locale</a></td><td>true</td><td>true</td><td>This key must be present if the extension contains the <code>_locales</code> directory, and must be absent otherwise. It identifies a subdirectory of <code>_locales</code>, and this subdirectory will be used to find the default strings for your extension. See <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization">Internationalization</a>.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/description">description</a></td><td>true</td><td>true</td><td>Defines a short description of the extension, intended for display in the Add-ons Manager. This is a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json">localizable property</a>.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/developer">developer</a></td><td>true</td><td>true</td><td>Defines the name of the extension's developer and their homepage URL, intended for display in the add-on manager tab. The <code>name</code> and <code>url</code> properties, if present, will override the <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/author"><code>author</code></a> and <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/homepage_url"><code>homepage_url</code></a> keys, respectively. This is a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json">localizable property</a>.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/dictionaries">dictionaries</a></td><td>true</td><td>true</td><td>The <code>dictionaries</code> key specifies the <code>locale_code</code> for which your extension supplies a dictionary.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/homepage_url">homepage_url</a></td><td>true</td><td>true</td><td><p>URL for the extension's home page. If the <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/developer"><code>developer</code></a> key is supplied and it contains the <code>url</code> property, this will override the <code>homepage_url</code> key.</p><p>This is a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json">localizable property</a>.</p></td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/icons">icons</a></td><td>true</td><td>true</td><td>The <code>icons</code> key specifies icons for your extension. Those icons will be used to represent the extension in components such as the Add-ons Manager.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/manifest_version">manifest_version</a></td><td>true</td><td>true</td><td>This key specifies the manifest version used by this extension. Supported are <code>2</code> and <code>3</code> (since Thunderbird Beta 110).</td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/messageDisplayAction.html">message_display_action</a></td><td>true</td><td>true</td><td>A <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#message-display-action-button">message display action is a button</a> that your extension can add to the toolbar of Thunderbird's message display tabs. The button has an icon, and may optionally have a popup whose content is specified using HTML, CSS, and JavaScript.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/name">name</a></td><td>true</td><td>true</td><td>Defines the name of the extension. This is used to identify the extension in the Add-on manager and on sites like addons.thunderbird.net.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/optional_permissions">optional_permissions</a></td><td>true</td><td>false</td><td>Defines permissions, which should be requested dynamically (when needed) and not during install.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/options_ui">options_ui</a></td><td>true</td><td>true</td><td>Defines an <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Options_pages">options page</a> for your extension.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions">permissions</a></td><td>true</td><td>true</td><td>This key requests <a href="https://developer.thunderbird.net/add-ons/mailextensions#permissions">special powers for your extension</a>. This key is an array of strings, and each string is a request for a permission.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers">protocol_handlers</a></td><td>true</td><td>true</td><td>This key registers one or more web-based protocol handlers. It allows to register a website or an extension page as a handler for a particular protocol. <em>Note: The default click handler in Thunderbird web tabs is currently not working correctly with custom defined protocol handlers. It does work in WebExtension windows.</em></td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/short_name">short_name</a></td><td>true</td><td>true</td><td><p>Short name for the extension. If given, this will be used in contexts where the <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/name">name</a> field is too long. It's recommended that the short name should not exceed 12 characters. If the short name field is not included in manifest.json, then name will be used instead and may be truncated.</p><p>This is a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json">localizable property</a>.</p></td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/theme.html">theme</a></td><td>true</td><td>true</td><td>This key defines a static theme to be applied to Thunderbird.</td></tr><tr><td><a href="https://webextension-api.thunderbird.net/en/mv2/theme.html">theme_experiment</a></td><td>true</td><td>true</td><td>This key enables the definition of experimental <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/theme"><code>theme</code></a> key properties for the Thunderbird interface. These experiments are a precursor to proposing new theme features for inclusion in Thunderbird.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/user_scripts">user_scripts</a></td><td>true</td><td>false</td><td>Instructs the browser to load a script packaged in the extension, known as the API script, this script is used to export a set of custom API methods for use in user scripts.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/version">version</a></td><td>true</td><td>true</td><td>The version of the extension, formatted as numbers and ASCII characters separated by dots. For the details of the version format, see the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/version">Version format</a> page.</td></tr><tr><td><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/web_accessible_resources">web_accessible_resources</a></td><td>true</td><td>false</td><td>This key exposes selected extension resources (for example images, HTML, CSS or JavaScript) to web pages.</td></tr></tbody></table>


# Supported UI Elements

Descriptions and examples of MailExtension APIs to interact with Thunderbird's user interface.

## Browser Action

A browser action adds a button to Thunderbird's main toolbar:

![](/files/-MJ90vnov5TcfO6CiXBg)

It is controlled by the `browser_action` manifest key in the extension's `manifest.json` file:

```javascript
"browser_action": {
    "default_title": "Restart",
    "default_icon" : "restart.png",
    "default_popup": "browser_popup.html"
},
```

If a `default_popup` is defined, a popup will be opened and the defined html page will be loaded, when the button is clicked. Additionally, you can use the [browserAction](https://webextension-api.thunderbird.net/en/mv2/browserAction.html) API in your background script to interact with the button to modify *badge text*, *icon*, *title* or its *enabled state*.

If the action button is defined as a [menu typed action button](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton), it will open a drop-down menu when clicked.

**Note:** The `browserAction` API has been renamed to `action` in [Manifest V3](https://webextension-api.thunderbird.net/en/mv3/action.html).

## Compose Action

A compose action adds a button to the toolbar of the composer window:

![](/files/-MJ91Vy66ElvcV7dj-nO)

It is controlled by `compose_action` manifest key in the extension's `manifest.json` file:

```javascript
"compose_action": {
    "default_title": "Expand lists",
    "default_icon" : "lists.png",
    "default_popup": "compose_popup.html"
},
```

If a `default_popup` is defined, a popup will be opened and the defined html page will be loaded, when the button is clicked. Additionally, you can use the [composeAction](https://webextension-api.thunderbird.net/en/mv2/composeAction.html) API in your background script to interact with the button to modify *badge text*, *icon*, *title* or its *enabled state*.

If the action button is defined as a [menu typed action button](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton), it will open a drop-down menu when clicked.

## Message Display Action

A message display action adds a button to the toolbar of the message view window:

![](/files/-MLrqNTSKKn-E5wOXpIp)

It is controlled by the `message_display_action` manifest key in the extension's `manifest.json` file:

```javascript
"message_display_action": {
    "default_title": "Copy Patch",
    "default_icon" : "patch.png",
    "default_popup": "message_popup.html"
    },
```

If a `default_popup` is defined, a popup will be opened and the defined html page will be loaded, when the button is clicked. Additionally, you can use the [messageDisplayAction](https://webextension-api.thunderbird.net/en/mv2/messageDisplayAction.html) API in your background script to interact with the button to modify *badge text*, *icon*, *title* or its *enabled state*.

If the action button is defined as a [menu typed action button](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton), it will open a drop-down menu when clicked.

## CloudFile Provider Configuration

Thunderbird supports cloud providers to upload large attachments to a server, instead of attaching them directly to the email. Extensions can register such cloud providers using the [CloudFile](https://webextension-api.thunderbird.net/en/mv2/cloudFile.html) API. These providers can be managed in Thunderbird's Composition options:

![](/files/-MM0CfQCHDEkLtJfXMen)

## Menu Items

The [menus](https://webextension-api.thunderbird.net/en/mv2/menus.html) API allows modifying Thunderbird's menus by adding or overriding menu items. The menu items can be added to different menus based on the provided `context` type. Examples can be found in our [example repository](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menu).

Thunderbird menus are accessible through the following context types:

<table><thead><tr><th width="372.3333333333333">Context</th><th width="369">Description</th></tr></thead><tbody><tr><td><code>all</code></td><td>Add menu entries to all supported contexts, excluding <code>tab</code> and <code>tools_menu</code>.</td></tr><tr><td><code>all_message_attachments</code></td><td>Add entries to the context menu of the summary area of message attachments.<br><br><img src="/files/3SI4MEHqKXCG0tmTH2x5" alt=""><br><br>If the message has only one attachment, then the <code>message_attachments</code> context is used instead.</td></tr><tr><td><code>action</code>,<br><code>browser_action</code>, <code>compose_action</code> , <code>message_display_action</code><br><br>N<strong>ote</strong>: <code>browser_action</code> is available in Manifest V2 and <code>action</code> is available in<br>Manifest V3.</td><td>Add entries to the context menu of action buttons.<br><br><img src="/files/ES4rouB4ubGgevi9lUIX" alt=""></td></tr><tr><td><code>action_menu</code>,<br><code>browser_action_menu</code>, <code>compose_action_menu</code>, <code>message_display_action_menu</code>,<br><br><strong>Note</strong>: <code>browser_action_menu</code> is available in Manifest V2 and <code>action_menu</code> is available in Manifest V3.</td><td><p>Add entries to the drop-down menu of <a href="https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton">menu typed action buttons</a>.</p><p><img src="/files/WH5RmU5StcPgb6Oo7l2S" alt="" data-size="original"></p></td></tr><tr><td><code>compose_attachments</code></td><td>Add entries to the context menu of attachments in the compose window.<br><br><img src="/files/VUDdnFA5wBvd8EzgwNlg" alt=""></td></tr><tr><td><code>editable</code>,<br><code>password</code></td><td><p>Add entries to the context menu of text/password input fields in WebExtension windows, web tabs or message display tabs.<br></p><p>Limited to content areas.<br><br><img src="/files/-MLx0_3xEM5hXKSj8YsQ" alt="" data-size="original"></p></td></tr><tr><td><code>folder_pane</code></td><td>Add entries to the context menu of the folder pane of mail tabs.<br><br><img src="/files/-MLwhIP-LihMPd5ZaydG" alt="" data-size="original"></td></tr><tr><td><code>image</code>,<br><code>audio</code>,<br><code>video</code></td><td>Add entries to the context menu of embedded images, audio or video players in WebExtension windows, web tabs or message display tabs.<br><br>Limited to content areas.<br><br><img src="/files/-MLx30yAHbENfA5QFUmN" alt="" data-size="original"></td></tr><tr><td><code>link</code></td><td><p>Add entries to the context menu of links in WebExtension windows, in web tabs or in message display tabs.</p><p><br>Limited to content areas.<br><br><img src="/files/-MLwgnYQLGvpBeKfjt1V" alt="" data-size="original"></p></td></tr><tr><td><code>message_attachments</code></td><td>Add entries to the context menu of attachments of messages.<br><br><img src="/files/vVMpZtobwmoMmU13Gr8M" alt=""></td></tr><tr><td><code>message_list</code></td><td>Add entries to the context menu of the message list area of a mail tab.<br><br><img src="/files/-MLwhAsNB0dLIsjdoFNT" alt="" data-size="original"></td></tr><tr><td><code>page</code>,<br><code>frame</code></td><td><p>Add entries to the context menu of WebExtension windows, web tabs or message display tabs, if none of the other content contexts apply (link, selection, image, audio, video, editable, password).</p><p><br>If the page is loaded inside a frame, the <code>frame</code> context applies, otherwise the <code>page</code> context.<br><br>Limited to content areas.<br><br><img src="/files/-MLwhxQvMG0S4RwyN7gn" alt="" data-size="original"></p></td></tr><tr><td><code>selection</code></td><td><p>Add entries to the context menu in WebExtension windows, web tabs or message display tabs, if any text has been selected.<br></p><p>Limited to content areas.<br><br><img src="/files/-MLwgzJB9uf_lps_qfyB" alt="" data-size="original"></p></td></tr><tr><td><code>tab</code></td><td><p>Add entries to the context menu of tab ribbons.<br><br><img src="/files/-MLwgYE1rFx7NLgG2pax" alt=""></p><p>The <code>tab</code> context can also be used to override the context menu of content pages (action popups or content tabs).</p><p><img src="/files/NrXEuOLmOhDQv1AKqWnE" alt="" data-size="original"></p><p>See the <a href="https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menu">menu example</a> for more details.</p></td></tr><tr><td><code>tools_menu</code></td><td>Add entries to Thunderbird's tools menu.<br><br><img src="/files/joTxnqIdLEfBxRrAvGdb" alt=""></td></tr></tbody></table>


# Supported WebExtension APIs

Descriptions of all WebExtension API supported by Thunderbird.

## Thunderbird WebExtension APIs

Thunderbird provides the following messenger related WebExtension APIs, which are sometimes referred to as MailExtension APIs.

| API                                                                                                 | Permission                                                                                                                                                                                        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [accounts](https://webextension-api.thunderbird.net/en/mv2/accounts.html)                           | accountsRead                                                                                                                                                                                      | Enables an extension to access information of accounts and identities configured in Thunderbird's account manager.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| [addressBooks](https://webextension-api.thunderbird.net/en/mv2/addressBooks.html)                   | addressBooks                                                                                                                                                                                      | Enables an extension to access, modify, create and delete Thunderbird address books.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| [browserAction](https://webextension-api.thunderbird.net/en/mv2/browserAction.html)                 |                                                                                                                                                                                                   | <p>Enables an extension to interact with a <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#browser-action">browser action button</a>.<br></p><p><em>Permission info:</em></p><ul><li>This API does not require a permission, but a <code>browser\_action</code> manifest key.</li></ul><p><strong>Note:</strong> This API has been renamed to action in <a href="https://webextension-api.thunderbird.net/en/mv3/action.html">Manifest V3</a>.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| [cloudFile](https://webextension-api.thunderbird.net/en/mv2/cloudFile.html)                         |                                                                                                                                                                                                   | <p>Enables an extension to register a cloudFile provider, which can be used to upload large attachments to a server, instead of attaching them directly to the email.</p><p><em>Permission info:</em></p><ul><li>This API does not require a permission, but a <code>cloudFile</code> manifest key.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| [commands](https://webextension-api.thunderbird.net/en/mv2/commands.html)                           |                                                                                                                                                                                                   | <p>The commands API adds keyboard shortcuts that can trigger actions in an extension.<br><br><em>Permission info:</em></p><ul><li>This API does not require a permission, but a <code>commands</code> manifest key.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| [compose](https://webextension-api.thunderbird.net/en/mv2/compose.html)                             | compose                                                                                                                                                                                           | Enables an extension to open a new message compose window or react to events while the message is being composed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| [composeAction](https://webextension-api.thunderbird.net/en/mv2/composeAction.html)                 |                                                                                                                                                                                                   | <p>Enables an extension to interact with a <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#compose-action">compose action button</a>.</p><p><br><em>Permission info:</em></p><ul><li>This API does not require a permission, but a <code>compose\_action</code> manifest key.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| [composeScripts](https://webextension-api.thunderbird.net/en/mv2/composeScripts.html)               | compose                                                                                                                                                                                           | Functionally is the same as the [contentScripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/contentScripts) API except that it works on the document of email messages during composition.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| [contacts](https://webextension-api.thunderbird.net/en/mv2/contacts.html)                           | addressBooks                                                                                                                                                                                      | Enables an extension to access, modify, create and delete contacts in Thunderbird address books.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| [folders](https://webextension-api.thunderbird.net/en/mv2/folders.html)                             | accountsFolders                                                                                                                                                                                   | Enables an extension to access, modify, create and delete mail account folders.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| [mailingLists](https://webextension-api.thunderbird.net/en/mv2/mailingLists.html)                   | addressBooks                                                                                                                                                                                      | Enables an extension to access, modify, create and delete mailing lists in Thunderbird address books.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [mailTabs](https://webextension-api.thunderbird.net/en/mv2/mailTabs.html)                           | accountsFolders                                                                                                                                                                                   | <p>Enables an extension to interact with Thunderbird's main window.<br><br><em>Permission info:</em></p><ul><li>The <code>accountsFolders</code> permission is only needed to set the currently displayed folder.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| [menus](https://webextension-api.thunderbird.net/en/mv2/menus.html)                                 | <p>menus,<br><a href="https://webextension-api.thunderbird.net/en/mv2/menus.html#overridecontext-contextoptions">menus.overrideContext</a>,</p><p>accountsRead, messagesRead,</p><p>activeTab</p> | <p>Enables an extension to add (context-) menu entries to Thunderbird menus.<br><br><em>Permission info:</em></p><ul><li>The <code>menus.overrideContext</code> permission is needed to <a href="https://webextension-api.thunderbird.net/en/mv2/menus.html#overridecontext-contextoptions">override a default menu entry</a>.</li><li>The <code>accountsRead</code> and <code>messagesRead</code> permissions are needed to populate the associated fields in the <a href="https://webextension-api.thunderbird.net/en/mv2/menus.html#onclickdata"><code>OnClickedData</code></a> object.</li><li>The <code>activeTab</code> permission is (currently) needed to get advanced information for the <code>onShown</code> event (see <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1664423">bug 1664423</a>)</li></ul>                                                                                                                                                                                                  |
| [messageDisplay](https://webextension-api.thunderbird.net/en/mv2/messageDisplay.html)               | messagesRead                                                                                                                                                                                      | Enables an extension to react on and interact with the currently displayed messages.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| [messageDisplayAction](https://webextension-api.thunderbird.net/en/mv2/messageDisplayAction.html)   |                                                                                                                                                                                                   | <p>Enables an extension to interact with a <a href="https://developer.thunderbird.net/add-ons/mailextensions/supported-ui-elements#message-display-action">message display action button</a>.<br><br><em>Permission info:</em></p><ul><li>This API does not require a permission, but a <code>message\_display\_action</code> manifest key.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [messageDisplayScripts](https://webextension-api.thunderbird.net/en/mv2/messageDisplayScripts.html) | messagesModify                                                                                                                                                                                    | Functionally is the same as the [contentScripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/contentScripts) API except that it works on the document of email messages being displayed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| [messages](https://webextension-api.thunderbird.net/en/mv2/messages.html)                           | <p>messagesRead,</p><p>messagesMove,<br>accountsRead</p>                                                                                                                                          | <p>Enables an extension to list, search, read, copy, move and delete messages.</p><p><br><em>Permission info:</em></p><ul><li>The <code>messagesRead</code> permission is needed to list, read, mark and tag messages.</li><li>The <code>messagesMove</code> permission is needed to copy, move and delete messages.</li><li>The <code>accountsRead</code> permission is needed by any function/event which involves folder information.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| [tabs](https://webextension-api.thunderbird.net/en/mv2/tabs.html)                                   | tabs, activeTab, compose, messageModify                                                                                                                                                           | <p>Enables an extension to interact with Thunderbird's tab system. It allows to create, modify, and rearrange tabs and to communicate with scripts in tabs.<br><br><em>Permission info:</em></p><ul><li>The <code>tabs</code> or <code>activeTab</code> permission is only needed if the <code>url</code>, <code>favIconUrl</code> or <code>title</code> information of tabs are accessed. The <code>tab</code> permission will allow access to that information in all tabs, the <code>activeTab</code> permission restricts that to the active tab.<br>Note: The <code>activeTab</code> permission implies the <code>\<all\_urls></code> host permission for the active (web page -) tab.</li><li>The <code>compose</code> permissions is needed to call <code>tabs.executeScript()</code> and <code>tabs.insertCSS()</code> for compose tabs.</li><li>The <code>messageModify</code> permission is needed to call <code>tabs.executeScript()</code> and <code>tabs.insertCSS()</code> for a message display tab.</li></ul> |
| [theme](https://webextension-api.thunderbird.net/en/mv2/theme.html)                                 | theme                                                                                                                                                                                             | <p>The theme API can be used to create static or dynamic Thunderbird themes. Theme experiments are supported.</p><p><em>Permission info:</em></p><ul><li>The <code>theme</code> permission is needed to dynamically update the theme.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| [windows](https://webextension-api.thunderbird.net/en/mv2/windows.html)                             | tabs                                                                                                                                                                                              | <p>Enables an extension to interact with Thunderbird's windows which can contain webpage tabs and also other window types like composer or address books that cannot contain webpage tabs. You can use this API to create, modify, and rearrange windows.</p><p><br><em>Permission info:</em></p><ul><li>The <code>tabs</code> permission is needed to populate the <code>url</code>, <code>favIconUrl</code> and <code>title</code> information in the <code>tabs</code> member of the <a href="https://webextension-api.thunderbird.net/en/mv2/windows.html#window"><code>Window</code></a> object.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                               |

You can find more information in the [Thunderbird WebExtensions API documentation ](https://webextension-api.thunderbird.net).

## Firefox WebExtension APIs

As Thunderbird is based on Firefox, many of its WebExtension APIs can be used in Thunderbird as well. The APIs listed in the following table are known to work with Thunderbird.

{% hint style="warning" %}
The following APIs link to their MDN description pages. Please be aware, that MDN is dedicated to web browsers (not limited to Firefox). Some information listed on MDN may not apply to Thunderbird and some API methods may not be supported. Each API page should include a compatibility chart and if that includes support for Firefox, it should work in Thunderbird as well.
{% endhint %}

| API                                                                                                           | Permission                                                                                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [browserSettings](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browserSettings) | browserSettings                                                                                                                                 | Enables an extension to modify certain global browser settings. Because these are global settings, it's possible for extensions to conflict. See the documentation for [BrowserSetting.set()](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/types/BrowserSetting/set) for details of how conflicts are handled.                                                                                                                                                                                                                                                                                                                                                                                                                    |
| [clipboard](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/clipboard)             | <p>clipboardWrite,</p><p>clipboardRead</p>                                                                                                      | <p>Enables an extension to copy items to the system clipboard. Currently the API only supports copying images, but it's intended to support copying text and HTML in the future.<br><br><em>Permission info:</em></p><ul><li>The <code>clipboardRead</code> permission is not used by this API but is listed here for completeness. To read from the clipboard, the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API">Clipboard Web API</a> has to be used and needs the <code>clipboardRead</code> permission.</li></ul>                                                                                                                                                                                                   |
| [contentScripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/contentScripts)   |                                                                                                                                                 | <p>Use this API to register content scripts to instruct the browser to insert the given content scripts into pages that match the URL patterns specified during registration. In Thunderbird, content scripts can only be used in web pages loaded into tabs.<br><br><em>Permission info:</em></p><ul><li>This API does not require a dedicated permission, but an appropriate <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions#Host_permissions">host permission</a> for any patterns it passes to <code>register()</code>.</li></ul>                                                                                                                                                                 |
| [cookies](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies)                 | cookies                                                                                                                                         | <p>Enables an extension to get and set cookies, and be notified when they change.<br><br><em>Permission info:</em></p><ul><li>In addition to the <code>cookies</code> permission, this API also needs a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions#Host_permissions">host permissions</a> for the sites whose cookies are to be accessed. See <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/cookies#Permissions">cookie Permissions</a>.</li></ul>                                                                                                                                                                                                                      |
| [dns](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/dns)                         | dns                                                                                                                                             | Enables an extension to resolve domain names.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| [downloads](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads)             | <p>downloads,</p><p>downloads.open</p>                                                                                                          | <p>Enables extensions to interact with the browser's download manager. You can use this API to <a href="https://thunderbird.topicbox.com/groups/addons/Td4979394bb2782c5-Me7445c7edfb7410f6d2973a2/save-as-the-email-send">save files to disk</a>.<br><br><em>Permission info:</em></p><ul><li>The <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads/open">downloads.open</a> permission is needed to open the saved file with the systems default application.</li></ul>                                                                                                                                                                                                                              |
| [extension](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/extension)             |                                                                                                                                                 | Utilities related to an extension. Gets URLs to resources packages with an extension. Gets the [Window](https://developer.mozilla.org/en-US/docs/Web/API/Window) object for some of the extension's pages. Get the values for various settings.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| [i18n](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n)                       |                                                                                                                                                 | Functions to internationalize an extension. It can be used to get localized strings from locale files packaged with an extension and to find out Thunderbird's current language.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| [identity](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/identity)               | identity                                                                                                                                        | Use the identity API to get an [OAuth2](https://oauth.net/2/) authorization code or access token, which an extension can then use to access user data from a service that supports OAuth2 access (such as Google or Facebook).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| [idle](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/idle)                       | idle                                                                                                                                            | Find out when the user's system is idle, locked, or active.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [privacy](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy)                 | privacy                                                                                                                                         | Access and modify various privacy-related settings.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| [management](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/management)           | management                                                                                                                                      | <p>Gets information about installed add-ons.<br><br><em>Permission info:</em></p><ul><li>The <code>management</code> permission is only needed to access information of other add-on.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| [notifications](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/notifications)     | notifications                                                                                                                                   | Display notifications to the user, using the underlying operating system's notification mechanism.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| [permissions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/permissions)         |                                                                                                                                                 | Enables extensions to request extra permissions at runtime, after they have been installed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [pkcs11](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/pkcs11)                   | pkcs11                                                                                                                                          | Enables an extension to enumerate [PKCS #11](https://en.wikipedia.org/wiki/PKCS_11) security modules and to make them accessible as sources of keys and certificates.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| [proxy](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy)                     | proxy                                                                                                                                           | <p>Enables an extension to proxy web requests. Use the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/onRequest"><code>proxy.onRequest</code></a> event listener to intercept web requests, and return an object that describes whether and how to proxy them.<br><br><em>Permission info:</em></p><ul><li>In addition to the <code>proxy</code> permission, this API also needs a <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions#Host_permissions">host permissions</a> for the URLs of intercepted requests.</li></ul>                                                                                                                                  |
| [runtime](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime)                 |                                                                                                                                                 | <p>This module provides information about the extension and the environment it's running in. It also provides messaging APIs to:</p><ul><li>communicate between different parts of the extension</li><li>communicate with other extensions</li><li>communicate with native applications</li></ul><p><em>Usage info:</em></p><ul><li>On macOS, Thunderbird looks for native messaging manifests in the per-user path <del><code>/Library/Mozilla/NativeMessagingHosts/</code></del> rather than <code>/Library/Application Support/Mozilla/NativeMessagingHosts/</code></li><li>On macOS, the global path for native messaging manifests is <code>/Library/Application Support/Mozilla/NativeMessagingHosts/</code> (the same as Firefox)</li></ul> |
| [storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage)                 | storage, [unlimitedStorage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#Unlimited_storage) | Enables extensions to store and retrieve data, and listen for changes to stored items.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| [userScripts](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts)         |                                                                                                                                                 | <p>Use this API to register user scripts, third-party scripts designed to manipulate webpages or provide new features. Registering a user script instructs the browser to attach the script to pages that match the URL patterns specified during registration. In Thunderbird, user scripts can only be used in web pages loaded into tabs.<br><br>This API offers similar capabilities to contentScripts but with features suited to handling third-party scripts.</p>                                                                                                                                                                                                                                                                           |
| [webNavigation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation)     | webNavigation                                                                                                                                   | Add event listeners for the various stages of a navigation. A navigation consists of a frame in the browser transitioning from one URL to another, usually (but not always) in response to a user action like clicking a link.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| [webRequest](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest)           | webRequest, webRequestBlocking                                                                                                                  | <p>Add event listeners for the various stages of making an HTTP request, which includes websocket requests on <code>ws\://</code> and <code>wss\://</code>. The event listener receives detailed information about the request and can modify or cancel the request.<br><br><em>Permission info:</em></p><ul><li>In addition to the <code>webRequest</code> permission, this API also needs the <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions#Host_permissions">host permission</a> for the requested host.</li><li>To use the blocking feature, the extension must also have the <code>webRequestBlocking</code> API permission.</li></ul>                                                         |

You can find more information about these APIs in the [MDN WebExtension API documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API).


# Introducing Experiments

This document aggregates information on topics that commonly arise when developing a new Experiment. For a complete documentation on each individual topic, refer to the linked articles.

{% hint style="info" %}
Since Experiments directly interact with Thunderbird's core functions, it is necessary to get used to the source code of Thunderbird itself. We gathered the most useful resources on the [Documentation & Resources](/add-ons/resources#legacy-extension-development) page.
{% endhint %}

{% hint style="info" %}
Thunderbird does contain a few useful features related to Experiments [whose documentation is no longer generated](https://bugzilla.mozilla.org/show_bug.cgi?id=1556460#c23). Especially if you're writing an Experiment with complex interactions between the WebExtension and your Experiment, it may be helpful to read the documentation blocks within ExtensionCommon.sys.mjs([git link](https://github.com/mozilla-firefox/firefox/blob/main/toolkit/components/extensions/ExtensionCommon.sys.mjs), [searchfox link](https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ExtensionCommon.sys.mjs)) and possibly other modules in the same source code folder.
{% endhint %}

{% hint style="info" %}
For reference, the parent implementations of all built-in APIs can be found in\
/comm/mail/components/extensions/parent([git link](https://github.com/thunderbird/thunderbird-desktop/blob/main/mail/components/extensions/parent), [searchfox link](https://searchfox.org/comm-central/source/mail/components/extensions/parent)).
{% endhint %}

{% hint style="danger" %}
Experiment APIs have full access to Thunderbird's core functions and can bypass the WebExtension permission system entirely. Including one or more Experiment APIs will therefore disable the individual permission prompt and instead prompt the user only for the [*Have full, unrestricted access to Thunderbird, and your computer*](https://support.mozilla.org/kb/permission-request-messages-thunderbird-extensions) permission.

The use of optional permissions is not supported for the same reason.
{% endhint %}

## Designing the API

When you encounter a feature that cannot get implemented with existing WebExtension APIs, it may be helpful to first read through the documentation of some built-in APIs, this document and some linked documentation to get a feeling about how existing APIs encapsulate common problems (i.e. listener registration through events, excessive use of `Promise`, ...) and what limitations your API will have to live with (i.e. potential complexity when passing functions or raw DOM elements).

Afterwards, think about (hypothetical or real) add-ons that would have similar needs and try to design an API that would be useful for a wide range of add-ons:

* A good API provides a single feature with as little complexity as possible (smaller is usually better).

  Example: an API adding both a menu item and a toolbar item for a single callback function might be useful, but it would be better to split it in two separate operations and combine them in the WebExtension.
* A good API can be used in a generic way and is not tied to your add-on's logic or functionality.

  Example: an API to add a menu item that will perform a fixed action might solve the specific issue you're working on, but it would be better to permit WebExtension code to dynamically specify the action to perform.
* A good API adheres to the conventions of WebExtension APIs.

  Example: a `registerListener` function might be a good idea in isolation, but it would be better to use events (that also simplifies the implementation!).

Depending on your time constraints, experience, and the concrete feature you're trying to write an API for, it is not necessarily reasonable to satisfy all three criteria, they are just a guideline to aim for.

## Building the Structure

Once you have a draft for your API, you can start to build the Experiment. Experiments consist of three parts, which are registered through [manifest.json](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/basics.html#webextensions-experiments):

1. A [*schema*](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/schema.html) describes the API that can be accessed by the WebExtension part of the add-on.
2. A *parent* implementation implements the API in Thunderbird's main process. All features that were available to a bootstrapped legacy extension can be used here.
3. A *child* implementation implements the API in the content process(es). This permits more complex interactions with WebExtension code and potentially improves performance, at the cost of not being able to access the main process.

A typical entry in the manifest.json file to register an Experiment:

```json
    "experiment_apis": {
        "LegacyPrefs": {
            "schema": "api/LegacyPrefs/schema.json",
            "parent": {
                "scopes": ["addon_parent"],
                "paths": [["LegacyPrefs"]],
                "script": "api/LegacyPrefs/implementation.js",
                "events": ["startup"]
            }
        }
    }
```

Either *parent* or *child* implementation may be omitted. Full examples for [a simple function with parent and child implementations](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/functions.html) and [events add-ons can listen for](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/events.html) are available in the Firefox source documentation.

{% hint style="info" %}
Technically speaking, Thunderbird is not actually using multiple processes (yet). However, the APIs were designed with multiple processes in mind and enforce at least some constraints as if the parts were in different processes.
{% endhint %}

In most cases, you can start by formalizing your API draft into a schema and adding a *parent* implementation using one of the linked articles as base. Add or switch to a *child* implementation if you have performance considerations or need to pass more complex data (see below).

{% hint style="info" %}
Check out the [Experiment Generator](https://darktrojan.github.io/generator/generator.html), which creates all the needed files.
{% endhint %}

{% hint style="info" %}
Check out the Example Experiments:

* [Activity Manager Experiment](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.activityManager)
* [Open SearchDialog Experiment](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.openSearchDialog)
* [Remove Attachments If Junk Experiment](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.removeAttachmentsIfJunk)
* [Restart Experiment](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.restart)
  {% endhint %}

{% hint style="warning" %}
Avoid declaring global variables in the implementation of your Experiment, as that can cause collisions with other Experiments loaded. Instead declare them as members of your API, or use a closure.
{% endhint %}

## Managing your Experiment's lifecycle

Experiments are loaded on demand. In order for an Experiment to get loaded, you thus need to either use the API from the WebExtension or register an implementation for the `startup` event (which calls the `onStartup()` method of that implementation's `ExtensionAPI` object once your add-on is loaded).

If your add-on is unloaded, Thunderbird will call the `onShutdown()` method of each *loaded* implementation's `ExtensionAPI` object. You should perform any cleanup tasks in that method, for example you **must** invalidate Thunderbird's startup cache whenever your add-on is unloaded for a non-shutdown reason:

```javascript
Services.obs.notifyObservers(null, "startupcache-invalidate", null);​
```

Failure to invalidate caches may cause parts of the add-on's Experiment APIs to be cached across updates of the Add-on, even if they are changed in the update. It is thus usually a good idea to execute the code above in the `onShutdown()` method of an Experiment that is always loaded.

In addition to your Experiment being loaded and unloaded as a whole, that Experiment's API will get loaded into each WebExtension context independently. As there can be multiple contexts at the same time, an Experiment may have multiple loaded APIs in parallel. You can perform context-specific loading tasks directly in `getAPI()`, and register context-specific unloading code through `context.callOnClose()`.

## Passing data to and from a WebExtension

### Parent implementations

In general, you can always pass simple data structures as function parameters and return values of an API. Thunderbird will automatically adapt them using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm), so you do not need to worry about them.

If your API function is supposed to return a value, it must be defined as `async` in the schema file.

### Child implementations

If you want to pass more complex data structures, especially functions or instances of custom classes, you can do so from a *child* implementation. There is a big caveat, though: the Experiment's implementation scripts are privileged relative to WebExtension scripts, which causes their scopes to be disjunct:

* **Accessing data that belongs to the WebExtension from an Experiment:** the code in the Experiment gains [xray vision](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Xray_vision), permitting it to directly access the chrome implementation of the given object. Usually, you don't need to worry about that and things work out just fine – but if they don't, you can opt-out via `Components.utils.waiveXrays()`.
* **Accessing data that belongs to an Experiment from a WebExtension:** it is not possible to directly access chrome-scoped objects from a WebExtension (but it can hold references on it).

  There are two options to work around that: either the Experiment clones the object into the unprivileged scope of the WebExtension or it directly constructs an unprivileged object.

  The first option usually boils down to invoking [Components.utils.cloneInto()](http://www.devdoc.net/web/developer.mozilla.org/en-US/docs/Components.utils.cloneInto.html) or a related function with `context.cloneScope` as target scope. A notable exception is returning data from async API functions or wrapping Promises via [context.wrapPromise()](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/reference.html#BaseContext.wrapPromise), which causes automatic cloning of the result (unless the result is wrapped into a `ExtensionCommon.NoCloneSpreadArgs`).

  The second option is using the constructors in `context.cloneScope` directly from the Experiment. Their results can be used from the WebExtension without further cloning.

{% hint style="info" %}
Common pitfall: `async` functions return a `Promise` in the scope of the function, so you need to wrap such functions before cloning them into a WebExtension scope.
{% endhint %}

## Structuring Experiment code

If your Experiment API is so complex that it does not reasonably fit into a single source file, you can load your own system modules with some additional boilerplate: you need to define a custom global URL. The [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment can be used to define custom global URLs. In this example, we are registering a custom `resource://` URL with the namespace `myaddon`:

```javascript
// Register a resource:// url with a custom namespace, which points to the
// "modules" folder. The namespace should be unique to avoid conflicts with
// other add-ons.
await messenger.LegacyHelper.registerGlobalUrls([
    ["resource", "myaddon", "modules/"],
]);
```

The file `TestModule.sys.mjs` in the `modules` folder will then be accessible via\
`resource://myaddon/TestModule.sys.mjs`.

Since system modules cannot be unloaded, we have to append a unique query, to make sure cached files are not re-used after an update. In the following example the `version` key from `manifest.json` is used. This allows us to use the same identifier throughout the entire add-on, but load the new version whenever the add-on has been updated:

```javascript
const { ExtensionParent } = ChromeUtils.importESModule(
    "resource://gre/modules/ExtensionParent.sys.mjs"
);
const extension = ExtensionParent.GlobalManager.getExtension(
    "<id-of-your-extensions>"
);
const query = extension.manifest.version;

// Load TestModule.sys.mjs.
var { TestModule } = ChromeUtils.importESModule(
  "resource://myaddon/TestModule.sys.mjs?" + query
)
```

## Accessing WebExtensions directly from an Experiment

Experiments should be standalone and have no dependencies to any WebExtension components. If possible, design your Experiments as if they were a feature of Thunderbird that does not know about your add-on, and keep the scope as narrow as possible. If you design your APIs correctly, you will not need to access the WebExtension part of your add-on.

In some rare cases, it may be possible that that is not feasible to follow this recommended practice for the current update cycle. In that case, it is possible to tear down the separation between the Experiment and the WebExtension. That can permit you to treat an Experiment as if it were a part of the WebExtension, except for the restrictions regarding passing data outlined above.

In a *child* implementation, you can directly access the real WebExtension scope via `Components.utils.waiveXrays(context.cloneScope)`.

Outside of a *child* implementation, or if you need the scope of the background page in particular, you can extract the background page's scope from an `extension` object:

```javascript
const webextScope = Array.from(extension.views).find(
    view => view.viewType === "background").xulBrowser.contentWindow
    .wrappedJSObject;
```

This hack only works because Thunderbird is internally not (yet) using multiple processes. Again, it is highly recommended to design your APIs in a way that these interactions are not necessary as it is likely that this technique will stop working in future versions of Thunderbird.


# A Guide to Themes

How to create themes for Thunderbird.

A theme is a Thunderbird add-on that allows to change the appearance of Thunderbird. This document covers the following topics:

* [Static Themes](#static-themes)
* [Dynamic Themes](#dynamic-themes)
* [Theming of message-compose-windows and message-display-tabs](#theming-message-compose-windows-and-message-display-tabs)
* [Theme Experiments](#theme-experiments)

## Static Themes

Static themes, like the name implies - are static and do not change. They have a set of colors or images that make up the theme, and this does not change. It typically consists of two files, zipped up with an .xpi extension just as any other add-on:

* manifest.json
* image.png or .jpg

### The `manifest.json` file

You must prepare a JSON manifest, named `manifest.json` just as with other add-ons. Below is a basic example:

```javascript
{
  "manifest_version":2,
  "browser_specific_settings":{
    "gecko":{
      "id":"mytheme@sample.themes.thunderbird.net",
      "strict_min_version":"60.0"
    }
  },
  "name":"Thunderbird ExampleTheme",
  "description":"Static theme version of an example Thunderbird theme.",
  "version":"1.0",
  "theme":{
    "images":{
      "theme_frame":"thunderbirdimage.jpg"
    },
    "colors":{
      "frame":"#000000",
      "tab_background_text":"#ffffff"
    }
  }
}
```

The following manifest keys define basic properties:

* `manifest_version`: A *mandatory key* defining the Manifest version used by the theme. Supported versions are `2` and `3` (since Thunderbird Beta 110). The Manifest defines the basic rules how a theme needs to be crafted and how it can interact with Thunderbird.
* `name` : A *mandatory key* to set the name of the theme.
* `version` : A *mandatory key* to define a number that denotes the version of the theme.
* `description` : A brief description of the theme.
* `author` : The name of a person or company representing the developer.

The `browser_specific_settings.gecko` manifest key defines the following properties:

* `strict_min_version`: Defines the lowest targeted version of Thunderbird.
* `strict_max_version`: Defines the highest targeted version of Thunderbird. It can be set to a specific version or a broader match to limit it to a branch (for example `102.*`). Usually not needed.
* `id`: The id serves as a unique identifier for the theme and is *mandatory* in order to upload a theme to ATN or to be able to install it from an XPI file.

{% hint style="danger" %}
Although not required by Firefox, Thunderbird requires an`id.`Thunderbird does not sign add-ons, and themes will not install without it.

Best practice is to use an "email-address-style" id (but not a real email address) on a domain you control, for example `name-of-your-addon@example.com`, if you own `example.com`. As the id of your theme cannot be changed once it is published, it is highly recommended to use a domain that you plan to keep for the forseeable future. If you don't have a domain to use, feel free to use:\
`<atn-user-name>.<add-on-name>@addons.thunderbird.net`\\

Alternatively, you may use an UUID enclosed in curly braces, for example:\
`{e4aa2097-8ee9-49a4-9ec7-c633b1e8dfda}`
{% endhint %}

### Additional theme properties

Although the above theme will work as-is, there are other properties which can be added. All currently supported properties are listed in the [ThemeType](https://webextension-api.thunderbird.net/en/mv2/theme.html#theme-themetype) definition in our WebExtension API documentation.

### Icons

You can add an icon for your theme, like other types of add-ons, with the following code:

```javascript
  "icons": {
    "16": "icon.png"
  },
```

Don't forget to put the `icon.png` file in your add-on as well.

### A complete example

Here is a `manifest.json` from a theme that uses all the above features, thanks to Paenglab:

```javascript
{
  "manifest_version": 2,
  "name": "Nuvola WebExtension theme",
  "version": "1.1",
  "browser_specific_settings": {
    "gecko": {
      "id": "nuvola@paenglab.ch",
      "strict_min_version": "60.0"
    }
  },
  "description": "Light theme with some gradients.",
  "icons": {
    "16": "icon.png"
  },
  "theme": {
    "colors": {
      "frame": "#e7e8ec",
      "tab_text": "#000",
      "tab_line": "#1f9afd",
      "tab_loading": "#1f9afd",
      "tab_background_text": "#000",
      "bookmark_text": "#333",
      "toolbar_field": "#f2f2f2",
      "toolbar_field_text": "#444",
      "toolbar_field_highlight": "#1f9afd",
      "toolbar_field_highlight_text": "#fff",
      "toolbar_field_border": "#999",
      "toolbar_field_focus": "#f2f2f2",
      "toolbar_field_text_focus": "#000",
      "toolbar_field_border_focus": "#1f9afd",
      "toolbar_top_separator": "#aaa",
      "toolbar_bottom_separator": "#888",
      "toolbar_vertical_separator": "#888",
      "sidebar": "#fbfbfb",
      "sidebar_text": "#000",
      "sidebar_highlight": "rgba(11,113,220,.6)",
      "sidebar_highlight_text": "#fff",
      "sidebar_border": "#999",
      "popup": "#e6e8ef",
      "popup_text": "#000",
      "popup_border": "#666",
      "popup_highlight": "#1f9afd",
      "popup_highlight_text": "#fff"
    },
    "images": {
      "theme_frame": "background.png"
    }
  }
}
```

## Dynamic Themes

Dynamic themes are actually normal extensions, that use the [update()](https://webextension-api.thunderbird.net/en/mv2/theme.html#update-windowid-details) method of the [theme](https://webextension-api.thunderbird.net/en/mv2/theme.html#theme) API instead of a static [theme](https://webextension-api.thunderbird.net/en/mv2/theme.html) manifest key. They can set the same theme properties like static themes, but they can change them dynamically. For instance the [Night and Day theme](https://addons.thunderbird.net/addon/night-and-day-dynamic/) is a dynamic theme that changes the theme colors based on the time of day.

{% content-ref url="/pages/-MJ6xtDSW60GvgKtAgsG" %}
[A Guide to Extensions](/add-ons/mailextensions)
{% endcontent-ref %}

## Theming message-compose-windows and message-display-tabs

The built-in theming properties do not modify the message-compose-windows and the message-display-tabs. These can be manipulated by injecting CSS files using the following WebExtension API methods:

* [messageDisplayScripts.register()](https://webextension-api.thunderbird.net/en/mv2/messageDisplayScripts.html#register-messagedisplayscriptoptions)
* [composeScripts.register()](https://webextension-api.thunderbird.net/en/mv2/composeScripts.html#register-composescriptoptions)

To inject the file `compose.css` into the message-compose-window, register it in your background script as follows:

```javascript
messenger.composeScripts.register({
  css : [ { file: "compose.css"} ]
});
```

## Theme Experiments

A theme experiment allows modifying the user interface of Thunderbird beyond what is currently possible using the built-in color, image and property keys of the [theme](https://webextension-api.thunderbird.net/en/mv2/theme.html) API. These experiments are a precursor to proposing new theme features for inclusion in Thunderbird.

Experimentation is done by exposing already existing internal CSS variables (e.g. `--arrowpanel-dimmed`) to the [theme](https://webextension-api.thunderbird.net/en/mv2/theme.html) API and by loading additional stylesheets to define new CSS variables, extending the theme-able areas of Thunderbird.

Use the [browser toolbox](https://developer.mozilla.org/en-US/docs/Tools/Browser_Toolbox) to discover CSS selectors for Thunderbird UI elements or internal Thunderbird CSS variables.

{% hint style="info" %}
Further information regarding theme experiments can be found in our [WebExtension API documentation of the theme API](https://webextension-api.thunderbird.net/en/mv2/theme.html).
{% endhint %}

{% hint style="info" %}
Our example repository includes an [add-on using a theme experiment](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/theme_experiment) to change the color of the chat icon.
{% endhint %}


# Developer Community

Learn how to get in touch with the Thunderbird add-on developer community.

|                                                                                                                          | Description                                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Add-On Developer Topicbox](https://thunderbird.topicbox.com/groups/addons)                                              | A communication platform / mailing list for Thunderbird add-on developers and aspiring add-on developers to ask questions and share knowledge.                                                                      |
| [Add-On Developer Chat](https://matrix.to/#/#tb-addon-developers:mozilla.org?web-instance\[element.io]=chat.mozilla.org) | A matrix chat room for Thunderbird add-on developers. More information on the Mozilla matrix server and how to establish a connection apart from the web app, can be found [here](https://wiki.mozilla.org/Matrix). |
| [Add-On Reviewer Chat](https://matrix.to/#/#tb-addon-reviewers:mozilla.org?web-instance\[element.io]=chat.mozilla.org)   | A matrix chat room to get in touch with add-on reviewers.                                                                                                                                                           |
| [Add-on Developer Zoom Meeting](https://us02web.zoom.us/j/84871054746?pwd=bTlvTEdEbG0yV29rTU5xQ1NRdnBlUT09)              | <p>An almost weekly meeting where add-on developers can discuss technical difficulties or general add-on related issues.</p><p>Thursday 21:00 - 22:30 CET (Europe/Berlin)</p>                                       |


# Documentation & Resources

A collection of the most important developer resources outside of developer.thunderbird.net.

## MailExtension Development

A few helpful resources relevant for developing add-ons for Thunderbird.

|                                                                                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [AI skill file](https://github.com/thunderbird/webext-support/tree/master/ai)                                                | Teach your AI assistant how to code Thunderbird WebExtensions.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| [addons.thunderbird.net](https://addons.thunderbird.net)                                                                     | The official Thunderbird add-on repository.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| [List of Thunderbird app versions](https://addons.thunderbird.net/pages/appversions/)                                        | A list of all supported values, which can be used as `strict_min_version` and `strict_max_version` values in add-on manifest files.                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| [Debugging Guide](https://extensionworkshop.com/documentation/develop/debugging/)                                            | A guide from the extension workshop with the most recent information on debugging add-ons. It is written for Mozilla Firefox but applies for Thunderbird as well.                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Supercharge your Thunderbird extension debugging](https://arndissler.net/supercharge-your-thunderbird-extension-debugging/) | A guide how to use Visual Studio Code to debug Thunderbird extension.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| WebExtension API Documentation for Thunderbird                                                                               | <p>Documentation of all WebExtension APIs supported by Thunderbird.</p><ul><li>Thunderbird Release (<a href="https://webextension-api.thunderbird.net/en/mv3/">Manifest V3</a>, <a href="https://webextension-api.thunderbird.net/en/mv2/">Manifest V2</a>)</li><li>Thunderbird ESR (<a href="https://webextension-api.thunderbird.net/en/esr-mv3/">Manifest V3</a>, <a href="https://webextension-api.thunderbird.net/en/esr-mv2/">Manifest V2</a>)</li></ul>                                                                                                                                            |
| [Thunderbird WebExtension Examples](https://github.com/thunderbird/webext-examples)                                          | A collection of explanatory WebExtension examples for Thunderbird.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [Firefox WebExtension Examples](https://github.com/mdn/webextensions-examples)                                               | A collection of WebExtension examples for Firefox and other browsers. They probably won’t work directly in Thunderbird, but they may provide hints on how to use some of the WebExtension APIs that Thunderbird inherited from Firefox.                                                                                                                                                                                                                                                                                                                                                                   |
| [Thunderbird WebExtension Support Repository](https://github.com/thunderbird/webext-support)                                 | The WebExtension support repository provides additional tools, scripts, custom elements, Experiment APIs and other resources, to simplify the development of WebExtensions for Thunderbird.                                                                                                                                                                                                                                                                                                                                                                                                               |
| [Bugzilla](https://bugzilla.mozilla.org)                                                                                     | <p>Central bug tracking for Mozilla projects. Some useful pages related to Thunderbird:</p><ul><li><a href="https://bugzilla.mozilla.org/buglist.cgi?product=Thunderbird&#x26;component=Add-Ons%3A%20Extensions%20API&#x26;resolution=---&#x26;list_id=15187727">List of bugs related to Extension APIs</a></li><li><a href="https://bugzilla.mozilla.org/describecomponents.cgi?product=Thunderbird">List of all Thunderbird Components</a></li></ul>                                                                                                                                                    |
| [Mozilla Developer Documentation (MDN)](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)              | <p>The main documentation for Mozilla developers. As Thunderbird is based on the Mozilla platform, some Mozilla documentation is valid for Thunderbird as well. Useful MDN pages on WebExtension are:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions">Introduction to WebExtensions</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json">manifest.json</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions">permissions</a></li></ul> |

## Experiment Development

Useful resources for converting legacy extensions or for creating Experiments.

|                                                                                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Searchfox](https://searchfox.org/)                                                 | An online viewer to search the Firefox code base ([*mozilla-central*](https://searchfox.org/mozilla-central/source/)) and the Thunderbird code base ([*comm-central*](https://searchfox.org/comm-central/source/)).                                                                                                                                                                                                                                                                                                                                                                                                          |
| [Firefox Source Tree documentation](https://firefox-source-docs.mozilla.org/)       | The current Firefox code documentation, which might be needed when converting legacy extensions.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| [Thunderbird Source Tree documentation](/thunderbird-development/codebase-overview) | The current Thunderbird code documentation, in addition to comments in [idl files on comm-central](https://searchfox.org/comm-central/search?q=\&path=*.idl). This is still work in progress.                                                                                                                                                                                                                                                                                                                                                                                                                                |
| [Archived XUL and XPCOM Documentation](https://udn.realityripple.com/docs/Mozilla)  | <p>The archived Mozilla documentation includes information about internal components and functions used by legacy extensions. Please be aware, that those pages are not maintained and are potentially outdated. Some useful direct links:</p><ul><li><a href="https://udn.realityripple.com/docs/Mozilla/JavaScript_code_modules">Archived JavaScript Code Modules</a></li><li><a href="https://udn.realityripple.com/docs/Archive/Mozilla/XUL">Archived XUL Documentation</a></li><li><a href="https://udn.realityripple.com/docs/Mozilla/Tech/XPCOM/Reference/Interface">Archived XPCOM Interface Reference</a></li></ul> |
|                                                                                     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |


# Tips and Tricks

Tips and tricks for successful Thunderbird add-on development.

## Debugging

Some general tips to speed up your development workflow:

* To debug code running in the browser context (e.g.: your Experiment APIs) you must use the global browser console (Ctrl+Shift+J) or developer toolbox (Ctrl+Shift+I).<br>
* To debug code running in a content page of your extension (e.g.: your background script), you need to select "debug add-ons" from the gear icon in the add-on tab and then inspect your add-on.\
  \
  **Hint:** Alternatively, you can enable content messages in the global browser console or in the developer toolbox as well. This will also show console output from popus (e.g. from browser\_action):

![](/files/-MCpm9bkD5YzWmRkd_Jx)

* The add-on debugging tools accessible through the add-on page's gear icon permit to directly install add-ons without packaging them. Using that option permits to reload the add-on without restarting Thunderbird.

There is also a [debug guide from the extension workshop](https://extensionworkshop.com/documentation/develop/debugging/) with more details and the most recent information on debugging. It is written for Mozilla Firefox but applies for Thunderbird as well.

## Experiments: Forcing modified JavaScript to reload

One of the stumbling blocks that legacy add-on developers frequently encounter is Thunderbird's caching mechanisms, which cache JavaScript files for performance reasons so that they don't have to be re-read from their source and parsed or compiled repeatedly. If you're developing an Experiment, you may find that Thunderbird insists on continuing to use old versions even after you've changed them and restarted.

### `-purgecaches` command-line option

DMO [claims](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#-purgecaches) that specifying the `-purgecaches` command line option when launching Thunderbird will force it to purge the JavaScript cache.

### Invalidate caches programmatically

Executing this JavaScript code inside Thunderbird will cause the XUL cache to be invalidated the next time it restarts:

```javascript
Services.obs.notifyObservers(null, "startupcache-invalidate", null);
```

This is described in more detail in the section [Introducing Experiments](/add-ons/mailextensions/experiments).


# Add-on Update Guides


# Updates for Thunderbird 140

{% hint style="warning" %}
Individual change lists are no longer published for each release.
{% endhint %}

Modern WebExtensions are generally stable and do not require ongoing updates to remain compatible. For Experiment add-ons, the number of potential changes in Thunderbird’s core code has grown so large that tracking and documenting every incompatibility is not an effective use of resources.

It has proven more effective to discuss compatibility issues during the [**weekly add-on developer call** or on the **add-on developer mailing list**](/add-ons/community). These forums allow developers to raise questions, share experiences, and collaborate on solutions with the community.


# Update for Thunderbird 128

Required steps to update add-ons for Thunderbird 128.

This section covers the required update steps for add-ons which are already compatible with Thunderbird 115 and need to be made compatible with Thunderbird 128.

## Changes in Manifest V2 WebExtension APIs

Our WebExtension APIs are meant to be stable, but we did have to introduce a backward incompatible change:

* Introduction of the ***messagesUpdate*** permission, required to use [messages.update()](https://webextension-api.thunderbird.net/en/esr-mv2/messages.html#messages-update).

## Introducing Manifest V3

Thunderbird 128 is the first release to officially support [Manifest Version 3](/add-ons/whats-new/manifest-v3). A guide to convert extensions from Manifest V2 to Manifest V3 can be found in our WebExtension API documentation:

* [Convert extensions to Manifest V3](https://webextension-api.thunderbird.net/en/mv3/guides/manifestV3.html)

## Changes in Thunderbird Core

Thunderbird WebExtensions can still run legacy code inside [Experiments](/add-ons/mailextensions#experiment-apis). Such legacy code has to be adjusted to changes made in Thunderbird Core. All known changes are listed below.

If you have encountered a change which is not yet listed there, please [contact us](/add-ons/community), so we can update the list.

### **ESMification**

The Thunderbird team has finished the conversion of all its JSM files and now only uses ES6 modules instead. There is a compatibility layer, which still maps requests for the old `*.jsm` files to the new `*.sys.mjs` files. It is however recommended to use the new files now already. The new files are either loaded via `ChromeUtils.importESModule()`:

```javascript
const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
```

or via `ChromeUtils.defineESModuleGetters()`:

```javascript
ChromeUtils.defineESModuleGetters(this, {
  AttachmentInfo: "resource:///modules/AttachmentInfo.sys.mjs",
  MailUtils: "resource:///modules/MailUtils.sys.mjs",
  PluralForm: "resource:///modules/PluralForm.sys.mjs",
});
```

### nsIMsgCopyServiceListener

Changed in Thunderbird 126 (Bug 1887047).

* `OnProgress()` -> `onProgress()`
* `OnStartCopy()` -> `onStartCopy()`
* `OnStopCopy()` -> `onStopCopy()`
* `GetMessageId()` -> `getMessageId()`
* `SetMessageKey()` -> `setMessageKey()`

### thread-pane-columns.mjs

This file has been renamed to `ThreadPaneColumns.mjs` in Thunderbird 128 (Bug 1890731).

### nsIFilePicker::init()

Changed in Thunderbird 125 (Bug 1878401). The type of the first parameter was changed from `DOMWindow` to `BrowsingContext`. It will fail when a `DOMWindow` is passed in. Every `DOMWindow` has a `browsingContext` getter:

`window.browsingContext`

### FileUtils.getFile()

The `getFile()` method has been removed in Thunderbird 116 (Bug 1747467). Use `FileUtils.File()` instead. Example:

```javascript
let file = FileUtils.getFile("TmpD", [fileName]);
```

can be replaced by

```javascript
let file = new FileUtils.File(PathUtils.join(PathUtils.tempDir, fileName));
```

### MailServices.accounts.FindAccountForServer()

The `FindAccountForServer()` method has been renamed to `findAccountForServer()` in Thunderbird 121 (Bug 1865068).

### MozElements.NotificationBox.appendNotification()

The implementation of this method has become `async` in Thunderbird 123. The [NotificationBox Experiment](https://github.com/thunderbird/webext-experiments/tree/main/NotificationBox) has been adjusted accordingly.

### Services.jsm

The file `Services.jsm` and its compatibility layer (added in Thunderbird 115) have been removed, and loading it will now cause an error in Thunderbird 128. It is safe to simply remove all code which was used to load the module in Thunderbird 115 and later.

### Services.logins.addLogin()

The `addLogin()` method has been replaced by the async method `Services.logins.addLoginAsync()`.

### XPCOMUtils.defineLazyGetter()

This method has been a thin wrapper for `ChromeUtils.defineLazyGetter()` since Thunderbird 112 (Bug 1805288). Its usage has been purged from core code and it may stop working anytime.


# Update for Thunderbird 115

Required steps to update add-ons for Thunderbird 115.

This section covers the required update steps for add-ons which are already compatible with Thunderbird 102 and need to be made compatible with Thunderbird 115.

## Changes in Thunderbird Core

Thunderbird WebExtensions can still run legacy code inside [Experiments](/add-ons/mailextensions#experiment-apis). Such legacy code has to be adjusted to changes made in Thunderbird Core. All known changes are listed in the following document:

* [Changes in Thunderbird 103-115](/add-ons/updating/tb115/adapt-to-changes-in-thunderbird-103-115)

If you have encountered a change which is not yet listed there, please [contact us](/add-ons/community), so we can update the list.

## Changes in Shared Experiments

If you are using any of the shared Experiments, you probably do not have to update them on your own. Check if an updated version is already available:

* [Shared Experiments on DTN](/add-ons/mailextensions#sharing-experiment-apis)


# Adapt to Changes in Thunderbird 103-115

This document tries to cover the internal changes that may be needed to make Experiment add-ons compatible with Thunderbird Supernova. If you find changes which are not yet listed on this page, you can ask for help and advice in one of our [communication channels](https://developer.thunderbird.net/add-ons/community).

## Rework of Thunderbird's main messenger window

### Changed tab implementation

Each `mail3PaneTab` and `mailMessageTab` in Thunderbird's main messenger window is now loaded into its own browser element, instead of sharing and updating a single browser.

**A general and very helpful introduction to the new front end can be found in its** [**official documentation**](https://developer.thunderbird.net/thunderbird-development/codebase-overview/mail-front-end)**.**

The `mail3PaneWindow` (`about:3pane`) can now be accessed as follows:

1. `window.gTabmail.currentAbout3Pane`
2. `window.gTabmail.currentTabInfo.chromeBrowser.contentWindow`
3. `window.gTabmail.tabInfo[0].chromeBrowser.contentWindow`
4. `window.gTabmail.tabInfo.find(`\
   `t => t.mode.name == "mail3PaneTab"`\
   `).chromeBrowser.contentWindow`

Options *#1 & #2* will only work if the current active tab is a `mail3PaneTab`. Option *#3* assumes the first tab is always a `mail3PaneTab`.\
\
The `mailMessageWindow` (`about:message`) can be accessed similarly:

1. `window.gTabmail.currentAboutMessage`
2. `window.gTabmail.currentTabInfo.chromeBrowser.contentWindow`
3. `mail3PaneWindow.messageBrowser.contentWindow`
4. `window.messageBrowser.contentWindow`

Option *#1* will only work if the current tab is a `mail3PaneTab` or a `mailMessageTab`. Option *#2* will return the `mailMessageWindow`, if the current tab is a `mailMessageTab` (it will return the `mail3PaneWindow`, if the current tab is a `mail3PaneTab`). Option *#3* will return the nested message browser of a `mail3PaneTab` through its `mail3PaneWindow` as defined in the previous section. Option *#4* will return the message browser of a message window.

#### Moved global objects

Some of the global objects defined in Thunderbird's main messenger window have been moved into the `mail3PaneWindow` (`about:3pane`) and/or the `mailMessageWindow` (`about:message`). Some objects have been removed.

* `gDBView`: Available in `mail3PaneWindow` and in `mailMessageWindow`.
* `gFolderDisplay`: Removed. Find displayed folder via `mail3PaneWindow.gFolder`.
* `gMessageDisplay`: Removed. Find displayed message via `mailMessageWindow.gMessage` or `mailMessageWindow.gMessageURI`.

Useful functions, methods and objects which have been moved from elsewhere:

* `mail3PaneWindow.displayFolder(folderURI)`
* `mail3PaneWindow.messagePane.displayMessage(messageURI)`
* `mail3PaneWindow.messagePane.displayMessages(messageURIs)`
* `mail3PaneWindow.messagePane.displayWebPage(url)`
* `mail3PaneWindow.folderPane.*`
* `mail3PaneWindow.folderTree.*`
* `mail3PaneWindow.threadPane.*`
* `mail3PaneWindow.threadTree.*`
* `mailMessageWindow.currentHeaderData`
* `mailMessageWindow.currentAttachments`

This [topicbox post](https://thunderbird.topicbox.com/groups/addons/Te5f62259df8c0c74-M7ea49d57cdc60d61a620c6b0) holds instructions on how to find other moved objects and functions.

#### Using WebExtension APIs

It is recommended to leverage WebExtension APIs as much a possible. Instead of adjusting to core changes, the following WebExtension APIs can be helpful:

* Use the [browserAction API](https://webextension-api.thunderbird.net/en/mv2/browserAction.html) to add buttons to Thunderbird's unified toolbar
* Use the [menus API](https://webextension-api.thunderbird.net/en/mv2/menus.html) to add entries to Thunderbird's context menu (for example in the folder pane or in the thread pane)
* Use the [commands API](https://webextension-api.thunderbird.net/en/mv2/commands.html) to register keyboard shortcuts. Additional benefit: all WebExtension shortcuts can be adjusted by the user in the Add-on Manager according to their needs.
* Use the [mailTabs API](https://webextension-api.thunderbird.net/en/mv2/mailTabs.html) to interact with the mail tab.

#### Using shared Experiments

There may already be a shared Experiment, which could help with add-on updates (or which could give helpful hints):

* community Experiments listed on [DTN](https://developer.thunderbird.net/add-ons/mailextensions#sharing-experiment-apis)
* Experiments from the [webext-support](https://github.com/thunderbird/webext-support/tree/master/experiments) repository
* Experiments from the [webext-experiments](https://github.com/thunderbird/webext-experiments) repository (scheduled to be shipped with Thunderbird soon)

### Unified Toolbar

The mail toolbar has been replaced by the unified toolbar. Adding your own buttons will become difficult, because the unified toolbar tends to remove unknown objects. Instead, use the [browserAction API](https://webextension-api.thunderbird.net/en/mv2/browserAction.html) to add buttons.

### XUL Tree replacement

The `folderTree` and `threadTree` in `about:3pane` (the `mail3PaneTab`) no longer use the deprecated XUL `tree` elements, but have been replaced by HTML `lists` and HTML `tables`.

### XUL flexbox changes

Mozilla continued to remove XUL in favour of standard HTML5/CSS. The most relevant changes are related to the XUL flexbox. A very helpful read is [this blogpost from the responsible developer](https://crisal.io/words/2023/03/30/xul-layout-is-gone.html).

Known attributes which have to be replaced:

* `height`: replace with [CSS height property](https://developer.mozilla.org/en-US/docs/Web/CSS/height)
* `width`: replace with [CSS width property](https://developer.mozilla.org/en-US/docs/Web/CSS/width)
* `flex`: replace with [CSS flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) (a very helpful tutorial is available at [css-tricks.com](https://css-tricks.com/snippets/css/a-guide-to-flexbox/))

**Important:** If add-ons still create old-fashioned XUL dialogues and load \*`.xhtml` files, it is not recommended to invest time into fixing them. It is more efficient to re-create them as pure `*.html` files. They can be opened using the [tabs API](https://webextension-api.thunderbird.net/en/mv2/tabs.html) or the [windows API](https://webextension-api.thunderbird.net/en/mv2/windows.html).

## Removed JSM files

### Services.jsm

Removed in Thunderbird 103. The [service object is now globally available](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/basics.html#globals-available-in-the-api-scripts-global) in API implementation scripts. If needed in self-created JSMs, it can be accessed as follows:

```javascript
const Services = globalThis.Services;
```

For a backward compatible solution, use

```javascript
const Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
```

### osfile.jsm

Removed in Thunderbird 115. Can be replaced as follows:

* `OS.Constants.Sys.Name` -> `Services.appinfo.OS`
* `OS.Constants.Path.profileDir` -> `PathUtils.profileDir`
* `OS.Constants.Path.tmpDir`-> `PathUtils.tempDir`
* `OS.File.*` -> [IOUtils.\*](https://searchfox.org/comm-central/source/mozilla/dom/chrome-webidl/IOUtils.webidl)
* `OS.Path.*` -> [PathUtils.\*](https://searchfox.org/comm-central/source/mozilla/dom/chrome-webidl/PathUtils.webidl)

## Changed API

### calITimezoneService.timezoneIds

In Thunderbird 106, this has been changed from an enumerator to a simple array.

### callTimezoneService.aliasIds

Removed in Thunderbird 106.

### nsIMsgDatabase.ContainsKey()

Renamed in Thunderbird 108 to `containsKey()`. Example `msgHdr.folder.msgDatabase.containsKey()`.

## Process Restrictions

Content processes, such as the extension process executing "child" experiment code, are subject to additional restrictions compared to Thunderbird 102. Known limitations include:

* Raw TCP socket operations never complete, even if timeouts are set up. It is likely that other networking primitives are affected in a similar way.

In consequence, you might need to move some functionality from "child" experiment code to the "parent" context. One way to achieve this is to implement the necessary functionality as a regular asynchronous API method in the "parent" experiment (without extending the API schema), then using `await context.childManager.callParentAsyncFunction("your_api_name.some_function", [arguments, passed, to, some_function])` to call it from the child experiment. Note that function arguments passed in between processes are subject to the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).

## Removed CSS features

### -moz-image-region

The CSS feature `-moz-image-region` has been removed. It is no longer possible to specify a button icon or a `list-style-image` as a region from a larger image. Add-on developers have to resort to individual images.


# Update for Thunderbird 102

Required steps to update add-ons for Thunderbird 102.

This section covers the required update steps for add-ons which are already compatible with Thunderbird 91 and need to be made compatible with Thunderbird 102.

## Changes in Thunderbird Core

Thunderbird WebExtensions can still run legacy code inside [Experiments](/add-ons/mailextensions#experiment-apis). Such legacy code has to be adjusted to changes made in Thunderbird Core. All known changes are listed in the following document:

* [Changes in Thunderbird 92-102](/add-ons/updating/tb102/adapt-to-changes-in-thunderbird-92-102)

If you have encountered a change which is not yet listed there, please [contact us](/add-ons/community), so we can update the list.

## Changes in Shared Experiments

If you are using any of the shared Experiments, you probably do not have to update them on your own. Check if an updated version is already available:

* [Shared Experiments on DTN](/add-ons/mailextensions#sharing-experiment-apis)


# Adapt to Changes in Thunderbird 92-102

This document tries to cover all the internal changes that may be needed to make Experiment add-ons compatible with Thunderbird 102. If you find changes which are not yet listed on this page, you can ask for help and advice in one of our [communication channels](https://developer.thunderbird.net/add-ons/community).

## String comparison against the version string

Some developers use the version string to determine which function to call in add-ons which try to be backward compatible. For example:

```javascript
if (xulAppInfo.version >= "91.0") {
  restartButton.addEventListener(
    "command", 
    () => MailUtils.restartApplication()
  );
} else {
  restartButton.addEventListener(
    "command", 
    () => BrowserUtils.restartApplication()
  );
}
```

This fails for Thunderbird 100 and newer, because this is a string comparison and not an integer comparison. A function to get the integer values could look like so:

```javascript
function getThunderbirdVersion() {
    let parts = Services.appinfo.version.split(".");
    return {
        major: parseInt(parts[0]),
        minor: parseInt(parts[1]),
        revision: parts.length > 2 ? parseInt(parts[2]) : 0,
    }
}
```

And then just use `getThunderbirdVersion().major >= 91` to check the version.

In this specific case, one could also use feature detection itself:

```javascript
if ("restartApplication" in MailUtils)
  restartButton.addEventListener(
    "command", 
    () => MailUtils.restartApplication()
  );
} else {
  restartButton.addEventListener(
    "command", 
    () => BrowserUtils.restartApplication()
  );
}
```

## Changed DOM Elements

### composer

The id of the editor element in the composer has been renamed from [content-frame](https://searchfox.org/comm-esr91/source/mail/components/compose/content/messengercompose.xhtml#2478) to [messageEditor](https://searchfox.org/comm-central/rev/36c713a3d0bf0d0ca2208fdf74f8a133d4ed7edd/mail/components/compose/content/messengercompose.xhtml#2460).

### message window

The id of the additional header area in the message display window has been renamed from [expandedHeaders2](https://searchfox.org/comm-esr91/source/mail/base/content/msgHdrView.inc.xhtml#268) to [extraHeadersArea](https://searchfox.org/comm-central/rev/7e775d6f5ba8334b968619bed2082cd967615a75/mail/base/content/msgHdrView.inc.xhtml#408). The element has also been converted from a `table` to a `div`.

The class `headerName` used for the styling header entries has been renamed to `message-header-label`. The class`message-header-row` has been added, styling the entire row.

The class `msgHeaderView-button` used to style toolbar buttons has been renamed to `message-header-view-button`.

### \<stringbundle id="bundle\_smime\_read\_info">

This `stringbundle` element has been removed from `messageWindow.xhtml` and `messenger.xhtml`. To access the strings, you have to load the bundle directly:

```javascript
let bundle = Services.strings.createBundle(
    "chrome://messenger-smime/locale/msgSecurityInfo.properties"
);
```

The method to retrieve strings from the created bundle is slightly different, instead of `getString()` use `GetStringFromName()` .

## Changed API

### calICalendar.\*

Since Thunderbird 96, [many calendar functions return Promises](https://searchfox.org/comm-central/source/calendar/base/public/calICalendar.idl). This includes:

* `getItem()`
* `addItem()`
* `adoptItem()`
* `modifyItem()`
* `deleteItem()`
* `deleteOfflineItem()`
* `getItemOfflineFlag()`

The former methods to promisify these functions have been removed together with `calAsyncUtils.jsm`. Additionally, the `getItem()` method will return the item directly instead of an array with the item. Replace

```javascript
let pcal = cal.async.promisifyCalendar(calendar.wrappedJSObject);
let item = await pcal.getItem(itemId)[0];
```

by

```javascript
let item = await calendar.getItem(itemId);
```

The `calIOperationListener` and `calIOperation` interfaces are still used in various places but the general direction is to remove them eventually after 102 in favor of Promises and ReadableStreams. If you have code that uses them internally, please update where feasible.

If your code is synchronous, you will have to rework it to make use of asynchronous functions. Feel free to reach out for further help on this through our [community channels](/add-ons/community).

#### Provider changes in calICalendar.addItem/adoptItem/modifyItem

Calendar providers need to change above mentioned functions to be asynchronous. Calling the listeners is no longer necessary. Instead, you should return the item from the `addItem`/`adoptItem`/`modifyItem` functions and make sure to throw an error in case of failure.

For providers with offline support, you need to call listeners set by the cache layer using the `_cachedAdoptItemCallback` and `_cachedModifyItemCallback` properties on your provider class. This is an unfortunate hack needed to maintain the order the `onAddItem` event is fired by `calCachedCalendar`. These listeners need to be called just before returning.

> Note: It is important to store the callbacks before executing any async work to avoid issues when the same operation is run in concurrently. See the example below.

```javascript
class CalendarProvider extends cal.provider.BaseClass {
  _cachedAdoptItemCallback = null;
  _cachedModifyItemCallback = null;

  async addItem(item) {
    return this.adoptItem(item.clone());
  }

  async adoptItem(item) {
    let adoptCallback = this._cachedAdoptItemCallback; // Store callback locally to execute later.
    let createdItem = item; // Create your item here, this may be asynchronous.

    if (adoptCallback) {
      await adoptCallback(
        this.superCalendar,
        Cr.NS_OK,
        Ci.calIOperationListener.ADD,
        createdItem.id,
        createdItem
      );
    }
    return item;
  }

  async modifyItem(newItem, oldItem) {
    let modifyCallback = this._cachedModifyItemCallback;
    let modifiedItem = newItem; // Modify your item here, this may be asynchronous.

    if (modifyCallback) {
      await modifyCallback(
        this.superCalendar,
        Cr.NS_OK,
        Ci.calIOperationListener.MODIFY,
        modifiedItem.id,
        modifiedItem
      );
    }
    return modifiedItem;
  }
}
```

#### calICalendar.getItems()

Since Thunderbird 96, `calICalendar.getItems()` returns a `ReadableStream`. Replace

```javascript
let operationListener = {
    QueryInterface: ChromeUtils.generateQI(["calIOperationListener"]),
    onOperationComplete(calendar, status, operationType, id, detail) {
        // Completed
    },
    onGetResult(calendar, status, itemType, detail, itemsArg) {
        for (let item of itemsArg) {
            // Do something with item.
        }
    },
};
calendar.getItems(itemFilter, count, rangeStart, rangeEnd, operationListener);
```

by

```javascript
let iterator = cal.iterate.streamValues(
    calendar.getItems(itemFilter, count, rangeStart, rangeEnd)
);

for await (let items of iterator) {
    for (let item of items) {
        // Do something with item.
    }
}
```

If you are implementing a provider you will need to adapt your code to return a `ReadableStream`. For cached providers, ensure you are returning the result from the offline cache:

```javascript
getItems(aFilter, aCount, aRangeStart, aRangeEnd) {
    // Previous code may have been missing the return, and used aListener
    return this.mOfflineStorage.getItems(...arguments);
}
```

#### calICalendar.getItemsAsArray()

This is a new addition to the API that returns the results as an array instead of a `ReadableStream`. The `BaseClass` provider has a default implementation however providers not extending it should provide their own implementation. If you intend to use this method, please be careful about memory usage with large queries.

### calStorageCalendar.resetItemOfflineFlag()

Since Thunderbird 96, this function returns a Promise. Replace

```javascript
let resetListener = {
    QueryInterface: ChromeUtils.generateQI(["calIOperationListener"]),
    onGetResult(calendar, status, itemType, detail, items) { },
    onOperationComplete(calendar, status, opType, id, detail) {
      // Reset completed.
    },
}
storage.resetItemOfflineFlag(item, resetListener);
```

by

```javascript
await storage.resetItemOfflineFlag(item);
// Reset completed.
```

### `ChromeUtils.import()`

Since Thunderbird 101, it is no longer possible to load JSMs via extension URLs, for example

```javascript
var { myModule } = ChromeUtils.import(extension.rootURI.resolve("myModule.jsm"));
```

It is now mandatory to register an internal URL, for example a `resource://` URL. See our [Activity Manager Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.activityManager).

### DisplayNameUtils.getCardForEmail()

This method has been removed in 102.3.0. Replace its former usage

```javascript
let cardDetails = DisplayNameUtils.getCardForEmail(emailAddress);
```

by

```javascript
let card = MailServices.ab.cardForEmailAddress(emailAddress);
let cardDetails = {
  card,
  book: card ? MailServices.ab.getDirectoryFromUID(card.directoryUID) : null,
a};
```

### `NotificationBox.appendNotification()`

The parameters have [changed](https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/toolkit/content/widgets/notificationbox.js#78-149) in Thunderbird 94. Most former properties have moved into an object. Replace this

```javascript
notificationbox.appendNotification(
  notificationLabel,
  notificationId,
  imageUrl,
  notificationbox.PRIORITY_CRITICAL_HIGH,
  buttons,
  eventCallback,
  customElementClass
);
```

with this

```javascript
let notification = notificationbox.appendNotification(
    notificationId,
    {
        label: notificationLabel,
        priority: notificationbox.PRIORITY_CRITICAL_HIGH,
        eventCallback: eventCallback,
        notificationIs: customElementClass
    },
    buttons
);
notification.messageImage.src = imageUrl;
```

### nsIAbCard

The interface itself has not changed much, but contact details are handled differently now. Instead of storing the individual contact details as key/value pairs, they are now stored as a vCard string in the `_vCard` property. The interface has gained two new members:

* `supportsVCard` a boolean value indicating support for vCard (or lack thereof).
* `vCardProperties` is a `VCardProperties` object if the card supports vCard, or null

All former standard contact properties (now referred to as [banished properties](https://searchfox.org/comm-esr102/source/mailnews/addrbook/modules/VCardUtils.jsm#306-348)) are migrated into the vCard string and can no longer be updated directly via `card.setProperty()`. A limited set of banished properties can still be read from: `DisplayName`, `FirstName`, `LastName`, `PrimaryEmail`, `SecondEmail`, and `NickName`.

#### Creating a new card

The new `AddrBookCard` object is a wrapper for `nsIAbCard` to enable support for vCards. No longer use `nsIAbCard` to create a new card:

```javascript
let card = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(
    Ci.nsIAbCard
);
card.setProperty("DisplayName", "Default User");
card.setProperty("PrimareEmail", "user@inter.net");
addrbook.addCard(card);
```

Instead, create an `AddrBookCard`:

```javascript
const { XPCOMUtils } = ChromeUtils.import(
  "resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
  AddrBookCard: "resource:///modules/AddrBookCard.jsm",
  VCardProperties: "resource:///modules/VCardUtils.jsm",
  VCardPropertyEntry: "resource:///modules/VCardUtils.jsm",
});

// Create a new card from a vCard.
let newCard = new AddrBookCard();
newCard.setProperty("_vCard", "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Default User\r\nUID:123456\r\nEMAIL:user@inter.net\r\nEND:VCARD\r\n");
// When a card is saved, a UID which might have been set in the _vCard string
// is ignored. The UID of the card is the one specified in its UID member. If
// it is not set, a new UID will be generated. Use its vCardProperties object
// to access the parsed UID and manually update the card's UID.
let uid = newCard.vCardProperties.getFirstValue("uid");
if (uid) {
  newCard.UID = uid;
}
addrbook.addCard(newCard);
```

#### Modifying an existing card

To update a card, create a new `AddrBookCard` from the updated vCard string and enforce the same UID:

```javascript
let updatedCard = new AddrBookCard();
card.setProperty("_vCard", "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Admin User\r\nEMAIL:admin@inter.net\r\nEND:VCARD\r\n");
updatedCard.UID = newCard.UID;
addrbook.modifyCard(newCard);
```

Alternativly, modify the card's `vCardProperties`:

```javascript
newCard.vCardProperties.addValue("email","lazy@inter.net");
newCard.vCardProperties.removeValue("email","admin@inter.net");
newCard.vCardProperties.getFirstEntry("fn").value = "Lazy User";
addrbook.modifyCard(newCard);
```

{% hint style="warning" %}
After an `AddrBookCard` has been created, its `vCardProperties` object is populated on first access from the cards `_vCard` string property. While saving the card, its `_vCard` string property is re-generated from its `vCardProperties`.

This effectivly means that all changes to the `_vCard` string property after its `vCardProperties` object has been used, are ignored.
{% endhint %}

#### Convert between key/value pairs and vCard

[VCardUtils.jsm](https://searchfox.org/comm-esr102/source/mailnews/addrbook/modules/VCardUtils.jsm) contains a number of utility functions for converting between the storage types:

```javascript
// Convert a vCard string into a key/value property Map. Not all details stored
// in the card will be available. Data loss is inevitable.
let vCardProperties = VCardProperties.fromVCard(vCardString);
let propertyMap = vCardProperties.toPropertyMap();

propertyMap.set("Notes", "This is a note");

// Convert the updated property Map back into a vCard.
let vCardPropertiesUpdated = VCardProperties.fromPropertyMap(propertyMap);
let vCardStringUpdated = vCardProperties.toVCard();
```

Read more about the address book implementation in Thunderbirds core documentation:

{% content-ref url="/pages/zoVODPPO9K6WMzuX15QX" %}
[Address Book](/thunderbird-development/codebase-overview/address-book)
{% endcontent-ref %}

### nsICollationFactory

Has been removed in Thunderbird 93. One of its use case was to compare locale strings. You can replace the following:

```javascript
function localeCompare(a, b) { 
  var collator = Cc["@mozilla.org/intl/collation-factory;1"]
    .getService(Components.interfaces.nsICollationFactory)
    .CreateCollation();
  return collator.compareString(0, a, b);
}
```

by

```javascript
function localeCompare(a, b) { 
  return a.localeCompare(b);
}
```

### nsIFileProtocolHandler

The method `getURLSpecFromFile` has been replaced by `getURLSpecFromActualFile` and `getURLSpecFromDir` in Thunderbird 92. Use the variant which fits your `file` object.

### nsIFolderListener

Its generic callback functions have been renamed from `onItem*()` to `onFolder*()` in Thunderbird 94. Where needed, dedicated `onMessage*()` callback functions have been added:

* OnItemAdded(in nsIMsgFolder aParentItem, in nsISupports aItem)\
  -> *onFolderAdded(in nsIMsgFolder parent, in nsIMsgFolder child)*\
  -> *onMessageAdded(in nsIMsgFolder parent, in nsIMsgDBHdr msg)*
* OnItemRemoved(in nsIMsgFolder aParentItem, in nsISupports aItem)\
  -> *onFolderRemoved(in nsIMsgFolder parent, in nsIMsgFolder child)*\
  -> *onMessageRemoved(in nsIMsgFolder parent, in nsIMsgDBHdr msg)*

The following callback functions were renamed without changing their parameters:

* OnItemEvent -> *onFolderEvent*
* OnItemPropertyChanged -> *onFolderPropertyChanged*
* OnItemIntPropertyChanged -> *onFolderIntPropertyChanged*
* OnItemBoolPropertyChanged -> *onFolderBoolPropertyChanged*
* OnItemUnicharPropertyChanged -> *onFolderUnicharPropertyChanged*
* OnItemPropertyFlagChanged -> *onFolderPropertyFlagChanged*

### nsIMsgCompSendFormat.AskUser

Has been renamed to `nsIMsgCompSendFormat.Auto` in Thunderbird 101.

### nsIMsgSendListener.onGetDraftFolderURI(aFolderURI)

The parameters of this listener have been changed in Thunderbird 102. The header messageId has been added:

```javascript
nsIMsgSendListener.onGetDraftFolderURI(aMsgId, aFolderURI)
```

The new parameter has been added at the first position to [match all the other listeners](https://searchfox.org/comm-central/rev/7923bcceab6029d38d0be76a021424b300997dbe/mailnews/compose/public/nsIMsgSendListener.idl), which already return the messageId.

### nsIPrintSettings.\*

A few members and methods have been removed without replacement:

* `isPrintSelectionRBEnabled` (see alternative described in the following section)
* `isCancelled`
* `saveOnCancel`
* `showPrintProgress`
* `SetupSilentPrinting()`

Instead of `printToFile`, use [outputDestination](https://searchfox.org/mozilla-central/rev/3419858c997f422e3e70020a46baae7f0ec6dacc/widget/nsIPrintSettings.idl#291) with a value from [OutputDestinationType](https://searchfox.org/mozilla-central/rev/3419858c997f422e3e70020a46baae7f0ec6dacc/widget/nsIPrintSettings.idl#93).

### nsIPrintingPromptService.\*

Has been replaced by `nsIPrintDialogService.*`.

The function `showPrintDialog()` has also been updated to accept a new parameter to indicate if the entire document or just the current selection should be printed (replacement for the removed `nsIPrintSettings.isPrintSelectionRBEnabled`).

```javascript
await Cc["@mozilla.org/widget/printdialog-service;1"]
 .getService(Ci.nsIPrintDialogService)
 .showPrintDialog(window, selectionOnly, printingSettings);
```

## Changed Core Functions

### OpenMessageInNewTab

The parameters of this function have changed: It is now possible to specify, which msgHdr is to be displayed. To open the currently selected message, update your usage from

```javascript
OpenMessageInNewTab(event);
```

to

```javascript
OpenMessageInNewTab(gFolderDisplay.selectedMessage, { event });
```

## Changed JSM Files

### folderUtils.jsm

Since Thunderbird 97, the JSM no longer exports a set of functions but a single object. The file has also been renamed from `folderUtils.jsm` to `FolderUtils.jsm`. Replace this former usage:

```javascript
var { 
  getFolderProperties,
  allAccountsSorted,
  getMostRecentFolders,
  folderNameCompare 
} = ChromeUtils.import("resource:///modules/folderUtils.jsm");
```

by

```javascript
var { FolderUtils } = ChromeUtils.import("resource:///modules/FolderUtils.jsm");
var {
  getFolderProperties,
  allAccountsSorted,
  getMostRecentFolders,
  folderNameCompare,
} = FolderUtils;
```

or use the FolderUtils namespace to access its functions.


# Update for Thunderbird 91

Required steps to update add-ons for Thunderbird 91.

Support for legacy extensions was removed from Thunderbird Beta version 74, released in February 2020. Since Thunderbird 78 only modern [MailExtensions](https://developer.thunderbird.net/add-ons/about-add-ons) are supported. This section only covers the required update steps for add-ons which are already compatible with Thunderbird 78 and need to be made compatible with Thunderbird 91.

## Changes in Thunderbird Core

MailExtensions can still run legacy code inside [Experiments](/add-ons/mailextensions#experiment-apis). Such legacy code has to be adjusted to changes made in Thunderbird Core. All known changes are listed in the following document:

* [Changes in Thunderbird 79-91](/add-ons/updating/tb91/changes)

If you have encountered a change which is not yet listed there, please [contact us](/add-ons/community), so we can update the list.

## Changes in Shared Experiments

If you are using any of the shared Experiments, you probably do not have to update them on your own. Check if an updated version is already available:

* [Shared Experiments on DTN](/add-ons/mailextensions#sharing-experiment-apis)


# Adapt to Changes in Thunderbird 79-91

This document tries to cover all the internal changes that may be needed to make Experiment add-ons compatible with Thunderbird 91. If you find changes which are not yet listed on this page, you can ask for help and advice in one of our [communication channels](https://developer.thunderbird.net/#getting-plugged-into-the-community).

## Changed API

### nsISimpleEnumerator, nsIArray and nsIMutableArray

Most usage of `nsIArray` and `nsIMutableArray` has been replaced by standard JavaScript arrays and functions which returned a `nsISimpleEnumerator`have been changed to return a JavaScript array as well. The following APIs have been updated (links to actual patches, showing how core handled the change):

* [nsIAbCard.properties](https://hg.mozilla.org/comm-central/diff/f7bc5c5ef5ebaec6fcd3394905b1190f7f9043e3/mailnews/addrbook/modules/AddrBookDirectory.jsm) (returns array)
* [nsIAbDirectory.childCards](https://hg.mozilla.org/comm-central/diff/8d90e013f1e5ec392d10832272e622276010be05/mailnews/addrbook/public/nsIAbDirectory.idl) (returns array)
* nsIAbDirectory.getCardsFromProperty (returns array)
* nsIAbManager.directories (returns array)\\
* [nsIMsgAccountManager.accounts](https://hg.mozilla.org/comm-central/diff/1da9c365060718ffc88deb4d39b3930aeb02a1a7/calendar/base/modules/utils/calEmailUtils.jsm) (returns array)
* [nsIMsgAccountManager.allFolders](https://hg.mozilla.org/comm-central/diff/4fd2ffb624922351c4c7ec6a775c78b436339fb3/mail/test/browser/folder-display/browser_recentMenu.js) (returns array)
* [nsIMsgAccountManager.allIdentities](https://hg.mozilla.org/comm-central/diff/8d219926818378dda077442385c0d59650cc3ab6/calendar/base/modules/utils/calItipUtils.jsm) (returns array)
* [nsIMsgAccountManager.allServers](https://hg.mozilla.org/comm-central/diff/16ca51abc6dda000296c7d7d082b9951ca04fd6d/mail/base/content/folderPane.js) (returns array)\\
* nsIMsgCompFields.attachments (returns array)\\
* [nsIMsgCopyService.CopyFolders](https://hg.mozilla.org/comm-central/diff/4f8d3dff196eba35f09bbbe4f6487f882c07840e/mailnews/imap/test/unit/test_localToImapFilter.js) (has been [renamed](#nsimsgcopyservice-copyfolders) and now only accepts a single src folder)
* [nsIMsgCopyService.CopyMessages](https://hg.mozilla.org/comm-central/diff/fe318ec033d42bd4342a63b86c320116bfa5c5bc/mail/test/browser/message-window/browser_commands.js) (expects an array for the second argument)
* nsIMsgFilterCustomAction.getCustomActions (returns array)
* [nsIMsgFolder.addKeywordsToMessages](https://hg.mozilla.org/comm-central/diff/4f1fac782b47fc236f62a18e7a51a51c8982edfd/mail/base/content/mailWindowOverlay.js#l1.71) (expects an array for the first argument)
* [nsIMsgFolder.deleteMessages](https://hg.mozilla.org/comm-central/diff/c2ddbcc4f19781c5c61f46918a94dd33fab2c8d9/mail/components/compose/content/MsgComposeCommands.js) (expects an array for the first argument)
* [nsIMsgFolder.listFoldersWithFlags](https://hg.mozilla.org/comm-central/diff/d146b4e2061c4ec55fce6ac0615c722fed19e756/mail/base/content/folderPane.js#l1.16) (returns array)
* [nsIMsgFolder.markMessagesRead](https://hg.mozilla.org/comm-central/diff/93610e507d37b2da5e034536ba05f7d2679fcfac/mail/components/extensions/parent/ext-messages.js#l1.22) (expects an array for the first argument)
* [nsIMsgFolder.markMessagesFlagged](https://hg.mozilla.org/comm-central/diff/93610e507d37b2da5e034536ba05f7d2679fcfac/mail/components/extensions/parent/ext-messages.js#l1.25) (expects an array for the first argument)
* [nsIMsgFolder.removeKeywordsFromMessages](https://hg.mozilla.org/comm-central/diff/4f1fac782b47fc236f62a18e7a51a51c8982edfd/mailnews/base/test/unit/test_bug428427.js#l1.40) (expects an array for the first argument)
* [nsIMsgFolder.setJunkScoreForMessages](https://hg.mozilla.org/comm-central/diff/93610e507d37b2da5e034536ba05f7d2679fcfac/mail/components/extensions/parent/ext-messages.js#l1.29) (expects an array for the first argument)
* [nsIMsgFolder.subFolders](https://hg.mozilla.org/comm-central/diff/d0c8f7c8585e0f485a80f67ccb483b329115eedd/mail/test/browser/folder-pane/browser_folderNamesInRecentMode.js) (returns array)\\
* [nsIMsgFolderListener.msgsClassified](https://hg.mozilla.org/comm-central/diff/e7ab0d2b72908c0a66347f188f8e00d3b87e7b11/mail/components/extensions/parent/ext-messages.js) (first parameter is an array)\\
* nsISmtpService.servers (returns array)

  \\

### fixIterator() & iteratorutils.jsm

The function `fixIterator()` is no longer needed by any core code and was subsequently removed together with `iteratorutils.jsm`. It was mostly used in the following way:

```javascript
for (let account of fixIterator(MailServices.accounts.accounts)) {
  ...
}
```

Since `MailServices.accounts.accounts` or `MailServices.accounts.allIdentities` return a simple array since Thunderbird 75, there is no need to pipe it through `fixIterator()` anymore. If your add-on is multi-version compatible and still supports Thunderbird 68 this has to be dealt with separately.\\

### nsILoadInfo.SEC\_ALLOW\_CROSS\_ORIGIN\_DATA\_IS\_NULL

Renamed in Beta 80 to `SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL`. This is often used as the `aSecurityFlags` argument in calls to `Services.io.newChannelFromURI()`.

```javascript
nsIChannel newChannelFromURI(
   in nsIURI aURI,
   in Node aLoadingNode,
   in nsIPrincipal aLoadingPrincipal,
   in nsIPrincipal aTriggeringPrincipal,
   in unsigned long aSecurityFlags,
   in nsContentPolicyType aContentPolicyType);
```

For example [here](https://searchfox.org/comm-central/rev/bb3eb2b4cb4b17405e8efcc423563a7a07473292/calendar/base/modules/utils/calProviderUtils.jsm#74).\\

### nsILoadInfo.SEC\_REQUIRE\_SAME\_ORIGIN\_DATA\_INHERITS

Renamed in Beta 80 to `SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT`.

### nsIMsgCompose.SendMsg

Renamed in Beta 86 to `nsIMsgCompose.sendMsg`. It also returns a Promise now. More details can be found [here](https://searchfox.org/comm-central/rev/bb3eb2b4cb4b17405e8efcc423563a7a07473292/mailnews/compose/public/nsIMsgCompose.idl#117).\\

### nsISocketTransportService.createTransport

Changed in Beta 87. Needs a fifth parameter to specify a `nsIDNSRecord` value, but can be `null` if not needed:

```javascript
nsISocketTransport createTransport(in Array<ACString> aSocketTypes,
                                   in AUTF8String aHost,
                                   in long aPort,
                                   in nsIProxyInfo aProxyInfo,
                                   in nsIDNSRecord dnsRecord);
```

See [here](https://searchfox.org/mozilla-central/rev/15f6b60e343c536305a5aa81e2020d7b87f93158/netwerk/base/nsISocketTransportService.idl#62) for more details.

### IOUtils.jsm

The Thunderbird-specific [IOUtils.jsm](https://hg.mozilla.org/comm-central/file/bec25a74919c93d954d48beeb617a1ecb92e55b8/mailnews/base/src/IOUtils.jsm) file was removed without a direct replacement. You need to alter your logic or copy code from the old JSM into your add-on. [Bug 1678109](https://bugzilla.mozilla.org/show_bug.cgi?id=1678109) might be a good inspiration, as it contains the changes that were necessary for core code.

## Renamed or Replaced API

### nsIMsgCopyService.CopyFileMessage

Has been renamed to `nsIMsgCopyService.copyFileMessage`.

### nsIMsgCopyService.CopyMessages

Has been renamed to `nsIMsgCopyService.copyMessages`.

### nsIMsgCopyService.CopyFolders

Has been renamed to `nsIMsgCopyService.copyFolder` and no longer accepts an Array of src folders, but a single src folder.

### nsIMsgCopyService.NotifyCompletion

Has been renamed to `nsIMsgCopyService.notifyCompletion`.

### nsIMsgFolder.deleteSubFolders

Has been replaced by [nsIMsgFolder.deleteSelf](https://hg.mozilla.org/comm-central/diff/d4333da4784fc707a56a5bdda13392a94765c05b/mail/base/content/folderPane.js#l1.14).

## Removed API

### Log4Moz

The file `Log4moz.jsm` has been removed in Thunderbird 85. Instead, use `console`:

* `console.debug()`
* `console.log()`
* `console.trace()` - same as `console.log()`, but dumps a trace log additionally.
* `console.info()`
* `console.warn()`
* `console.error()`

These basic log functions accept multiple parameters, which are all dumped to the console. For example:

```javascript
console.log("function XY executed", objectX, objectY);
```

Alternatively,`console.createInstance()` allows to define a custom console logger, with a `prefix` and a `maxLogLevel`. The `maxLogLevel` can be used to disable/enable logging in debug or production builds or set the level based on users choice.

```javascript
var gLog = console.createInstance({
    prefix: "My great Add-on",
    maxLogLevel: "Warn", // Error, Warn, Info, Trace, Log, Debug
  });

gLog.info("This will not be logged, as maxLogLevel is set to 'Warn'");
```

The levels `Info`, `Trace` and `Log` are actually identical.

### nsIAbListener

Interface has been dropped in favor of an observer based approach. See

* <https://searchfox.org/comm-central/source/mail/base/content/msgHdrView.js#379-429>
* <https://searchfox.org/comm-central/source/mail/base/content/msgHdrView.js#277>
* <https://searchfox.org/comm-central/source/mail/base/content/msgHdrView.js#335>

### nsIMsgFolder.listFoldersWithFlags

Use [nsIMsgFolder.getFoldersWithFlags](https://searchfox.org/comm-central/rev/03c30c3cab0afd0fb2b5d53c7dd5a021366a8810/mailnews/base/public/nsIMsgFolder.idl#442)instead.

### nsIMsgFolderNotificationService.notifyItemElement()

Use the dedicated function [MailServices.mfn.notifyMsgsJunkStatusChanged](https://searchfox.org/comm-esr91/rev/4732e5ce696f2c33f616e7105ce253c2e89057ae/mailnews/base/public/nsIMsgFolderNotificationService.idl#83) or [MailServices.mfn.notifyFolderReindexTriggered](https://searchfox.org/comm-esr91/rev/4732e5ce696f2c33f616e7105ce253c2e89057ae/mailnews/base/public/nsIMsgFolderNotificationService.idl#118) instead.

## Broken/Removed XUL Elements

### \<xul:grid>

The grid element does not seem to work anymore. Alternative is to use \<html:table>.

## Changes in commonly used Files

### folderPane.js

The `ftvItem` object has been renamed to `FtvItem` in Beta 86.

### MsgComposeCommands.js

#### GetMsgAttachmentElement()

Has been replaced by`gAttachmentBucket`. More information can be found [here](https://hg.mozilla.org/comm-central/diff/b84ef4aee6c977f95fdf04d37f74791d3fecfbf4/mail/components/compose/content/MsgComposeCommands.js#l1.666).

#### attachmentsCount()

Has been replaced by `gAttachmentBucket.itemCount`.

#### attachmentsSelectedCount()

Has been replaced by `gAttachmentBucket.selectedCount`.

### chrome://messenger/content/newmailalert.xhtml

No longer supports the `gAlertListener`, `gUserInitiated` and `gOrigin` parameters when being opened (argument 1-3). Instead, the following parameters are are used now:

```
+  // arguments[0] --> The real nsIMsgFolder with new mail.
+  // arguments[1] --> The keys of new messages.
+  // arguments[2] --> The nsIObserver to receive window closed event
```

## Low-level changes

### Thunderbird is now multi-process (e10s)

Thunderbird can now separate content into different processes, which can only communicate through limited channels. This change has advantages relative to security and performance, but restricts code from freely accessing data that belongs to a different process.

In Thunderbird 91, this is primarily affecting pages belonging to an Add-on, like the background page or frames injected with an experiment. Most other parts of Thunderbird did not (yet!) change as much.

In practical terms, this means that you may need to update your experiments:

* **If you create frames in Thunderbird's UI**, these frames now need the appropriate attributes to load content in the right process. You can find an example of required changes in [the E10S compatibility commit for the CustomUI experiment](https://github.com/rsjtdrjgfuzkfg/thunderbird-experiments/commit/11232201ff437e7bb293efdcb93ecc3963a8328d#diff-41cf834ce8c3fae411d5f4c18abf8c024630074e616cf98307e582cea5362be7).
* **If you exchange raw objects between WebExtension scopes / "child" experiment code and Thunderbird / "parent" experiment code**, you need to migrate to an indirect approach, usually based on explicit message passing. Depending on the complexity of your task, this can either happen through notifyTools, a custom experiment API or through custom experiment code directly using cross-process APIs (likely either [message managers](https://searchfox.org/mozilla-central/source/dom/chrome-webidl/MessageManager.webidl) or [actors](https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html)).
* As a consequence, **if you load JavaScript modules in "child" experiment code**, you will now get separate instances of the JSM: each process has its own instance, and there could be multiple child processes. If you keep using JSMs from "child" code, you may furthermore need to manually unload these separate instances on API shutdown (`ExtensionAPI.onShutdown`), even if you use a catch-all unloading solution like CachingFix or the WindowListener API.


# Update for Thunderbird 78

Required steps to update add-ons for Thunderbird 78.

{% hint style="info" %}
Support for legacy WebExtensions was removed from Thunderbird Beta version 74, released in February 2020. Only modern WebExtensions are now compatible with Thunderbird 78.
{% endhint %}

## Required Changes

There are two types of changes which are required to make your legacy extensions compatible with Thunderbird 78:

* The legacy WebExtension must be converted to a modern WebExtension

{% content-ref url="/pages/DTjHmbwJ51QK1bJWb9zY" %}
[Convert legacy WebExtensions to modern WebExtensions](/add-ons/updating/historical-overview/legacy-to-modern)
{% endcontent-ref %}

* All extensions need to be updated to reflect changes in Thunderbird core, like renamed/replaced API calls, removed support for some XUL elements and much more.

{% content-ref url="/pages/-LmPscFTyqiM9ltgchMv" %}
[Adapt to Changes in Thunderbird 69-78](/add-ons/updating/tb78/changes)
{% endcontent-ref %}

## Additional Tips

{% content-ref url="/pages/-LclSX0RLl8c4AuEWlWp" %}
[Tips and Tricks](/add-ons/resources/tips-and-tricks)
{% endcontent-ref %}


# Adapt to Changes in Thunderbird 69-78

This document tries to cover all the internal changes that may be needed to make add-ons compatible with Thunderbird 78. If you find changes which are not yet listed on this page, you can ask for help and advice in one of our [communication channels](https://developer.thunderbird.net/add-ons/community).

The changes are grouped by category and are listed in the order we became aware of them.

## Changed XUL elements

{% hint style="warning" %}
As of Thunderbird 74, the built-in overlay loader has been removed, which means XUL overlay files are no longer supported. The preferred way to interact with Thunderbird is through WebExtension and MailExtension APIs, which only support HTML/CSS.

However, Thunderbird itself is still using XUL for its UI, and it is still possible to interact with that XUL based UI through experimental APIs. More information can be found in the [update guide for Thunderbird 78](/add-ons/updating/tb78#step-5-creating-missing-ui-entry-points-and-apis-as-experiments).

Since XUL is still usable, this document includes information about deprecated and removed XUL elements.
{% endhint %}

{% hint style="warning" %}
Some XUL elements have been converted to [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), which have an additional `is="something"` attribute. If such a custom element is to be created with `createXULElement()`, the `is` parameter needs to be passend in via the second argument:

```javascript
let toolbar = document.createXULElement(
    "toolbar", 
    { is : "customizable-toolbar" }
);
toolbar.setAttribute("is", "customizable-toolbar");
```

{% endhint %}

### \<textbox>

Removed completely in TB71. Use

```markup
<html:input>
```

All former values of the `type` parameter of the `textbox` element are supported by `html:input` as well. For proper styling include the following CSS file: `chrome://messenger/skin/input-fields.css`

The `flex` parameter is no longer supported and should be removed. Attach the `input-container` class to a surrounding `hbox` to force the input field to behave like a former `flex="1"` `textbox`.

If the input fields context menu is not working, you need to include two JavaScript files by adding:

```markup
<script src="chrome://global/content/globalOverlay.js"/>
<script src="chrome://global/content/editMenuOverlay.js"/>
```

{% hint style="info" %}
JavaScript methods that are using `element.localName == "textbox"` , `getElementsByTagName("textbox")`or similar need to be updated as well.

Visually compare the fields before and after the conversion to be sure the UI, sizing, and spacing doesn't change.
{% endhint %}

### \<toolbar customizable="true">

In TB78, the XUL element `toolbar` with attribute `customizable` has been re-implemented as a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements). It needs an additional `is` attribute. The following example is taken from the [source of the Thunderbird calendar](https://searchfox.org/comm-central/rev/444b626fc442cb92b1b29ee47912600bc61bab1f/calendar/base/content/dialogs/calendar-event-dialog.xhtml#574):

```markup
<!-- Note the additional "is" attribute: -->
<toolbar is="customizable-toolbar"
   id="event-toolbar"
	 customizable="true"   
	 toolboxid="event-toolbox"
	 class="chromeclass-toolbar"
	 labelalign="end"
	 defaultlabelalign="end"
	 context="event-dialog-toolbar-context-menu"
	 defaultset="button-url,button-delete"/>
```

### \<wizard>

In TB78, the XUL element `wizard` may no longer be a top level element, but must be encapsulated by a `window` element which includes some fluent locales:

```markup
<window 
      width="600"
      height="600"
      title="Wizard Title"
      xmlns:html="http://www.w3.org/1999/xhtml"
      xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <linkset>
    <html:link rel="localization" href="toolkit/global/wizard.ftl"/>
  </linkset>

  <wizard>
  ...
  </wizard>
</window>
```

If you have referred to the `wizard` element by `document.documentElement.*`, this is now referring to the `window` element. Use `getElementById()` instead.

### \<wizardpage>

If you set the label of a `wizardpage` element via JavaScript during wizard load, it will be ignored. You have to manually call `_adjustWizardHeader` after the label has been set. :

```javascript
document.getElementById(wizardID)._adjustWizardHeader();
```

### \<richlistbox>

With TB78, the method `scrollToIndex(idx)` has been removed, replace it with:

```javascript
richlistbox.ensureElementIsVisible(listbox.getItemAtIndex(idx),true);
```

## Changed API

### document.createElement()

With TB69, the default namespace for createElement has switched from XUL to HTML. So you can no longer create XUL elements with `document.createElement()` (but HTML elements). To create XUL elements, use:

```javascript
document.createXULElement();
```

### nsISocketTransportService

Bug [1558726](https://bugzilla.mozilla.org/show_bug.cgi?id=1558726) introduced a breaking change to the `nsISocketTransportService` interface:

The first parameter used to be an array and the second one its length. This length parameter has been dropped, causing all subsequent arguments to shift by one. Furthermore, to create a default socket, you now have to pass an empty array as the first parameter, instead of null.

To stay backward compatible, check the argument count of "createTransport". In case it is 4 it is the new interface, in case it is 5 you got the old interface. Alternatively, you may also check if the version of Thunderbird is 69 or later.

```javascript
   if (transportService.createTransport.length === 4)
      return transportService.createTransport(((secure) ? ["starttls"] : []), host, port, proxyInfo);

    if (transportService.createTransport.length === 5) {
      if (secure)
        return transportService.createTransport(["starttls"], 1, host, port, proxyInfo);

      return transportService.createTransport(null, 0, host, port, proxyInfo);
   }

   throw new Error("Unknown Create Transport signature");
```

### nsICookie2

Merged into nsICookie.

### window\.QueryInterface()

With TB70, window objects don't need to be and cannot be QI'ed to nsIDOMChromeWindow, nsIDOMWindow and nsIInterfaceRequestor. You can just remove its usage, this is backwards compatible to TB 68.

{% hint style="info" %}
This does not apply to `nsIXULWindow`, which for example is used in `nsIWindowMediatorListener.onOpenWindow(xulWindow)`.
{% endhint %}

Equally, TreeColumns and TreeContentView don't need to be and cannot be QI'ed to nsITreeView.

### Services.scriptSecurityManager.createCodebasePrincipal()

Renamed. Use

```javascript
Services.scriptSecurityManager.createContentPrincipal()
```

### nsIStringBundle.formatStringFromName()

The 3rd parameter has been dropped, which was the length of the array passed in as the 2nd parameter. See the [patch applied to Thunderbird itself](https://bug1557829.bmoattachments.org/attachment.cgi?id=9071511).

### IOUtils.js

Module file extension changed in TB78 from `js` to `jsm`, now available via

```javascript
const { IOUtils } = ChromeUtils.import("resource:///modules/IOUtils.jsm");
```

### AddRecipient()

In TB78 the function `AddRecipient()` in the contact sidebar has been renamed to `awAddRecipientsArray()`.

### MailServices.headerParser.parseHeadersWithArray()

In TB71 `MailServices.headerParser.parseHeadersWithArray()` has been removed. Instead, use:

```javascript
let addresses = MailServices.headerParser.parseEncodedHeader(address);
for (let addr of addresses) {
  let email = addr.email
  let name = addr.name
  let fullname = addr.toString();
}
```

### nsIPromptService.select()

Since TB69 `nsIPromptService.select()` / `Services.prompt.select()` has dropped the parameter which specifies the length of the array of the item list which the user can choose from. See the [patch applied to Thunderbird itself](https://phabricator.services.mozilla.com/differential/changeset/?ref=1068287).

### nsIEditorStyleSheets has been removed

The [removal of `nsIEditorStyleSheets`](https://bugzilla.mozilla.org/show_bug.cgi?id=1449522) in TB77 necessitates the following changes:

* `nsIEditorStyleSheets.addOverrideStyleSheet(uri)`\
  -> `windowUtils.loadSheetUsingURIString(uri, windowUtils.AGENT_SHEET)`\\
* `nsIEditorStyleSheets.removeOverrideStyleSheet(uri)`\
  -> `windowUtils.removeSheet(uri, windowUtils.AGENT_SHEET)`\\
* `nsIEditorStyleSheets.enableStyleSheet(uri, enable)`\
  Either manually load or remove the stylesheet:\
  -> `windowUtils.loadStyleSheetUsingURIString(uri, windowUtils.AGENT_SHEET)`\
  -> `windowUtils.removeSheet(uri, windowUtils.AGENT_SHEET)`

### getAnonymousElementByAttribute() and getAnonymousNodes() have been removed

These two methods are leftovers from de-XBL effort and have not been working since TB68. In TB72 they have been removed completely and using them will now throw an error. They have to be replaced by [document.getElementById()](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById), [document.querySelectorAll()](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll) or similar methods.

The documents these methods have been used with have probably changed dramatically. Check out [searchfox.org](https://searchfox.org) to learn about the current layouts.

## Changes to the Address Book

### Searching a nsIAbDirectory

In TB68 a `nsIAbDirectory` could be searched by simply attaching a search query to the URI of the directory when calling `getDirectory()`:

```javascript
let cards = MailServices.ab.getDirectory(URI + "?" + searchQuery).childCards;
while (cards.hasMoreElements()) {
  let card = cards.getNext().QueryInterface(Components.interfaces.nsIAbCard);
  ...
}
```

In TB78 `getDirectory()` no longer accepts search queries and throws an error. Instead, use the `search()` method, which uses an `nsIAbDirSearchListener`. A simple promisified implementation could look like so:

```javascript
searchDirectory: function (uri, search) {
  return new Promise((resolve, reject) => {
    let listener = {
      cards : [],
      onSearchFinished(aResult, aErrorMsg) {
        resolve(this.cards);
      },
      onSearchFoundCard(aCard) {
        this.cards.push(aCard.QueryInterface(Components.interfaces.nsIAbCard));
      }
    }
    MailServices.ab.getDirectory(uri).search(search, listener);
  });
}

let cards = await searchDirectory(URI, searchQuery);
for (let card of cards) {
  ...
}
```

{% hint style="warning" %}
`MailServices.ab.getDirectory(uri).search(search, listener)` will not return anything, if `search` is empty.
{% endhint %}

## Changes to the Preference System

### Removal of onsyncfrompreference and onsynctopreference Attributes

With TB70, these two attributes have been removed from XUL elements like `textbox`, `radio`, `checkbox` and friends. This is no longer supported:

```markup
<textbox
    id="my-textbox"
    preference="some.preference.name"
    onsynctopreference="return onToPref();"
    onsyncfrompreference="return onFromPref();"/>
```

Most of the XUL based preferences system has been long removed and had to be replaced by either using the [preferencesBindings.js](https://developer.thunderbird.net/add-ons/updates/tb68#less-than-prefwindow-greater-than-less-than-prefpane-greater-than-less-than-preferences-greater-than-and-less-than-preference-greater-than) [wrapper](https://developer.thunderbird.net/add-ons/updates/tb68#less-than-prefwindow-greater-than-less-than-prefpane-greater-than-less-than-preferences-greater-than-and-less-than-preference-greater-than) or by manually reimplementing that functionality using the`nsIPrefService`.

The second approach is not affected by this change, as it has full control over the load and save process for all its preferences. The [preferencesBindings.js](https://developer.thunderbird.net/add-ons/updates/tb68#less-than-prefwindow-greater-than-less-than-prefpane-greater-than-less-than-preferences-greater-than-and-less-than-preference-greater-than) [wrapper](https://developer.thunderbird.net/add-ons/updates/tb68#less-than-prefwindow-greater-than-less-than-prefpane-greater-than-less-than-preferences-greater-than-and-less-than-preference-greater-than) has been updated and now supports:

```javascript
Preferences.addSyncFromPrefListener(
     document.getElementById("my-textbox"),
     function() { return onFromPref() });

Preferences.addSyncToPrefListener(
     document.getElementById("my-textbox"),
     function() { return onToPref() });
```

{% hint style="danger" %}
The `nsIPrefService`is not available via WebExtension APIs. It is therefore advised to move away from storing addon preferences in a preference branch and instead use the local storage via the [storage](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) API. More information can be found in the [update guide](https://developer.thunderbird.net/add-ons/updating/tb78#replacing-options).
{% endhint %}


# Update for Thunderbird 68

Required steps to update add-ons for Thunderbird 68.

{% hint style="info" %}
Technically, legacy bootstrap extensions and legacy overlay extensions need to be converted to modern WebExtensions, but by activating the **legacy mode**, their general structure does not need to be changed. Such extensions are called legacy WebExtensions.
{% endhint %}

## Required Changes

There are two types of changes which are required to make your legacy extensions compatible with Thunderbird 68:

* The legacy extension must be converted to a legacy WebExtension by replacing the old `install.rdf` by a `manifest.json`.

{% content-ref url="/pages/-Lc5vkKneusydLgduFSY" %}
[Convert legacy overlay extension to legacy WebExtension](/add-ons/updating/historical-overview/overlays)
{% endcontent-ref %}

{% content-ref url="/pages/-LfUZCpUvryuAmXOkWlj" %}
[Convert legacy bootstrapped extension to legacy WebExtension](/add-ons/updating/historical-overview/bootstrapped)
{% endcontent-ref %}

* All legacy extensions need to be updated to reflect changes in Thunderbird core, like renamed/replaced API calls, removed support for some XUL elements (need to use HTML elements now) and much more.

{% content-ref url="/pages/-Lc5wk5ug7\_NLqWCxYHg" %}
[Adapt to Changes in Thunderbird 61-68](/add-ons/updating/tb68/changes)
{% endcontent-ref %}

{% hint style="danger" %}
Even though it is possible to have both `install.rdf` and `manifest.json` files in your extension, so you *could* release a version compatible with Thunderbird 60 and 68, it is *not* suggested for the following reasons:

* The amount of changes is huge and some changes are incompatible with Thunderbird 60 so it will require extra steps to ensure the modified version still runs with Thunderbird 60.
* You may actually break your add-on for Thunderbird 60 users by releasing a backward compatible version for Thunderbird 68 ("Do not fix something, that is not broken").
* We think the time and resources needed to code and test backward compatible add-ons is not justified by the small amount of users running older versions of Thunderbird.
  {% endhint %}

{% hint style="success" %}
It **is** possible to maintain a legacy version **and** a WebExtension version of your add-on in parallel on ATN! You just need to use a higher major version number for the WebExtension version of your add-on and keep the old major version number when releasing a new legacy version. Basically releasing them in two different branches.
{% endhint %}


# Adapt to Changes in Thunderbird 61-68

This document tries to cover all the changes that may by needed to make add-ons compatible with Thunderbird 68. If you find stuff that is no longer working but is not yet on this list, check our [communication channels](https://developer.thunderbird.net/add-ons/community).

The changes are grouped by category and are listed in the order we became aware of them.

## Removed global Variables

A bunch of global variables available in some window scopes were removed. If you have used any of these, there are still available as the same names underneath `Components.interfaces.`

### main window

* nsMsgFolderFlags

### message composition window

* `nsIMsgCompDeliverMode`
* `nsIMsgCompSendFormat`
* `nsIMsgCompConvertible`
* `nsIMsgCompType`
* `nsIMsgCompFormat`
* `nsIAbPreferMailFormat`
* `nsIPlaintextEditorMail`
* `nsISupportsString`
* `mozISpellCheckingEngine`

## Removed XBL bindings

XBL is on death row. Many XBL bindings have been replaced or simply no longer exist. The remainder are being removed. This may result in slight behavior changes for some UI components.

With [this query](https://bugzilla.mozilla.org/buglist.cgi?o1=equals\&v1=1484976\&f1=blocked), you can see all the bugs related to de-XBL-ing Thunderbird, and see how the removal of each binding is handled.

{% hint style="info" %}
If you have your own XBL bindings, you can convert them to [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements). Here are some notes on the process of [converting an XBL binding into a custom element](https://wiki.mozilla.org/Thunderbird/de-xbl).
{% endhint %}

{% hint style="info" %}
As part of the de-XBL effort, the usage of the [nsIDOMDocumentXBL](https://udn.realityripple.com/docs/Archive/Mozilla/XBL/XBL_1.0_Reference/DOM_Interfaces) Interface has also been deprecated. This includes:

* getAnonymousElementByAttribute()
* getAnonymousNodes()

They have to be replaced by [document.getElementById()](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById), [document.querySelectorAll()](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll) or similar methods. The documents these methods have been used with have probably changed dramatically. Check out [searchfox.org](https://searchfox.org) to learn about the current layouts.
{% endhint %}

## Removed XUL elements

Some XUL elements (or some of their attributes) no longer exist and must be replaced by an HTML element or some other XUL element. It does not matter, if you use these elements in a XUL file (as in overlay extensions) or create them via JavaScript (as in bootstrapped extensions).

In order to use HTML elements in a XUL file, you must load the HTML namespace into your overlay or dialog:

```markup
<dialog
    xmlns:html="http://www.w3.org/1999/xhtml"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
```

The following list also includes deprecated elements, which could still be used but Thunderbird has already started to purge their usage. In that case you must update your overlay files which are overlaying such elements, otherwise your overlay will not be applied. Check [SearchFox](https://searchfox.org/comm-central/source) for the current state of the files you are overlaying.

{% hint style="info" %}
The replacements listed here might work in subtly different ways. Check your functionality!
{% endhint %}

### \<colorpicker>

Removed. Use

```markup
<html:input type="color">
```

### \<progressmeter>

Removed. Use

```markup
<html:progress max="100">
```

### \<listbox>, \<listitem>, \<listcell> and \<listcols>

All `<listbox>` related elements have been removed. Use instead. A `<richlistbox>` does not support cells or columns, just one `<richlistitem>` per row (which can contain multiple other elements like `hbox`, `vbox`, `label`or `image` elements)

Furthermore, a few dedicated `listbox/richtlistbox`methods have been removed and can be replaced as follows:

* `listbox.appendItem(label, value)`:

```javascript
let newNode = document.createXULElement("richlistitem");

// Store the value in the list item as before.
newNode.value = value; 
let newLabel = document.createXULElement("label");
// The label is now stored in the value attribute of the label element.
newLabel.value = label;

newNode.appendChild(newLabel);
listbox.appendChild(newNode);
```

* `listbox.insertItemAt(index, label, value)`:

```javascript
let newNode = document.createXULElement("richlistitem");

// See above example.

let refNode = listbox.getItemAtIndex(index);
refNode.parentNode.insertBefore(newNode, refNode);
```

* `listbox.removeItemAt(index)`:

```javascript
listbox.getItemAtIndex(index).remove();
```

### \<textbox multiline="true">

Removed. Use

```markup
<html:textarea>
```

### \<prefwindow>, \<prefpane>, \<preferences> and \<preference>

All preference related XUL elements have been removed. If you have something like this in your add-on:

{% tabs %}
{% tab title="preferences.xul" %}

```markup
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<?xml-stylesheet type="text/css" href="chrome://messenger/skin/preferences/preferences.css"?>
<!DOCTYPE prefwindow SYSTEM "chrome://path/to/locale.dtd">

<prefwindow 
   id="appPreferences"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <prefpane 
     id="pane1" 
     label="&title">
    <preferences>
      <preference 
         id="pref1" 
         name="extensions.nameOfAddon.pref1" 
         type="bool"/>
      <preference 
         id="pref2" 
         name="extensions.nameOfAddon.pref2" 
         type="string"/>
    </preferences>

    <checkbox
       id="checkbox1" 
       preference="pref1"
       label="&checkbox1.label;"
       accesskey="&checkbox1.accesskey;"/>
    <textbox 
      id="textbox1" 
      preference="pref2"/>       
  </prefpane>
</prefwindow>
```

{% endtab %}
{% endtabs %}

it must be replaced by a `dialog` as follows:

{% tabs %}
{% tab title="preferences.xul" %}

```markup
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<?xml-stylesheet type="text/css" href="chrome://messenger/skin/preferences/preferences.css"?>
<!DOCTYPE dialog SYSTEM "chrome://path/to/locale.dtd">

<dialog    
   id="appPreferences"
   buttons="accept"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <vbox>
    <checkbox 
       id="checkbox1" 
       preference="extensions.nameOfAddon.pref1"
       label="&checkbox1.label;" 
       accesskey="&checkbox1.accesskey;"/>
    <textbox 
       id="textbox1" 
       preference="extensions.nameOfAddon.pref2"/>
  </vbox>          

  <script 
     src="chrome://global/content/preferencesBindings.js" 
     type="application/javascript"/>
  <script 
     src="chrome://path/to/preferences.js" 
     type="application/javascript"/>          
</dialog>
```

{% endtab %}
{% endtabs %}

Note that the `DOCTYPE` changed and the `preference` attribute now contains the full ID of the preference. If you used more than one `prefpane` you need to rework the UI into [tabs](https://udn.realityripple.com/docs/Archive/Mozilla/XUL/tabbox).

Furthermore, note the included JavaScript file [preferencesBindings.js](https://searchfox.org/mozilla-central/rev/6e6265bd607cbe4c96e714f86d3d9e36620f63d6/toolkit/content/preferencesBindings.js) at the bottom, which is mandatory to recreate the functionality of the `preference` attribute. It is also mandatory, that you include a custom JavaScript file (as`preferences.js` in the above example) afterwards, which defines the types of the used preferences (which was formerly done inside the `preferences` tag). The file can be as short as this:

{% tabs %}
{% tab title="preferences.js" %}

```javascript
Preferences.addAll([
    { id: "extensions.nameOfAddon.pref1", type: "bool" },
    { id: "extensions.nameOfAddon.pref2", type: "string" },
    { id: "extensions.nameOfAddon.pref3", type: "int" },
]);
```

{% endtab %}
{% endtabs %}

Per default, all preferences will be saved instantly after they have been changed. If you want to postpone saving until the user clicks the OK button, add a `type` attribute and a cancel button to the dialog:

{% tabs %}
{% tab title="preferences.xul" %}

```markup
<dialog    
   id="appPreferences"
   type="child"
   buttons="accept, cancel"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
If you are doing or plan to do any advanced stuff with the preferences in JavaScript, like validating user entered values and such, it is recommended to abandon the usage of the `preference` attribute (and `preferencesBindings.js`) and directly use the [preferences service](https://udn.realityripple.com/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefService) instead.
{% endhint %}

### \<stringbundleset> and \<stringbundle>

Both elements have been removed. To load and access your own string property files, include the following in your JavaScript:

```javascript
let bundle = Services.strings.createBundle("chrome://path/to/your/string.property");
let stringProp = bundle.GetStringFromName("...");
```

### **\<statusbar> and \<statusbarpanel>**

Both elements are deprecated and its usage in Thunderbird is removed. They can be replaced by `hbox`elements with an appropriate `class` identifier:

```markup
<hbox class="statusbar">
  <label class="statusbarpanel"
         context="..."
         popup="..."
         value="label" />
</hbox>
```

You may actually use other elements besides `hbox` as a replacement for the `statusbarpanel`, like `label` or `image`.

### \<menulist editable="true">

The XUL element `menulist` no longer supports the `editable` attribute. However, editable menulists have been re-implemented as [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements). To be able to use it, you need some extra files to be linked from your document:

```markup
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/global.css"?>
<!-- New stylesheet needed: -->
<?xml-stylesheet type="text/css" href="chrome://messenger/skin/menulist.css"?>

<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <!-- New script needed: -->
  <script type="application/javascript" src="chrome://messenger/content/customElements.js"/>

  <!-- Note the additional "is" attribute: -->
  <menulist is="menulist-editable" editable="true">
    <menupopup>
      <menuitem value="foo" label="foo"/>
      <menuitem value="bar" label="bar"/>
    </menupopup>
  </menulist>
</page>
```

An editable menulist can also be created via JavaScript:

```javascript
let menulist = document.createXULElement("menulist", { is : "menulist-editable" });
menulist.setAttribute("is", "menulist-editable");
menulist.setAttribute("editable", "true");
```

### \<menulist> and \<menupopup>

Even though the `menupopup` element is defined as a direct child of the `menulist` element in the above example, it cannot be accessed by

```javascript
menuListElement.firstChild
```

anymore. You may assign its own ID to the `menupop` element, or get it by

```javascript
menuListElement.getElementsByTagName("menupopup")
```

You may use the inspector of the developer tools to see the full DOM structure of the `menulist` element.

### \<groupbox> and \<caption>

These two do not display as before. You now need to include the following css file:

```markup
<?xml-stylesheet type="text/css" href="chrome://messenger/skin/messenger.css"?>
```

While the `groupbox` tag continues to work, the `caption` tag has been removed. Use the following now:

```markup
<groupbox>
  <hbox class="groupbox-title">
    <label class="header">Your caption label</label>
  </hbox>
  Your groupbox body content.
</groupbox>
```

{% hint style="info" %}
In the current Thunderbird 68 Beta, this still looks a bit wrong, but will be fixed in Beta 3. More details can be found in [bug 1559964](https://bugzilla.mozilla.org/show_bug.cgi?id=1559964).
{% endhint %}

### \<datepicker> and \<timepicker>

Removed. Currently there is no working replacement part of Thunderbird itself, but Lightning has its own `datetimepicker`, which can be used.

```markup
<datetimepicker id="myDateTimePicker" />
```

Its value is a JavaScript `Date` object:

```javascript
document.getElementById("myDateTimePicker").value = new Date('December 17, 1995 03:24:00');
```

To be able to use the `datetimepicker`, the following CSS files need to be included:

```markup
<?xml-stylesheet type="text/css" href="chrome://calendar-common/skin/widgets/minimonth.css"?>
<?xml-stylesheet type="text/css" href="chrome://calendar/content/widgets/calendar-widget-bindings.css"?>
<?xml-stylesheet type="text/css" href="chrome://lightning-common/skin/datetimepickers.css"?>
```

The following JavaScript files also need to be included:

```markup
<script src="chrome://calendar/content/calendar-ui-utils.js"/>
<script src="chrome://messenger/content/customElements.js"/>
<script src="chrome://calendar/content/datetimepickers/datetimepickers.js"/>
```

If you do not want to be dependent on Lightning being installed, you need to include the above files with your add-on.

### \<notificationbox>

Removed. Thunderbird now has fixed build in notification boxes, where notifications can be added. The following list shows how to access some of them:

* Main window:\
  `let notifyBox = specialTabs.msgNotificationBar;`
* Message composer window:\
  `let notifyBox = gNotification.notificationbox;`
* Below the recipient list in the message composer window: `let notifyBox = gMessageNotificationBar.msgNotificationBar;`
* Most calendar dialogs:\
  `let notifyBox = gNotification.notificationbox;`

{% hint style="warning" %}
Since you no longer "own" notification boxes, you should not clear them by calling

`notifyBox.removeAllNotifications();`

as that would remove notifications added by others. You can get a specific notification by calling

`let notification = notifyBox.getNotificationWithValue(value);`

and remove only that via

`notificationbox.removeNotification(notification);`
{% endhint %}

You can still add notification boxes wherever you want, if you do not want to use the build in notification boxes (or if there is none). More details can be found [here](https://github.com/thunderbird/developer-docs/issues/31#issuecomment-508872843).

### \<textbox type="search">

Removed. Use:

```markup
<textbox is="search-textbox" class="searchBox">
```

## Renamed XUL elements

### \<mail-multi-emailHeaderField>

The element `mail-multi-emailHeaderField` has been renamed into `mail-multi-emailheaderfield` (no camelCase anymore).

See e.g. <https://searchfox.org/comm-esr68/source/mail/base/content/msgHdrView.inc.xul#282>

```markup
<mail-multi-emailheaderfield id="expandedfromBox" flex="1"/>
```

## Changed XUL elements

### \<treecol>

Each `treecol` can be either shown or hidden via the column picker. Up to TB68 the column picker closed after a column had been selected/toggled. By adding `closemenu="none"` to a `treecol`, the column picker stays open after the display state of associated `treecol` has been toggled.

## Changed event behavior

### \<dialog> events

Previously, if you had a `<dialog>` and you wanted to respond to the buttons being pressed, you’d have something like this:

```markup
<dialog buttons="accept,cancel"
        ondialogaccept="return onAccept();">
  <!-- dialog contents -->
</dialog>
```

The event handler would return true if the dialog should close, or false to prevent closing. This no longer works. Instead, add the event handlers in JavaScript:

```javascript
document.addEventListener("dialogaccept", function(event) {
  event.preventDefault(); // Prevent the dialog closing.
});
```

To prevent closing of the dialog, call `preventDefault()`. A return value is not needed.

This is valid for `ondialogaccept`, `ondialogextra1`, `ondialogextra2` and `ondialogcancel`.

### \<wizard> and \<wizardpage> events

The section about `<dialog>` events also applies to all `onwizard…` events on `<wizard>`, and `onpage…` events on `<wizardpage>`.

## Changes to Geko JavaScript Engine

As JavaScript itself evolves, browser engines will change accordingly. The following changes represent changes that affect JavaScript extension developers.

### String Generic Methods Deprecated

The nonstandard generic string methods have been deprecated and removed in the Geko engine and therefore Thunderbird 68+. A deprecation exception is issued if any methods are used. The updated paradigm uses String instance methods allowing for the use on any object.

Deprecated Generics Syntax:

```javascript
var strWithPadding = "   Hello There   "; 
String.trim(strWithPadding);
```

Instance Method Replacement:

```javascript
var strWithPadding = "   Hello There   "; 
strWithPadding.trim();
```

The following String methods are affected by the change:

```javascript
  contains(), substring(), toLowerCase(), toUpperCase(), charAt(),
  charCodeAt(), indexOf(), lastIndexOf(), startsWith(), endsWith(),
  trim(), trimLeft(), trimRight(), toLocaleLowerCase(), normalize(),
  toLocaleUpperCase(), localeCompare(), match(), search(), slice(),
  replace(), split(), substr(), concat(), localeCompare()
```

(All other String methods use the instance paradigm already)

## Changes for JavaScript modules

### Renamed JavaScript modules

A number of JavaScript modules have been renamed with the `.jsm` extension. Most notably:

* `mailServices.js` has been renamed to `MailServices.jsm`. This change was originally backwards-compatible with a deprecation warning, but the changes to module importing (see [below](#changed-javascript-module-import)) made that pointless and the old file has now been removed completely.
* `MailUtils.js` is now `MailUtils.jsm`.
  * `MailUtils.getFolderForUri` was renamed to `MailUtils.getExistingFolder`.

### Changed import of JavaScript modules

In Thunderbird 67, a major backwards-incompatible change was made to importing JavaScript modules. Where once you used any of these:

```javascript
Components.utils.import("resource://foo/modules/Foo.jsm");
// or…
Cu.import("resource://foo/modules/Foo.jsm");
// or…
ChromeUtils.defineModuleGetter(this, "Foo", "resource://foo/modules/Foo.jsm");
```

Or the two-argument variation:

```javascript
var { Foo } = Cu.import("resource://foo/modules/Foo.jsm", null);
// or…
var scope = {}; Cu.import("resource://foo/modules/Foo.jsm", scope); // scope.Foo…
```

You should now do this:

```javascript
var { Foo } = ChromeUtils.import("resource://foo/modules/Foo.jsm");
// or…
var scope = ChromeUtils.import("resource://foo/modules/Foo.jsm"); // scope.Foo…
```

`ChromeUtils.import` is a replacement for `Components.utils.import` (which was also changed in this way). Note that no second argument is supplied. The returned object is a dictionary of only the objects listed in `EXPORTED_SYMBOLS`.

## Changed API

### LoginManager

The function to retrieve passwords has lost its first parameter. Instead of

```javascript
var logins = Services.logins.findLogins({}, origin, formOrigin, realm);
```

call it as

```javascript
var logins = Services.logins.findLogins(origin, formOrigin, realm);
```

### document.persist(id, attribute)

Removed, use:

```javascript
Services.xulStore.persist(node, attribute);
```

Note: `document.persist()` used the ID attribute whereas `xulStore.persist()` uses the actual DOM node. More details in bug [1476678](https://bugzilla.mozilla.org/show_bug.cgi?id=1476678).

### AddonManager

All methods of the [AddonManager](https://udn.realityripple.com/docs/Mozilla/JavaScript_code_modules/Add-on_Manager/AddonManager#Method_Overview) API now return a Promise instead of executing a callback. Instead of calling it as

```javascript
AddonManager.getAddonByID(addon_id, callback_function)
```

you need to call it as

```javascript
AddonManager.getAddonByID(addon_id).then(callback_function)
```

### nsIMsgAccountManager

The `defaultAccount` member can (since TB 65) have a `null` value - if for some reason it doesn't have, or can't work out, the default account.

Hence you need to null-check it, and possibly handle not having an account at all (if in no other way than by an error message).

### nsIStringBundleService

Removed. Use

```javascript
Services.strings.createBundle("chrome://...")
```

### **nsIStreamListener**

The `onDataAvailable` method lost its `context` argument. This was removed in [bug 1525319](https://bugzilla.mozilla.org/show_bug.cgi?id=1525319) which breaks the API.

To be backward compatible you need to probe the parameters. In case the third parameter is an nsIInputStream it is the old API. If the second one is an input stream it is the new API.

```javascript
onDataAvailable(...args) {
  // The old API passes the stream as third parameter.
  if (args[2] instanceof Ci.nsIInputStream)
    return this.onOldDataAvailableCalled(args[2], args[3], args[4]);

  // The new API uses the second parameter.
  if (args[1] instanceof Ci.nsIInputStream)
    return this.onNewDataAvailableCalled(args[1], args[2], args[3]);

  throw new Error("Unknown signature for nsIStreamListener.onDataAvailable()");
}
```

### **nsIRequestObserver**

The `onStartRequest` and `onStopRequest` methods also no longer have a `context` argument, which could be detected in a similar way.

### **nsIProtocolHandler**

The obsolete method `newChannel` was removed and `newChannel2` was renamed to `newChannel`. ([Bug 1528971](https://bugzilla.mozilla.org/show_bug.cgi?id=1528971).)

As `newChannel` has been unused for a long time it should be safe to just replace the old `newChannel` implementation with the `newChannel2` and forward calls from `newChannel2`.

```javascript
// Change the signature to the new one...
// ... you'll need to add the loadInfo parameter.
//
// Note loadInfo may be null in Thunderbird 60.
newChannel(URI, loadInfo) {
  // Do your logic here
}

// Keep the old method as it will be needed for backward compatibility...
// ... and forward the request to the new method.
newChannel2(URI, loadInfo) {
  return this.newChannel(URI, loadInfo);
}
```

### nsIScriptableUnicodeConverter

The method `convertFromByteArray` has been removed. The new preferred way to deal with unicode is through the `TextEncoder` and `TextDecoder` classes.

To convert byte arrays to different charsets, use

```javascript
let charset = 'utf-8';
let encodedByteArray = [ /* ... */ ];
let decoder = new TextDecoder(charset); // charset can be omitted, default is utf-8
let decodedString = decoder.decode(new Uint8Array(encodedByteArray));
```

### nsIDOMElement, nsIDOMNode and other basic DOM interfaces

Removed. Use `Element`, `Node`, etc. instead, which are now available in all scopes.

### nsIDOMParser, nsIDOMSerializer, nsIXMLHttpRequest

These no longer need to be created by `Cc[...].createInstance(Ci....)`, but simply via the `new` keyword:

* `new DOMParser();`
* `new XMLSerializer();`
* `new XMLHttpRequest();`

They *should* be available in all scopes now. If not, the following is needed:

* `Cu.importGlobalProperties(["DOMParser"]);`
* `Cu.importGlobalProperties(["XMLSerializer"]);`
* `Cu.importGlobalProperties(["XMLHttpRequest"]);`

### nsITreeBoxObject, nsITreeColumn, nsITreeView

Trees have changed a lot. The `tree` object is now a `XULTreeElement`. It has lost the`treeBoxObject` property, because the `nsITreeBoxObject` has been removed. Most of its methods can now be accessed directly through the `tree` object. For example:

```javascript
// The treeBoxObject has been removed.
// tree.treeBoxObject.getLastVisibleRow();
tree.getLastVisibleRow();
```

Furthermore, `nsITreeColumn` has been replaced by the `TreeColumn` object. Even though their interfaces look the same, they could behave differently. Check your implementation, as this affects almost all methods of `nsITreeView`.

Some noteworthy changes:

* [tree.getCellAt()](https://dxr.mozilla.org/mozilla-central/source/dom/chrome-webidl/XULTreeElement.webidl#132) now returns a [TreeCellInfo](https://dxr.mozilla.org/mozilla-central/source/dom/chrome-webidl/XULTreeElement.webidl#10).
* If a method requires a `TreeColumn` parameter, a simple `{ id: columnName }` object no longer works. Get a proper `TreeColumn` object via `tree.columns.getNamedColumn(columnName)`.
* Trees no longer support selecting individual cells. The `TreeColumn` object no longer has a `selectable` attribute and `nsITreeView` has lost its `isSelectable()` method.

In general, check [searchfox.com](https://searchfox.org) to see the current definitions of tree related implementations:

* [XULTreeElement](https://searchfox.org/mozilla-beta/rev/b906de73ca1c11ffb73473731920f7867d364557/dom/chrome-webidl/XULTreeElement.webidl)
* [nsITreeView](https://searchfox.org/mozilla-beta/rev/b906de73ca1c11ffb73473731920f7867d364557/layout/xul/tree/nsITreeView.idl)
* [TreeColumn](https://searchfox.org/mozilla-beta/rev/b906de73ca1c11ffb73473731920f7867d364557/dom/webidl/TreeColumn.webidl)

### XPCOMUtils.generateQI()

Removed. Use

```javascript
ChromeUtils.generateQI()
```

### Services.io.newChannelFromURI2()

Renamed to

```javascript
Services.io.newChannelFromURI()
```

A long time ago newChannelFromURI2() has been added as an alternative to newChannelFromURI(), which as become deprecated by now. With ESR68 the old name is used again.


# How to convert legacy extensions?

The architecture of Thunderbird extensions has changed over the years. The following table describes the different legacy extension types, and how they can be converted to modern WebExtensions.

{% hint style="danger" %}
**The current Thunderbird ESR no longer supports legacy extensions.**

If you are currently maintaining a legacy extension, please identify the type of your extension in the table below and check the provided guides on updating your extension to remain compatible with the latest versions of Thunderbird.
{% endhint %}

{% tabs %}
{% tab title="Wrapped WebExtension" %}
Status: **Unsupported in Thunderbird 128.**

Wrapped WebExtension are modern WebExtensions, which use a so-called wrapper Experiment (the `WindowListener` Experiment or the `BootstrapLoader` Experiment). These Experiments were provided as an intermediate solution after legacy WebExtensions had been deprecated in Thunderbird 78. After 4 years, the Thunderbird team is no longer able to maintain the two mentioned wrapper Experiments and developers should no longer use them.

Wrapped WebExtensions have a background script similar to the following:

```javascript
await messenger.WindowListener.registerDefaultPrefs(
    "defaults/preferences/prefs.js"
);
await messenger.WindowListener.registerChromeUrl([
    ["content",  "myaddon",          "chrome/content/"],
    ["resource", "myaddon",          "chrome/"],
    ["locale",   "myaddon", "en-US", "chrome/locale/en-US/"],
    ["locale",   "myaddon", "de-DE", "chrome/locale/de-DE/"],
]);
await messenger.WindowListener.registerOptionsPage(
    "chrome://myaddon/content/options.xhtml"
);
await messenger.WindowListener.registerWindow(
    "chrome://messenger/content/messengercompose/messengercompose.xhtml",
    "chrome://myaddon/content/messengercompose.js"
);
await messenger.WindowListener.startListening();
```

#### Update Strategy

{% content-ref url="/pages/DQ0DpgAecSaRAoK61cHW" %}
[Convert wrapped WebExtensions to modern WebExtensions](/add-ons/updating/historical-overview/wrapped-to-modern)
{% endcontent-ref %}
{% endtab %}

{% tab title="Legacy WebExtension" %}
Status: **Deprecated in Thunderbird 78.**

Legacy WebExtensions are legacy overlay extensions or legacy bootstrap extensions wrapped inside a WebExtension.

They use a JSON manifest (`manifest.json`) with the `legacy` key (which has been deprecated in Thunderbird 78):

```javascript
{
  "manifest_version": 2,
  "applications": {
    "gecko": {
      "id": "myextension@sample.extensions.thunderbird.net",
      "strict_min_version": "68.0"
    }
  },
  "name": "Extension",
  "description": "Does a thing",
  "version": "2.0",
  "icons": {
    "32": "content/icon32x32.png"
  },
  "legacy": {
    "type" : "bootstrap",
    "options": {
      "page": "chrome://myextension/content/options.xul",
      "open_in_tab": true
    }
  }
}
```

#### Update Strategy

{% content-ref url="/pages/DTjHmbwJ51QK1bJWb9zY" %}
[Convert legacy WebExtensions to modern WebExtensions](/add-ons/updating/historical-overview/legacy-to-modern)
{% endcontent-ref %}

{% content-ref url="/pages/-LmPscFTyqiM9ltgchMv" %}
[Adapt to Changes in Thunderbird 69-78](/add-ons/updating/tb78/changes)
{% endcontent-ref %}
{% endtab %}

{% tab title="Legacy Bootstrap Extension" %}
Status: **Deprecated in Thunderbird 68.**

This type of extension uses a bootstrap file (`bootstrap.js`) as an entry point to the extension. The file defines four methods (`install`, `uninstall`, `startup`, and `shutdown`) from which all extension behaviour is controlled. These extensions can be installed/uninstalled and enabled/disabled without restarting Thunderbird, so they are sometimes called "restartless" extensions. They use an RDF manifest (`install.rdf`).

#### Update Strategy

It is recommended to update legacy bootstrap extensions to legacy WebExtensions first, before converting them to modern WebExtension. The update guide assumes, the extension is currently compatible with Thunderbird 60. If that is not the case, you can find further update instructions [here](https://wiki.mozilla.org/Thunderbird/Add-ons_Guide_57).

{% content-ref url="/pages/-LfUZCpUvryuAmXOkWlj" %}
[Convert legacy bootstrapped extension to legacy WebExtension](/add-ons/updating/historical-overview/bootstrapped)
{% endcontent-ref %}

{% content-ref url="/pages/-Lc5wk5ug7\_NLqWCxYHg" %}
[Adapt to Changes in Thunderbird 61-68](/add-ons/updating/tb68/changes)
{% endcontent-ref %}
{% endtab %}

{% tab title="Legacy Overlay Extension" %}
Status: **Deprecated in Thunderbird 68.**

The original type of extension for Thunderbird and Firefox, using documents that overlay the Thunderbird UI, adding to and modifying it. These extensions use an RDF manifest (`install.rdf`) and require a restart of Thunderbird for installation/uninstallation, upgrading/downgrading and enabling/disabling.

#### Update Strategy

It is recommended to update legacy overlay extensions to legacy WebExtensions first, before converting them to modern WebExtensions. The update guide assumes, the extension is currently compatible with Thunderbird 60. If that is not the case, you can find further update instructions [here](https://wiki.mozilla.org/Thunderbird/Add-ons_Guide_57).

{% content-ref url="/pages/-Lc5vkKneusydLgduFSY" %}
[Convert legacy overlay extension to legacy WebExtension](/add-ons/updating/historical-overview/overlays)
{% endcontent-ref %}

{% content-ref url="/pages/-Lc5wk5ug7\_NLqWCxYHg" %}
[Adapt to Changes in Thunderbird 61-68](/add-ons/updating/tb68/changes)
{% endcontent-ref %}
{% endtab %}
{% endtabs %}


# Convert wrapped WebExtensions to modern WebExtensions

After legacy WebExtensions had been deprecated in Thunderbird 78, the Thunderbird team provided two so-called wrapper Experiments (the `WindowListener` Experiment and the `BootstrapLoader` Experiment), which re-implemented the loading framework of legacy extensions and required only little changes for add-ons to be usable in Thunderbird 78. This mechanism was intended as an intermediate solution.

This document describes how to remove the wrapper Experiment and how to properly convert a legacy extension to a modern WebExtension.

If you need any help, get in touch with the add-on developer community:

{% content-ref url="/pages/-M4A1EuXcWzTw1\_TLhht" %}
[Developer Community](/add-ons/community)
{% endcontent-ref %}

Converting a wrapped WebExtension into a modern WebExtension will be a complex task: almost all interactions with Thunderbird will need to be re-written to use the new APIs. If these APIs are not yet sufficient for your add-on, you may even need to implement additional Experiment APIs yourself. Don't worry though: you can find information on all aspects of the migration process below, including links to many advanced topics.

{% hint style="warning" %}
Before working on an update, it is advised to read some information about the WebExtension technology first. Our [Extension guide](/add-ons/mailextensions) and our ["Hello World" Extension Tutorial](/add-ons/hello-world-add-on) are good starting points.
{% endhint %}

{% hint style="warning" %}
The guide assumes that the background script is loaded [as a module](/add-ons/mailextensions#background-page).
{% endhint %}

Wrapped WebExtensions have a background script similar to the following:

```javascript
await messenger.WindowListener.registerDefaultPrefs(
    "defaults/preferences/prefs.js"
);
await messenger.WindowListener.registerChromeUrl([
    ["content",  "myaddon",          "chrome/content/"],
    ["resource", "myaddon",          "chrome/"],
    ["locale",   "myaddon", "en-US", "chrome/locale/en-US/"],
    ["locale",   "myaddon", "de-DE", "chrome/locale/de-DE/"],
]);
await messenger.WindowListener.registerOptionsPage(
    "chrome://myaddon/content/options.xhtml"
);
await messenger.WindowListener.registerWindow(
    "chrome://messenger/content/messengercompose/messengercompose.xhtml",
    "chrome://myaddon/content/messengercompose.js"
);
await messenger.WindowListener.startListening();
```

## Step 1: Replace `registerDefaultPrefs()`

Most legacy extensions stored their preferences in an `nsIPrefBranch`, and the `registerDefaultPrefs()` function loaded a JavaScript file with default preference values. An example default preference file could look like this:

```javascript
pref("extensions.myaddon.enableDebug", false);
pref("extensions.myaddon.retries", 5);
pref("extensions.myaddon.greeting", "Hello");
```

This file and the associated call to `registerDefaultPrefs()` can be removed, and the default values must be set in the background script through the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment:

```javascript
const DEFAULTS = {
    enableDebug: false,
    retries: 5,
    greeting: "Hello",
}
for (let [prefName, defaultValue] of Object.entries(DEFAULTS)) {
    await browser.LegacyPrefs.setDefaultPref(
        `extensions.myaddon.${prefName}`,
        defaultValue
    );
}
```

We can now use the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment to access existing preferences, for example the preference entry at `extensions.myaddon.enableDebug` can be read from any WebExtension script via:

```javascript
let enableDebug = await browser.LegacyPrefs.getPref("extensions.myaddon.enableDebug");
```

Modern WebExtension should eventually use [browser.storage.local.\*](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local) for their preferences, but to simplify the conversion process, we will keep using the `nsIPrefBranch` for now. The very last conversion step will migrate the preferences.

## Step 2: Replace `registerChromeUrl()`

We will keep registering global legacy `chrome://` or `resource://` URLs, but we will use the [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment. Use the `registerGlobalUrls()` function of the [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment instead of the `registerChromeUrl()` function of the wrapper Experiment. For example:

```javascript
browser.LegacyHelper.registerGlobalUrls([
    ["content",  "myaddon",          "chrome/content/"],
    ["resource", "myaddon",          "chrome/"],
    ["locale",   "myaddon", "en-US", "chrome/locale/en-US/"],
    ["locale",   "myaddon", "de-DE", "chrome/locale/de-DE/"],
]);
```

## Step 3: Replace `registerOptionsPage()`

Modern WebExtensions show their options in an HTML page in a tab or in a frame inside the Add-on Manger. The wrapper APIs instead allowed to register a legacy XUL dialog to be opened when the wrench icon in the add-on card of the Add-on Manger was clicked. This has to be removed to allow that wrench icon to show the standard WebExtension HTML options page.

In this step, we will create a menu entry on the `tools` menu to open the XUL options dialog via the [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment:

```javascript
browser.menus.create({
    id: "oldOptions",
    contexts: ["tools_menu"],
    title: "Old XUL options dialog",
    onclick: () => browser.LegacyHelper.openDialog(
        "XulAddonOptions",
        "chrome://myaddon/content/options.xhtml"
    )
})
```

This will be removed after the XUL options dialog has been converted to a standard WebExtension HTML options page.

## Step 4: Remove the wrapper API

This step will interrupt the main functionality of your add-on. Remove the registration for the wrapper Experiment from `manifest.json`, remove its implementation and schema files and any usage from your background script. The only remaining working part of your add-on should now be your XUL options dialog.

**Please continue at** [**step 5**](/add-ons/updating/historical-overview/legacy-to-modern#step-5-converting-locale-files) **of the conversion from legacy WebExtensions to modern WebExtensions.**


# Convert legacy WebExtensions to modern WebExtensions

{% hint style="warning" %}
We do not suggest to convert older legacy bootstrapped extensions or legacy overlay extensions (as used in Thunderbird 60) directly to modern WebExtensions. They should first be converted to legacy WebExtensions.
{% endhint %}

If you need any help, get in touch with the add-on developer community:

{% content-ref url="/pages/-M4A1EuXcWzTw1\_TLhht" %}
[Developer Community](/add-ons/community)
{% endcontent-ref %}

Converting a legacy WebExtension into a modern WebExtension will be a complex task: almost all interactions with Thunderbird will need to be re-written to use the new WebExtension APIs. If these APIs are not yet sufficient for your add-on, you may even need to implement additional Experiment APIs yourself. Don't worry though: you can find information on all aspects of the migration process below, including links to many advanced topics.

{% hint style="warning" %}
Before working on an update, it is advised to read some information about the WebExtension technology first. Our [Extension guide](/add-ons/mailextensions) and our ["Hello World" Extension Tutorial](/add-ons/hello-world-add-on) are good starting points.
{% endhint %}

{% hint style="warning" %}
Please add a background script to your extension, which will be needed during the update process. The guide assumes that the background script is loaded [as a module](/add-ons/mailextensions#background-page).
{% endhint %}

## Step 1: Dropping the legacy key

The technical conversion from a legacy WebExtension to a modern WebExtension is simple: drop the `legacy` key from the `manifest.json` file.

Your add-on should now install in current versions of Thunderbird without issues, but it will not yet do anything, because the `chrome.manifest` file is no longer read.

## Step 2: Replace the `chrome.manifest` file

The most common entries in the `chrome.manifest` file are listed below:

```
content    myaddon                 chrome/content/
resource   myaddon                 chrome/
locale     myaddon   en-US         chrome/locale/en-US/
locale     myaddon   de-DE         chrome/locale/de-DE/

skin       myaddon   classic/1.0   chrome/skin/classic/

style      chrome://messenger/content/activity.xul   chrome://myaddon/skin/myaddon.css
overlay    chrome://messenger/content/messenger.xul  chrome://myaddon/content/messenger.xul
```

#### **content, resource and locale**

These entries registered global URLs used by the extension to access its assets. Use the [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment to register `content`, `resource` and `locale` entries. To replicate the entries shown in the previous example, add the following to your background script:

```javascript
await browser.LegacyHelper.registerGlobalUrls([
  ["content",  "myaddon", "chrome/content/"],
  ["resource", "myaddon", "chrome/"],
  ["locale",   "myaddon", "en-US", "chrome/locale/en-US/"],
  ["locale",   "myaddon", "de-DE", "chrome/locale/de-DE/"],  
]);
```

There are no direct equivalents to manifest flags, so add-ons now need to provide their own mechanisms to switch code or resources depending on the runtime environment. Relevant information is accessible through the [runtime API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/).

#### **skin**

This entry type is no longer supported, it has to be replaced by a `resource://` URL. In the above example we had the following `skin` definition:

```
skin       myaddon   classic/1.0   /chrome/skin/classic/
```

The `skin` folder is a subfolder of `/chrome/`, which is already available as a `resource://` URL. We can therefore replace all usages of

```
chrome://myaddon/skin/*
```

by

```
resource://myaddon/skin/classic/*
```

#### style

This entry is no longer supported, it has to be replaced by the [LegacyCSS](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyCSS) Experiment. To replicate the `style` entry shown in the previous example, add the following to your background script:

{% hint style="warning" %}
All `*.xul` files have been renamed to `*.xhtml` files in recent versions of Thunderbird! Still using `*.xul` files is unsupported and will cause issues.
{% endhint %}

```javascript
// Define all CSS files for core windows.
let files = {
   "chrome://messenger/content/activity.xhtml": "style.css"
}

// Inject CSS into all open windows during add-on start (if any).
for (let [url, file] of Object.entries(files) ) {
    messenger.LegacyCSS.inject(url, file);
}

// Listen for opened windows and inject CSS.
messenger.LegacyCSS.onWindowOpened.addListener((url) => {
    if (files.hasOwnProperty(url)) {
        messenger.LegacyCSS.inject(url, files[url]);
    }
});
```

This should only be a temporary step. After the initial conversion from a `style` entry to using the [LegacyCSS](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyCSS) Experiment, the required styles should be applied by using [standard WebExtension theming support](/add-ons/web-extension-themes).

#### **overlay**

This entry type is no longer supported. Replacing it will be the main conversion work, which is described in [step 7](#step-7-creating-missing-ui-entry-points-and-apis-as-experiments) and later.

#### interfaces, component, contract, category

These entries are no longer supported.

## Step 3: Replace the `/defaults/preferences/` folder

Most legacy extensions stored their preferences in an `nsIPrefBranch`, and the `/defaults/preferences/` folder contained JavaScript files with default preference values. An example default preference file could look like this:

```javascript
pref("extensions.myaddon.enableDebug", false);
pref("extensions.myaddon.retries", 5);
pref("extensions.myaddon.greeting", "Hello");
```

This file can be removed, and the default values must be set in the background script through the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment:

```javascript
const DEFAULTS = {
    enableDebug: false,
    retries: 5,
    greeting: "Hello",
}
for (let [prefName, defaultValue] of Object.entries(DEFAULTS)) {
    await browser.LegacyPrefs.setDefaultPref(
        `extensions.myaddon.${prefName}`,
        defaultValue
    );
}
```

We can now use the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment to access existing preferences, for example the preference entry at `extensions.myaddon.enableDebug` can be read from any WebExtension script via:

```javascript
let enableDebug = await browser.LegacyPrefs.getPref("extensions.myaddon.enableDebug");
```

Modern WebExtension should eventually use [browser.storage.local.\*](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local) for their preferences, but to simplify the conversion process, we will keep using the `nsIPrefBranch` for now. The very last conversion step will migrate the preferences.

## Step 4: The XUL options dialog

The XUL options dialog is no longer registered, after the `legacy` key has been removed from `manifest.json`. We will use the [LegacyHelper ](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper)Experiment to open the XUL options dialog via a menu entry in the `tools` menu. Add the following to your background script:

```javascript
browser.menus.create({
    id: "oldOptions",
    contexts: ["tools_menu"],
    title: "Old XUL options dialog",
    onclick: () => browser.LegacyHelper.openDialog(
        "XulAddonOptions",
        "chrome://myaddon/content/options.xhtml"
    )
})
```

This will be removed after the XUL options dialog has been converted to a standard WebExtension HTML options page.

## Step 5: Converting locale files

Even though the [LegacyHelper](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyHelper) Experiment allows to register legacy locales, the technology itself is deprecated: WebExtension HTML pages cannot access DTD or property files. Instead, they use the [i18n API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n) to access locales stored in simple JSON files.

The [localeConverter.py](https://github.com/thunderbird/webext-support/tree/master/tools/locale-converter) python script will do most of the work to convert your locale files (DTD and property files) into the new JSON format.

The new locale data can be accessed from any WebExtension script:

```javascript
browser.i18n.getMessage("a-locale-string");
```

## Step 6: Converting the XUL options page

Instead of a XUL dialog, WebExtensions use an HTML page for their options page, which will be accessible to the user through the add-on manager. The page is registered in `manifest.json`:

```javascript
"options_ui": {
  "page": "options.html"
}
```

JavaScript loaded by that `options.html` document can access all WebExtension APIs in the same way as for example the background script.

In this step the old XUL options dialog has to be re-created as an HTML page, using only HTML elements, JavaScript and CSS. It is no longer possible to use XUL elements. Some custom elements and 3rd party libraries to simplify this step can be found in the [webext-support](https://github.com/thunderbird/webext-support/tree/master/ui) repository.

It may help during development, that the old XUL options page can still be opened through the `tools` menu.

### Localisation

There is no automatic replacement of locale placeholder entities like `&myLocaleIdentifier;` in WebExtension HTML files any more. Instead, you can use placeholders like `__MSG_myLocaleIdentifier__` in your markup and include the [i18n.mjs](https://github.com/thunderbird/webext-support/tree/master/modules/i18n) module and automatically replace all `__MSG_*__` locale placeholders on page load.

```javascript
import * as i18n from "i18n.mjs"

document.addEventListener('DOMContentLoaded', () => {
  i18n.localizeDocument();
}, { once: true });
```

The script is using the `i18n` API to read the modern JSON locale files created in the previous step.

### Alternative for `preferencesBindings.js`

The legacy XUL options page used a framework to automatically load and save preference values, controlled by the [preferencesBindings.js](https://searchfox.org/mozilla-central/rev/6e6265bd607cbe4c96e714f86d3d9e36620f63d6/toolkit/content/preferencesBindings.js) script. That automatism does not exist for HTML option pages. But it is possible to implement a similar mechanism using a `data-preference` attribute:

```html
<div>
  <input type="checkbox" id="debug" data-preference="enableDebug"/>
  <label for="debug">__MSG_debug.label__</label>
</div>
```

We can loop over all elements which have such an attribute, and load their value from storage. Additionally we can attach an event listener to store the value after the input field has been changed by the user:

```javascript
let prefElements = document.querySelectorAll('[data-preference]');
for (let prefElement of prefElements) {
    let value = await browser.LegacyPrefs.getPref(
        `extensions.myaddon.${prefElement.dataset.preference}`
    );
    
    // handle checkboxes
    if (prefElement.tagName == "INPUT" && prefElement.type == "checkbox") {
        if (value == true) {
            prefElement.setAttribute("checked", "true");
        }
        // enable auto save
        prefElement.addEventListener("change", () => {
            browser.LegacyPrefs.setPref(
                `extensions.myaddon.${prefElement.dataset.preference}`,
                prefElement.checked
            );
        })
    }
}
```

## Step 7: Find matching WebExtension entry points and WebExtension APIs

Now it's time to find out how your add-on can leverage the existing WebExtension entry points. What UI elements did you use? Do any of the [supported WebExtension UI elements](/add-ons/mailextensions/supported-ui-elements) fit?

Even if they are not a perfect match, try to replace as many of your legacy UI entry points by WebExtension entry points:

* [menus and context menus](/add-ons/mailextensions/supported-ui-elements#menu-items)
* action buttons (normal or [menu-typed](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/menuActionButton))
* action popus
* message display scripts ([manipulate/overlay the displayed message](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/messageDisplayScript.pdfPreview))
* compose scripts ([interact with the editor, manipulate the DOM, the selection, the cursor position](https://github.com/jobisoft/quicktext/blob/WebExt/scripts/compose.js))
* content tabs
* content popup windows\\

  ```javascript
  let window = await messenger.windows.create({
      height: 400,
      width: 500,
      url: "/path/from/root/of/addon/to/dialog.html",
      type: "popup"
  });
  ```
* [native messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging)

The goal of this step is to re-create as much of the functionality of your add-on by using only WebExtension technology. Browse through the list of [supported WebExtension APIs](https://webextension-api.thunderbird.net/) to see if any of them provide what is needed by your add-on. Check available [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API), there is a high chance to find simple replacements for complicated XPCOM calls:

* [play sounds](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)
* [localize plural rules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules)

Do not hesitate to ask in [our community channels](/add-ons/community) for help.

## Step 8: Creating missing UI entry points and APIs as Experiments

If certain crucial features of your add-on cannot be implemented using the available WebExtension APIs or Web APIs, you can create your own Experiment APIs.

As Experiments usually run in the main process and have unrestricted access to any aspect of Thunderbird, they are expected to require updates for each new version of Thunderbird. To reduce the maintenance burden in the future, it is in your own interest to use Experiment APIs only to the extent necessary for the add-on.

Best practice: Try to write APIs that would be useful for a wide range of add-ons, not just the one you're porting. That way, you can later on propose the API you designed for inclusion in Thunderbird, with your add-on serving as the reference implementation. If your APIs become a part of Thunderbird, you no longer need to maintain them as part of the add-on.

A basic description of Experiment APIs can be found in a separate article:

{% content-ref url="/pages/-MAkO\_iyb2AT8xfUCXK3" %}
[Introducing Experiments](/add-ons/mailextensions/experiments)
{% endcontent-ref %}

### Overlay methods

Manipulating Thunderbirds UI through Experiments is historically referred to as *overlaying*. The basic principle of overlaying is to get hold of a native Thunderbird window object and to add or remove DOM elements (or monkey-patch functions living inside that native window to change some behaviour).

Adding or removing DOM elements can be achieved through JavaScript (note that Thunderbird sometimes still uses non-standard XUL elements, which are however slowly replaced by standard HTML elements):

```javascript
const { document } = window;
const rows = document.getElementById("attachemnt-rows");
const urlLabel = document.createXULElement("label");
urlLabel.setAttribute("class", "text-link");
urlLabel.setAttribute("value", url);
urlLabel.setAttribute("tooltiptext", url);
rows.appendChild(urlLabel);
```

A more detailed explanation of the shown code snippet is beyond the scope of this guide. It is advised to study [Thunderbird's code](https://searchfox.org/comm-central/search?q=symbol:%23createXULElement\&redirect=false) for more details.

Generating complex and nested DOM elements through JavaScript can become cumbersome, and legacy add-ons were able to provide a simple DOM string instead. This is still possible by using the following helper function:

<pre class="language-javascript"><code class="lang-javascript"><strong>// Helper function to inject a legacy XUL string into the DOM of Thunderbird.
</strong>// All injected elements will get the data attribute "data-extension-injected"
// set to the extension id, for easy removal.
const injectElements = function (extension, window, xulString, debug = false) {
  function checkElements(stringOfIDs) {
    let arrayOfIDs = stringOfIDs.split(",").map((e) => e.trim());
    for (let id of arrayOfIDs) {
      let element = window.document.getElementById(id);
      if (element) {
        return element;
      }
    }
    return null;
  }

  function localize(entity) {
    let msg = entity.slice("__MSG_".length, -2);
    return extension.localeData.localizeMessage(msg);
  }

  function injectChildren(elements, container) {
    if (debug) console.log(elements);

    for (let i = 0; i &#x3C; elements.length; i++) {
      if (
        elements[i].hasAttribute("insertafter") &#x26;&#x26;
        checkElements(elements[i].getAttribute("insertafter"))
      ) {
        let insertAfterElement = checkElements(
          elements[i].getAttribute("insertafter")
        );

        if (debug)
          console.log(
            elements[i].tagName +
            "#" +
            elements[i].id +
            ": insertafter " +
            insertAfterElement.id
          );
        if (
          debug &#x26;&#x26;
          elements[i].id &#x26;&#x26;
          window.document.getElementById(elements[i].id)
        ) {
          console.error(
            "The id &#x3C;" +
            elements[i].id +
            "> of the injected element already exists in the document!"
          );
        }
        elements[i].setAttribute("data-extension-injected", extension.id);
        insertAfterElement.parentNode.insertBefore(
          elements[i],
          insertAfterElement.nextSibling
        );
      } else if (
        elements[i].hasAttribute("insertbefore") &#x26;&#x26;
        checkElements(elements[i].getAttribute("insertbefore"))
      ) {
        let insertBeforeElement = checkElements(
          elements[i].getAttribute("insertbefore")
        );

        if (debug)
          console.log(
            elements[i].tagName +
            "#" +
            elements[i].id +
            ": insertbefore " +
            insertBeforeElement.id
          );
        if (
          debug &#x26;&#x26;
          elements[i].id &#x26;&#x26;
          window.document.getElementById(elements[i].id)
        ) {
          console.error(
            "The id &#x3C;" +
            elements[i].id +
            "> of the injected element already exists in the document!"
          );
        }
        elements[i].setAttribute("data-extension-injected", extension.id);
        insertBeforeElement.parentNode.insertBefore(
          elements[i],
          insertBeforeElement
        );
      } else if (
        elements[i].id &#x26;&#x26;
        window.document.getElementById(elements[i].id)
      ) {
        // existing container match, dive into recursively
        if (debug)
          console.log(
            elements[i].tagName +
            "#" +
            elements[i].id +
            " is an existing container, injecting into " +
            elements[i].id
          );
        injectChildren(
          Array.from(elements[i].children),
          window.document.getElementById(elements[i].id)
        );
      } else {
        // append element to the current container
        if (debug)
          console.log(
            elements[i].tagName +
            "#" +
            elements[i].id +
            ": append to " +
            container.id
          );
        elements[i].setAttribute("data-extension-injected", extension.id);
        container.appendChild(elements[i]);
      }
    }
  }

  if (debug) console.log("Injecting into root document:");
  let localizedXulString = xulString.replace(
    /__MSG_(.*?)__/g,
    localize
  );
  injectChildren(
    Array.from(
      window.MozXULElement.parseXULToFragment(localizedXulString, []).children
    ),
    window.document.documentElement
  );
};
</code></pre>

The function supports XUL strings with WebExtension `__MSG_*__` locale placeholders. It also supports `insertbefore` or `insertafter` attributes, to specify where the element should be added. If an existing `id` is specified, the element will be added as a child inside the existing element:

```javascript
injectElements(extension, window, `
  <tab insertafter="QuotaTab" id="FlagsTab" hidefor="rss,nntp" label="__MSG_folderflags.tab.label__"/>
  <vbox insertafter="quotaPanel" id="folderflags-tabPanel" align="start">
      <hbox align="center" valign="middle">
          <label>__MSG_folder__</label><label id="folderflags-folderName" />
      </hbox>
      <vbox id="folderflags-flaglist">
      </vbox>
  </vbox>
`);
```

A more detailed explanation of the shown code snippet is beyond the scope of this guide. The shown code is taken from the [FolderFlags](https://github.com/voccs/folderflags) add-on. The [Restart Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.restart) is also using this method.

### Overlay strategies

In order to add custom UI entry points, the add-on has to manipulate the native window object of all already open windows/tabs and also any window/tab which is opened in the future. The two most common concepts to achieve this are described below.

#### Detect open windows/tabs through WebExtension APIs

This is the preferred method, since the add-on can leverage existing WebExtension APIs and reduces the amount of code which has to be maintained by the add-on developer. For example, to manipulate all message display tabs, the following code can be used in the WebExtension background script:

```javascript
// Handle all already open/displayed messages.
let tabs = await browser.tabs.query({ type: ["messageDisplay", "mail"] })
for (let tab of tabs) {
  let message = await browser.messageDisplay.getDisplayedMessage(tab.id);
  if (message) {
    await removeAttachmentsIfJunk(tab, message);
  }
}

// React on any new message being displayed.
browser.messageDisplay.onMessageDisplayed.addListener(removeAttachmentsIfJunk);

async function removeAttachmentsIfJunk(tab, message) {
  // Only remove attachments, if message is junk.
  if (!message.junk) {
    return;
  }

  // browser.MessageDisplayAttachment.removeAttachments is an Experiment API,
  // which operates on the given tab and removes all displayed attachments.
  await browser.MessageDisplayAttachment.removeAttachments(tab.id);
}
```

The following API implementation is based on the [Remove Attachments If Junk Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.removeAttachmentsIfJunk):

```javascript
var MessageDisplayAttachment = class extends ExtensionCommon.ExtensionAPI {
  getAPI(context) {

    // Get the native about:message window from the tabId.
    function getMessageWindow(tabId) {
      let { nativeTab } = context.extension.tabManager.get(tabId);
      if (nativeTab instanceof Ci.nsIDOMWindow) {
        return nativeTab.messageBrowser.contentWindow
      } else if (nativeTab.mode && nativeTab.mode.name == "mail3PaneTab") {
        return nativeTab.chromeBrowser.contentWindow.messageBrowser.contentWindow
      } else if (nativeTab.mode && nativeTab.mode.name == "mailMessageTab") {
        return nativeTab.chromeBrowser.contentWindow;
      }
      return null;
    }

    return {
      MessageDisplayAttachment: {
        removeAttachments: async function (tabId) {
          let window = getMessageWindow(tabId);
          if (!window) {
            return;
          }

          // The following code depends on internal Thunderbird methods and may
          // change, which will break the add-on.
          for (let index = window.currentAttachments.length; index > 0; index--) {
            let idx = index - 1;
            window.currentAttachments.splice(idx);
          }

          await window.ClearAttachmentList();
          window.gBuildAttachmentsForCurrentMsg = false;
          await window.displayAttachmentsForExpandedView();
          window.gBuildAttachmentsForCurrentMsg = true;
        },
      },
    };
  }
};
```

An example which manipulates the main window using the same strategy is the [Restart Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.restart).

#### Detect open windows/tabs through the Experiment

If the window of interest is not supported by WebExtension APIs, it is not detectable through WebExtension APIs and the detection code has to live inside an Experiment.

The following example is based on the [Activity Manager Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.activityManager). Its background script triggers the Experiment to register a global window listener, which manipulates the window of interest:

```javascript
await browser.ActivityManager.registerWindowListener();
```

The implementation of the Experiment could be:

```javascript
class ActivityManager extends ExtensionCommon.ExtensionAPI {
  getAPI(context) {
    return {
      // This key must match the class name.
      ActivityManager: {
        registerWindowListener() {
          // Register a listener for newly opened activity windows.
          ExtensionSupport.registerWindowListener(context.extension.id, {
            chromeURLs: [
              "chrome://messenger/content/activity.xhtml",
            ],
            onLoadWindow(window) {
              // Add our event listener.
              window._exampleAddOnClickHandler = (e) => {
                console.log("The button was clicked, let's do something!")
              }
              window.document.getElementById("clearListButton").addEventListener(
                "click",
                window._exampleAddOnClickHandler
              );
            },
          });
        },
      },
    };
  }

  onShutdown(isAppShutdown) {
    if (isAppShutdown) {
      return;
    }

    // Remove our event listener.
    const { extension } = this;
    for (let window of ExtensionSupport.openWindows) {
      if ([
        "chrome://messenger/content/activity.xhtml",
      ].includes(window.location.href)) {
        // Remove our event listener.
        window.document.getElementById("clearListButton").removeEventListener(
          "click",
          window._exampleAddOnClickHandler
        );
        delete window._exampleAddOnClickHandler;
      }
    }

    // Unregister our listener for newly opened windows.
    ExtensionSupport.unregisterWindowListener(extension.id);
  }
}
```

### Custom WebExtension events

So far we have only discussed Experiments which perform a direct action *inside* the Experiment implementation. To move as much code out of the Experiment implementation, we can trigger a standard WebExtension event and let any follow-up action be handled by the WebExtension.

A common use case is a custom button added to Thunderbird's UI through an Experiment. The action which is triggered by clicking on the button should not be handled in the Experiment, but by the WebExtension background script, which has registered a listener for that button being pressed. For this to work we need to define an `EventEmitter` in the Experiment:

```javascript
// An EventEmitter has the following basic functions:
// * EventEmitter.on(emitterName, callback): Registers a callback for a
//   custom emitter.
// * EventEmitter.off(emitterName, callback): Unregisters a callback for a
//   custom emitter.
// * EventEmitter.emit(emitterName, ...args): Emit a custom emitter, all
//   provided args will be forwarded to the registered callbacks.
const emitter = new ExtensionCommon.EventEmitter();
```

The boilerplate, which connects the internals of a WebExtension event to the defined `EventEmitter`, is the added `EventManger` in lines 5-21 of the following example. The glue part to actually trigger the event is

```javascript
emitter.emit("activity-manager-command", e.clientX, e.clientY);
```

in line 31:

{% code lineNumbers="true" %}

```javascript
getAPI(context) {
  return {
    ActivityManager: {
      
      onCommand: new ExtensionCommon.EventManager({
        context,
        name: "ActivityManager.onCommand",
        register(fire) {
          function callback(event, x, y) {
            // Let the event return the coordinates of the click.
            return fire.async(x, y);
          }

          emitter.on("activity-manager-command", callback);
          return function () {
            emitter.off("activity-manager-command", callback);
          };
        },
      }).api(),

      registerWindowListener() {
        // Register a listener for newly opened activity windows.
        ExtensionSupport.registerWindowListener(context.extension.id, {
          chromeURLs: [
            "chrome://messenger/content/activity.xhtml",
          ],
          onLoadWindow(window) {
            // Add our event listener.
            window._exampleAddOnClickHandler = (e) => {
              console.log("The button was clicked, let's do something!")
              emitter.emit("activity-manager-command", e.clientX, e.clientY);
            }
            window.document.getElementById("clearListButton").addEventListener(
              "click",
              window._exampleAddOnClickHandler
            );
          },
        });
      },

    },
  };
}
```

{% endcode %}

The callback of the `EventEmitter` (line 9) has the `x` and `y` parameters, which are passed through to `fire.async()`, transmitting them to the WebExtension:

```javascript
browser.ActivityManager.onCommand.addListener((x, y) => {
  console.log(`Received an onCommand event with parameters: (${x},${y}).`)
});
```

A working implementation of this example can be found in the [Activity Manager Experiment Example](https://github.com/thunderbird/webext-examples/tree/master/manifest_v2/experiment.activityManager).

## Step 9: Migrate Preferences

So far we stored our preferences in an `nsIPrefBranch`, which could be accessed from WebExtension scripts through the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment, and from other Experiments directly through the `nsIPrefBranch`.

WebExtensions should eventually store their preferences in [browser.storage.local.\*](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local), which removes the data when the add-on is uninstalled. The user should be able to start fresh by uninstalling and reinstalling an extension, if a specific configuration causes the add-on to malfunction. This is a common pattern, which however does not work for preferences stored in an `nsIPrefBranch,` as they are not cleared on add-on uninstall.

{% hint style="danger" %}
The user actually expects that all his data associated with a certain add-on is removed from the Thunderbird profile, when the add-on is removed. An add-on can of course offer import and export functions.
{% endhint %}

### Accessing preferences in custom Experiments

In order to migrate preferences, your custom Experiments may no longer access the `nsIPrefBranch` directly, as they later cannot access the migrated values in [browser.storage.local.\*](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local). All your custom Experiments must be independent of the used storage. The two most common strategies are outlined below.

#### Passing preferences as function parameters

Consider a simple debug log in an Experiment function, which used to query the `extensions.myaddon.enableDebug` preference directly:

```javascript
fancyExperimentFunction: async function () {
  // Be verbose during development.
  let debug = Services.prefs.getBoolPref("extensions.myaddon.enableDebug");
  if (debug) {
    console.log(`This is a fancy Experiment function`);
  }
  // Do something ...
}
```

The function can receive the debug flag as a parameter:

```javascript
fancyExperimentFunction: async function (debug) {
  // Be verbose during development.
  if (debug) {
    console.log(`This is a fancy Experiment function`);
  }
  // Do something ...
}
```

In the WebExtension script calling that method, we continue (for now) to use the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment to retrieve the value for the `enableDebug` preference before passing it to the Experiment:

```javascript
let debug = await browser.LegacyPrefs.getPref("extensions.myaddon.enableDebug");
await browser.fancyExperiment.fancyExperimentFunction(debug);
```

#### Keeping a local preference cache in the Experiment

Cached preferences can be accessed everywhere inside the Experiment implementation. A simple implementation could be:

```javascript
"use strict";

// Using a closure to not leak anything but the API to the outside world.
(function (exports) {

  const cachedPreferences = new Map();
  const cachedDefaults = new Map();
  
  function getPref(prefName) {
    return cachedPreferences.get(prefName) ?? cachedDefaults.get(prefName)
  }

  class ExperimentWithPreferenceCache extends ExtensionCommon.ExtensionAPI {
    getAPI(context) {
      return {
        ExperimentWithPreferenceCache: {
          updatePreference(prefName, currentValue, defaultValue) {
            // Update the local preference cache, which can be accessed from
            // everywhere inside this Experiment implementation.
            if (currentValue !== null) {
              cachedPreferences.set(prefName, currentValue);
            } else {
              cachedPreferences.delete(prefName);
            }
            if (defaultValue !== null) {
              cachedDefaults.set(prefName, defaultValue);
            }
          },

          fancyFunction: async function () {
            // Be verbose during development if indicated by the cached preference.
            if (getPref("enableDebug")) {
              console.log(`This is a fancy Experiment function`);
            }
            // Do something ...
          }
        },
      };
    }
  };

  // Export the API by assigning it to the exports parameter of the anonymous
  // closure function, which is the global this.
  exports.ExperimentWithPreferenceCache = ExperimentWithPreferenceCache;

})(this)
```

In the background script we have to monitor the `extensions.myaddon.*` preference branch and update the cache if needed. The [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment provides an `onChanged` event for that purpose:

```javascript
// Cache initial values.
for (let [prefName, defaultValue] of Object.entries(DEFAULTS)) {
  let currentValue = await browser.LegacyPrefs.getUserPref(
    `extensions.myaddon.${prefName}`
  );
  await browser.ExperimentWithPreferenceCache.updatePreference(
    prefName,
    currentValue,
    defaultValue,
  );
}

// Update cache if user value changed.
browser.LegacyPrefs.onChanged.addListener(async (prefName, newValue) => {
  await browser.ExperimentWithPreferenceCache.updatePreference(
    prefName,
    newValue,
    null, // default value is not modified
  );
}, "extensions.myaddon.");
```

### Migration strategy

The last step is to move all preferences into [browser.storage.local.\*](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local) and update all WebExtension scripts to no longer use the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment. The [preferences.mjs](https://github.com/thunderbird/webext-support/tree/master/modules/preferences) module can be used as a drop-in replacement for the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment. Add the following to the top of your background script:

```javascript
import * as prefs from "preferences.mjs";

// Migrate preferences from extensions.myaddon.* to local storage.
let migrated = await prefs.getPref("_migrated");
if (!migrated) {
    for (let { prefName } of prefs.getDefaults()) {
        let prefValue = await browser.LegacyPrefs.getUserPref(
            `extensions.myaddon.${prefName}`
        );
        if (prefValue === null) {
            continue;
        }
        console.log(`Migrating extensions.myaddon.${prefName}: ${prefValue}`)
        await prefs.setPref(prefName, prefValue);
        await browser.LegacyPrefs.clearUserPref(
            `extensions.myaddon.${prefName}`
        );
    }
    await prefs.setPref("_migrated", true);
}
```

Move the definition of the `DEFAULTS` object from the top of your background script into your copy of the [preferences.mjs](https://github.com/thunderbird/webext-support/tree/master/modules/preferences) module.

Remove all code which used `browser.LegacyPrefs.setDefaultPref()` and update all other calls to access your preferences through the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment by the matching method of the [preferences.mjs](https://github.com/thunderbird/webext-support/tree/master/modules/preferences) module.

The preference caching mechanism for Experiments can be updated as follows:

```javascript
// Cache initial values.
for (let { prefName, defaultValue } of prefs.getDefaults()) {
  let currentValue = await prefs.getUserPref(prefName);
  await browser.ExperimentWithPreferenceCache.updatePreference(
    prefName,
    currentValue,
    defaultValue,
  );
}

// Update cache if user value changed.
browser.storage.local.onChanged.addListener(async (changes) => {
  for (const prefName of Object.keys(changes)) {
    await browser.ExperimentWithPreferenceCache.updatePreference(
      prefName,
      changes[prefName].newValue,
      null, // default value is not modified
    );    
  }
});
```

Wait about 6-12 months after the migration code has been shipped to your users, before removing the migration code and the [LegacyPrefs](https://github.com/thunderbird/webext-support/tree/master/experiments/LegacyPrefs) Experiment.


# Convert legacy overlay extension to legacy WebExtension

## Switch to JSON manifest

You *must* switch from an RDF manifest (`install.rdf`) to a JSON manifest (`manifest.json`). Here is a basic example. This RDF manifest:

```markup
<?xml version="1.0" encoding="utf-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>myextension@sample.extensions.thunderbird.net</em:id>
    <em:type>2</em:type>
    <em:name>Extension</em:name>
    <em:description>Does a thing!</em:description>
    <em:version>1.0</em:version>
    <em:optionsURL>chrome://myextension/content/options.xul</em:optionsURL>
    <em:optionsType>3</em:optionsType><!-- Options in a tab -->
    <em:iconURL>chrome://myextension/content/icon32x32.png</em:iconURL>
    <em:targetApplication>
      <Description>
        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
        <em:minVersion>60.0</em:minVersion>
        <em:maxVersion>60.*</em:maxVersion>
      </Description>
    </em:targetApplication>
  </Description>
</RDF>
```

Becomes this JSON manifest:

```javascript
{
  "manifest_version": 2,
  "applications": {
    "gecko": {
      "id": "myextension@sample.extensions.thunderbird.net",
      "strict_min_version": "68.0"
    }
  },
  "name": "Extension",
  "description": "Does a thing",
  "version": "2.0",
  "icons": {
    "32": "content/icon32x32.png"
  },
  "legacy": {
    "type": "xul",
    "options": {
      "page": "chrome://myextension/content/options.xul",
      "open_in_tab": true
    }
  }
}
```

Detailed information about the possible config options for `manifest.json` can be found in the [MDN documentation](https://developer.mozilla.org/en/docs/Mozilla/Add-ons/WebExtensions/manifest.json).

The `legacy` key enables Thunderbird’s legacy WebExtension support. Setting the `type` key to `xul` engages the new XUL overlay loader. The overlay loader is a Thunderbird component that takes XUL code as written in an overlay extension and applies it to the UI. In Thunderbird 60, this was a part of the core UI library, but it was removed. We have built a new overlay loader to replace as much of the removed code as possible.

{% hint style="warning" %}
The URL for icons must no longer be full chrome URL as before, but a simple path relative to the root directory of the add-on.
{% endhint %}

The shown example also specifies an optional `options` key to define the options page. The key `open_in_tab` is optional and defaults to a value of`false`. If your old RDF manifest included an `em:optionsType` of 3, you can set `open_in_tab` to `true`, to have your options opened again in a new tab instead of a new window.

{% hint style="info" %}
This example is only in English. You probably want to use translated strings in your manifest. Read [this MDN article about it](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json). Unfortunately that means you now need two sets of translated strings, one (that you already have) for your extension and another for the manifest.
{% endhint %}

**Examples of overlay extension converted like this are:**

* [Open With](https://github.com/darktrojan/openwith/blob/VERSION_6.9/manifest.json)
* [Shrunked Image Resizer](https://github.com/darktrojan/shrunked/blob/VERSION_4.5/manifest.json)

## Notes about overlaying in general

A lot of effort has been done to create the new overlay loader, but still things might not work as before. We are tracking this in bug [1476259](https://bugzilla.mozilla.org/show_bug.cgi?id=1476259).

Overlays in Thunderbird itself (except the calendar extensions) have been removed, so extensions can not overlay the removed Thunderbird overlays any more. For example, if your add-on overlaid `mailWindowOverlay.xul`, that needs to be changed; in this example you most likely need to overlay `messenger.xul` now.

Furthermore, the new overlay loader does not properly support dependencies between overlays in different add-ons. As a result, you should only reference elements from the original document you're overlaying, or other overlays in the same extension. Most notably, you need to switch to non-overlay methods when altering the calendar user interface in the main window, or your add-on will not load reliably.

## Notes about Chrome manifest

The `overlay` and `style` lines in your`chrome.manifest` are now handled by the new overlay loader. You’ll see lines like this in the Error Console:

```
Ignoring unrecognized chrome manifest directive 'overlay'.
```

Those errors come from the old system, which no longer deals with such things. You might see the same line, but regarding `interfaces`.

## Notes about `<script>` tags

`<script>` tags added to an overlay file are now run *after* the application of the entire overlay, regardless of their position in the overlay. This may cause unexpected behaviour if your script previously ran before elements were inserted. For elements with event handlers these event handlers may run when the element is added, and they may fail if they rely on content being set up by a script which now runs after the creation of the element.

You may be used to putting the contents of a script directly in a document. This currently still works, but it may break in the future. **Inline scripts are strongly discouraged.** Use a file instead.


# Convert legacy bootstrapped extension to legacy WebExtension

## Switch to JSON manifest

You *must* switch from an RDF manifest (`install.rdf`) to a JSON manifest (`manifest.json`). Here is a basic example. This RDF manifest:

```markup
<?xml version="1.0" encoding="utf-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>myextension@sample.extensions.thunderbird.net</em:id>
    <em:type>2</em:type>
    <em:bootstrap>true</em:bootstrap>
    <em:name>Extension</em:name>
    <em:description>Does a thing!</em:description>
    <em:version>1.0</em:version>
    <em:optionsURL>chrome://myextension/content/options.xul</em:optionsURL>
    <em:optionsType>3</em:optionsType><!-- Options in a tab -->
    <em:iconURL>chrome://myextension/content/icon32x32.png</em:iconURL>
    <em:targetApplication>
      <Description>
        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
        <em:minVersion>60.0</em:minVersion>
        <em:maxVersion>60.*</em:maxVersion>
      </Description>
    </em:targetApplication>
  </Description>
</RDF>
```

Becomes this JSON manifest:

```javascript
{
  "manifest_version": 2,
  "applications": {
    "gecko": {
      "id": "myextension@sample.extensions.thunderbird.net",
      "strict_min_version": "68.0"
    }
  },
  "name": "Extension",
  "description": "Does a thing",
  "version": "2.0",
  "icons": {
    "32": "content/icon32x32.png"
  },
  "legacy": {
    "type" : "bootstrap",
    "options": {
      "page": "chrome://myextension/content/options.xul",
      "open_in_tab": true
    }
  }
}
```

Detailed information about the possible config options for `manifest.json` can be found in the [MDN documentation](https://developer.mozilla.org/de/docs/Mozilla/Add-ons/WebExtensions/manifest.json).

The `legacy` key enables Thunderbird’s legacy WebExtenion support, for bootstrap extension you have to set the `type` key to `bootstrap`.

{% hint style="warning" %}
The URL for icons must no longer be full chrome URL as before, but a simple path relative to the root directory of the add-on.
{% endhint %}

The shown example also specifies an optional `options` key to define the options page. The key `open_in_tab` is optional and defaults to a value of`false`. If your old RDF manifest included an `em:optionsType` of 3, you can set `open_in_tab` to `true`, to have your options opened again in a new tab instead of a new window.

{% hint style="info" %}
This example is only in English. You probably want to use translated strings in your manifest. Read [this MDN article about it](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization#Internationalizing_manifest.json). Unfortunately that means you now need two sets of translated strings, one (that you already have) for your extension and another for the manifest.
{% endhint %}

## Possible timing issues

The changes made in Thunderbird for bootstrapped add-ons to use `manifest.json` may have changed when your code runs relative to events or notifications you've been listening for.

* Use the window mediator or window watcher services to be notified about opening and closing windows, rather than listening for notifications.
* Wherever you access a window, always check if it has been completely loaded\
  (`document.readyState == "complete"`), or otherwise, wait for the load event.

In the following example, `loadIntoWindow` is waiting for the window to be fully loaded and eventually calls `loadIntoWindowAfterWindowIsReady` to actually do something with it. There is no need to listen to any other load events outside of `loadIntoWindow`. This example also checks the state of already open windows during startup (line 14).

{% code title="bootstrap.js" %}

```javascript
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

function install(data, reason) {
}

function uninstall(data, reason) {
}

function startup(data, reason) {
  // Check if the window we want to modify is already open.
  let windows = Services.wm.getEnumerator("mail:3pane");
  while (windows.hasMoreElements()) {
    let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
    WindowListener.loadIntoWindow(domWindow);
  }

  // Wait for any new windows to open.
  Services.wm.addListener(WindowListener);
}

function shutdown(data, reason) {
  // When the application is shutting down we normally don't have to clean
  // up any UI changes made.
  if (reason == APP_SHUTDOWN) {
    return;
  }

  let windows = Services.wm.getEnumerator("mail:3pane");
  while (windows.hasMoreElements()) {
    let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
    WindowListener.unloadFromWindow(domWindow);
  }

  // Stop listening for any new windows to open.
  Services.wm.removeListener(WindowListener);
}

var WindowListener = {

  async loadIntoWindow(window) {
    console.log("load (1/2): " + window.document.readyState);
    if (window.document.readyState != "complete") {
      // Make sure the window load has completed.
      await new Promise(resolve => {
        window.addEventListener("load", resolve, { once: true });
      });
    }

    this.loadIntoWindowAfterWindowIsReady(window);
  },

  loadIntoWindowAfterWindowIsReady(window) {
    console.log("load (2/2): " + window.document.readyState);
    
    // Check if the opened window is the one we want to modify.
    if (window.document.documentElement
                 .getAttribute("windowtype") === "mail:3pane") {
      let document = window.document;
  
      // Take any steps to add UI or anything to the window
      // document.getElementById() etc. will work here.        
    }    
  },

  unloadFromWindow(window) {
    console.log("unload: " + window.document.readyState);
    let document = window.document;

    // Take any steps to remove UI or anything from the window
    // document.getElementById() etc. will work here.
  },

  // nsIWindowMediatorListener functions
  onOpenWindow(xulWindow) {
    // A new window has opened.
    let domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindow);

    this.loadIntoWindow(domWindow);
  },

  onCloseWindow(xulWindow) {
  },

  onWindowTitleChange(xulWindow, newTitle) {
  },
};

```

{% endcode %}

So you basically have to rename your current `loadIntoWindow` to `loadIntoWindowAfterWindowIsReady` and add the new asynchronous `loadIntoWindow` , to make sure to access the window only after it has been fully loaded.


# Thunderbird Channels

Thunderbird provides releases through four distinct channels, each serving a unique purpose. Below are the channels, their corresponding branches, and a summary of their purpose:

* **Thunderbird Daily (main branch)**: Where development and testing begin, and new features are developed.
* **Thunderbird Beta (beta branch)**: The monthly stabilization release.
* **Thunderbird Release (release branch)**: The official monthly release.
* **Thunderbird ESR (esr\<version> branch)**: The official annual extended support release.

These four channels offer increasing levels of stability, increasing as code moves from Daily to Beta and being the highest in Release and ESR.


# Release Cadence

## Thunderbird Release Train Model

Thunderbird follows a release train model to ensure timely and predictable releases. This approach allows for regular feature rollouts, stability improvements, and bug fixes.

## General Release Cadence

The general release cadence consists of 4-week cycles, with each branch corresponding to a specific release channel. Each branch, starting from main, is based on the previous branch.

![](/files/mHJR9gqvhjXL1Tvjc3iw)

***

## **Release Channels**

### **Thunderbird Daily**

* **Release Timeline**: A new major version of Thunderbird Daily begins every 4 weeks.
* **Releases**: Thunderbird Daily is automatically released on a daily basis from main.

### **Thunderbird Beta**

* **Release Timeline**:
  * A new major version of Thunderbird Beta begins every 4 weeks.
  * 4 weeks after Daily starts for a new version, main is merged into beta.
* **Releases**: Thunderbird Beta for this version is released weekly for the next 3 weeks.
  * **Beta 1**: Includes the merged code.
  * **Beta 2 and above**: Include any new uplifts.

### **Thunderbird Release**

* **Release Timeline**:
  * A new major version of Thunderbird Release begins every 4 weeks.
  * 3 weeks after Beta starts for a new version, beta is merged into release.
  * 1 week after this merge, the monthly Thunderbird Release is published.
* **Releases**: Thunderbird Release is released every 4 weeks, with a point release, as needed, 2 weeks after release.

### **Thunderbird ESR (Extended Support Release)**

* **Release Timeline**: The code from the release branch is merged into esr\<version> once a year.
* **Releases**: Thunderbird ESR is released every year, typically in July, with point releases as needed every 2 weeks.

***

## **Stabilization Milestones**

### **Daily Soft Code Freeze**

* A 1-week soft code freeze occurs for main prior to merging into beta.
* During this time:
  * Risky code should not land in main.
  * Features controlled by a feature flag that were disabled in Daily should not be enabled.

### **Pre-Merge Reviews**

* Prior to both merges, reviews are conducted for all changes included in the merge.
* Code may be backed out if:
  * It is deemed too risky.
  * It introduces new crashes or high-severity bugs.
  * It reduces the overall quality of Thunderbird.
  * It introduces a bug and a follow-up fix cannot be provided in a reasonable timeframe.

***

## **Sample Release Timeline for Thunderbird 154**

| Milestone                                | Date          |
| ---------------------------------------- | ------------- |
| Thunderbird 154.0a1 starts               | Jun 22        |
| Thunderbird 154.0a1 soft code freeze     | Jul 13–Jul 20 |
| Thunderbird 154.0a1 pre-merge review     | Jul 16        |
| Thunderbird merge 154.0a1 main → beta    | Jul 20        |
| Thunderbird 154.0b1                      | Jul 22        |
| Thunderbird 154.0b2                      | Jul 29        |
| Thunderbird 154.0b3                      | Aug 5         |
| Thunderbird 154.0b4 pre-merge review     | Aug 6         |
| Thunderbird 154.0b4                      | Aug 12        |
| Thunderbird merge 154.0b4 beta → release | Aug 12        |
| Thunderbird 154.0                        | Aug 18        |
| Thunderbird 154.0.1                      | Sep 1         |

***

## **Thunderbird Milestones Calendar**

You can view Thunderbird release milestones on the [Thunderbird Release Calendar](https://calendar.google.com/calendar/u/0/embed?src=c_f7b7f2cea6f65593ef05afaf2abfcfb48f87e25794468cd4a19d16495d17b6d1@group.calendar.google.com).


# Uplifting Fixes

Some fixes are more urgent than others and "riding the train" will take too long to get the fix to users. This is where uplifts come into play. Uplifts allow a fix to be fast-tracked into the Beta, Release, or ESR channels.

***

## **Uplifts to Beta, Release, and ESR**

Uplifts to Beta, Release, and ESR are limited to high-impact bug fixes. Features and enhancements are not candidates for uplift.

**Patches nominated for uplift should:**

* Be limited to stability, security, or high-impact bugs.
* **For Beta uplifts:** Already have landed, tested, and stabilized in main.
* **For Release/ESR uplifts:** Already have landed, tested, and stabilized in beta.
* Include tests or provide a strong justification for the absence of tests.
* Not change any localizable strings.

**Uplifts can include:**

* Major top crash fixes.
* High volume startup crash fixes.
* Security fixes.
* Dataloss fixes.
* Fixes for high-impact regressions with a broad impact.
* Fixes for high-impact bugs in a major feature.

## **Process for Uplifts**

Requesting an uplift is done by setting the relevant flag (`approval-comm-beta`, `approval-comm-release`, or `approval-comm-esr`) on a Bugzilla patch attachment to `?`. An Uplift Request Form is automatically generated to provide the required information. All uplifts must use the Uplift Request Form.

Ensure uplift requests are submitted at least two working days prior to any scheduled release date. This will allow the release team to build the release the day before the scheduled release date, giving ample time for troubleshooting and testing prior to release day.


# Feature Flags

Feature Flags are used to manage features that are not yet ready for general use. Their behavior varies depending on the branch:

## **main**

Feature flags are enabled once all related code for the feature have landed.

## **beta**

Feature flags remain enabled once the feature is complete unless the developers decide to temporarily pause it.

## **release and esr**

Feature flags are disabled by default until an explicit decision is made to enable the feature for all users.


# Tracking Fixes for Releases

If you believe a bug needs to be addressed in a release, follow these steps:

1. **Set the Tracking Flag**
   * Use `?` to nominate the bug for tracking for the specific Thunderbird version
   * Provide a justification explaining why the bug needs to be addressed in the release.
2. **Mark the Status Flag**
   * Set the corresponding version status flag to `affected`.

Release Management will review bugs nominated for tracking. If they agree that the bug requires investigation for the release, they will change the tracking flag from `?` to `+`.


# Contributing to Release Notes

Release notes are an essential part of keeping our users informed. If you believe your knowledge can enhance the release notes, we encourage developers to contribute suggested release notes alongside their patch fixes.

## **How to Contribute**

1. **Write Effective Release Notes**
   * Refer to [existing release notes](https://www.thunderbird.net/en-US/thunderbird/releases/) as examples.
   * Follow [this guide](https://jfx2006.github.io/thunderbird-ci-docs/documentation/drivers/releasenotes/) on how to write brief, clear, and user-focused release notes.
2. **Suggest a Release Note**
   * Set the `relnote-thunderbird` tracking flag in Bugzilla to `?`. This will provide a template to write your note and will nominate it for inclusion in the release notes.

## **Why This Matters**

* Contributing release notes is especially valuable for changes that land in comm-central. During the merge from comm-central to comm-beta, hundreds of commits are often reviewed, and release note suggestions help to make this process more efficient and accurate.
* By contributing, you help ensure that our users remain informed about new features, fixes, and changes.


