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.

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.:
1
"message_display_action": {
2
"default_popup": "messagePopup/popup.html",
3
"default_title": "Details",
4
"default_icon": "images/internet-32px.png"
5
},
Copied!

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:
popup.html
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="utf-8">
5
<title>Details</title>
6
<link rel="stylesheet" type="text/css" media="screen" href="popup.css">
7
<script src="popup.js"></script>
8
</head>
9
<body>
10
<div class="grid-container">
11
<div class="header">Subject:</div><div id="subject" class="content"></div>
12
<div class="header">From:</div><div id="from" class="content"></div>
13
<div class="header">Received-Header:</div><div id="received" class="content"></div>
14
</div>
15
</body>
16
</html>
Copied!

popup.css

We place the following popup.css file in the same folder as the popup.html file.
popup.css
1
.grid-container {
2
display: grid;
3
grid-template-columns: 1fr 6fr;
4
}
5
6
.header {
7
font-weight: bold
8
}
9
10
.grid-container div {
11
margin: 1ex;
12
}
Copied!
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 or this grid guide for more details.

popup.js

All WebExtension API functions return a Promise instead of an actual value. This aims to simplify the handling of asynchronous functions.
The author of this example prefers the async/await syntax over the .then() approach for handling Promises. The load() function defined in the following popup.js is therefore defined as async. All used WebExtension API calls, or better all returned Promises of WebExtension API calls are awaited, which means the execution flow in this function sort of halts until the individual Promise is fulfilled. This allows to use asynchronous functions but still being able to write sequential code.
popup.js
1
async function load() {
2
// The user clicked our button, get the active tab in the current window using
3
// the tabs API.
4
let tabs = await messenger.tabs.query({active: true, currentWindow: true});
5
6
// Get the message currently displayed in the active tab, using the
7
// messageDisplay API. Note: This needs the messagesRead permission.
8
// The returned message is a MessageHeader object with the most relevant
9
// information.
10
let message = await messenger.messageDisplay.getDisplayedMessage(tabs[0].id);
11
12
// Update the HTML fields with the message subject and sender.
13
document.getElementById("subject").textContent = message.subject;
14
document.getElementById("from").textContent = message.author;
15
16
// Request the full message to access its full set of headers.
17
let full = await messenger.messages.getFull(message.id);
18
document.getElementById("received").textContent = full.headers.received[0];
19
}
20
21
document.addEventListener("DOMContentLoaded", load);
Copied!
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.

messenger.tabs.query()

The tabs API 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 method to find it (line 4).
Using messeger.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.

messenger.messageDisplay.getDisplayedMessage()

The getDisplayedMessage method of the messageDisplay API provides access to the currently viewed message in a given tab. It returns a Promise for a MessageHeader object from the messages API with basic information about the message (line 10).
At this stage we are interested in the subject (line 13) and the author (line 14).
The getDisplayMessage method requires the messagesRead permission, which needs to be added to the permissions key of our manifest.json file.
1
"permissions": [
2
"messagesRead"
3
],
Copied!

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 method (line 17) returns a Promise for a 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 have all the files in the right places:
1
hello-world/
2
├── manifest.json
3
├── mainPopup/
4
├── popup.html
5
├── popup.css
6
└── popup.js
7
├── messagePopup/
8
├── popup.html
9
├── popup.css
10
└── popup.js
11
└── images/
12
├── internet.png
13
├── internet-32px.png
14
└── internet-16px.png
Copied!
This is how our manifest.json should now look like:
manifest.json
1
{
2
"manifest_version": 2,
3
"name": "Hello World",
4
"description": "Your basic Hello World extension!",
5
"version": "2.0",
6
"author": "[Your Name Here]",
7
"applications": {
8
"gecko": {
10
"strict_min_version": "78.0"
11
}
12
},
13
"browser_action": {
14
"default_popup": "mainPopup/popup.html",
15
"default_title": "Hello World",
16
"default_icon": "images/internet-32px.png"
17
},
18
"message_display_action": {
19
"default_popup": "messagePopup/popup.html",
20
"default_title": "Details",
21
"default_icon": "images/internet-32px.png"
22
},
23
"permissions": [
24
"messagesRead"
25
],
26
"icons": {
27
"64": "images/internet.png",
28
"32": "images/internet-32px.png",
29
"16": "images/internet-16px.png"
30
}
31
}
Copied!

Installing

As described in the first part of the Hello World Extension Tutorial, 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.