Developing Thunderbird Addons
Developing Thunderbird Addons
What to expect
- Learn how to create a simple addon.
- Learn some cool Thunderbird-specific techniques.
- Learn how to make sure AMO guys will like your addon.
- Get pointers if you want to create more sophisticated stuff.
Who's developed Firefox addons already?
Depends on the audience.
This presentation
- Three sections: introduction, getting started, Thunderbird techniques
- Interrupt me! Ask questions!
A demo addon
Customize your main toolbar and add the demo button.
Part one
(for those of you who don't know Thunderbird)
Thunderbird: an old beast
/* ****************************************************************************
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
*
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
*
* Dear Mortals,
*
* Please be advised that if you are adding something here, you should also
* strongly consider adding it to the other place it goes too! These can be
* found in paths like so: mailnews/.../build/WhateverFactory.cpp
*
* If you do not, your (static) release builds will be quite pleasant, but
* (dynamic) debug builds will disappoint you by not having your component in
* them.
*
* Yours truly,
* The ghost that haunts the MailNews codebase.
*
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
*
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
* ****************************************************************************/
Thunderbird: an old beast
- lots, lots, lots of functionality (Mail, RSS, NNTP)
- zillions of conf options
- old codebase (15-year-old+ code)
- not much documentation
These guys still look happy!
And they're Thunderbird developers!
Thunderbird: I CAN HAZ FUN
- newer APIs (since Thunderbird 3),
- more active contributors
- new exciting projects
OpenSearch, account provisioner
Big potential for addons!
Everyone likes to have email their own way. Everyone has a strong sense of
what email should be. Everyone wants an option to enable their favorite
feature.
Part two
(getting started with addons)
Getting started
- In Firefox, right-click, save link as
- In Thunderbird, Tools > Add-ons > Wrench Menu > Install add-on
from file
- Restart Thunderbird
- Right-click on the main toolbar, and customize
One thing before we get going
Apply every single piece of advice from “Setting up an extension development
environment” on MDN.
- proxy file
- development prefs
- development profile
We're going to use
- XUL, for adding elements to the main Thunderbird window
- JS, for getting stuff done
- HTML, for our own user interface
Basic structure of an addon
myaddon.xpi
|- content/
|- chrome.manifest
\- install.rdf
Of course there's much more. But let's keep it simple.
An overlay: adding elements to the UI
In chrome.manifest:
content demo content/
overlay ↵
chrome://messenger/content/messenger.xul ↵
chrome://demo/content/overlay.xul
Adding elements to the UI
In content/overlay.xul:
<overlay id="messengerWindow" xmlns="...">
<script src="demo.js" />
<toolbarpalette id="MailToolbarPalette">
<toolbarbutton id="demo-button"
class="demo-button toolbarbutton-1"
label="Demo, yay!" type="button"
oncommand="Demo.load();"
/>
</toolbarpalette>
</overlay>
Stuff happening
var Demo = {};
(function () {
Demo.load = function () {
let tabmail =
document.getElementById("tabmail");
tabmail.openTab("chromeTab", {
chromePage:
"chrome://demo/content/main.html",
});
};
})();
AMO-Compliant!
(No global namespace pollution.)
Lesson 1: how to open a tab in Thunderbird
You just learnt how to open a tab in Thunderbird.
Many types of tabs: chrome, content, folder, message, as well as your own tab types
if you wish.
Lesson 1: now do something with the tab
- Use HTML: you know the technology.
- Use jQuery:
chrome://messenger/content/jquery.js
(1.6.4) and jquery-ui.js (1.8.2).
Part three
before moving on...
Your first enemy: the attacker
- Be wary of remote data: attachment contents,
message contents: do not display them "as is".
- Display messages with <iframe type="content">
- If your script is manipulating remote data, run it with a resource:// URL
One golden rule
Steal as much code as you can.
Parler des trucs un peu tricky sur les nsIMsgDBHdr
Part four
(some cool Thunderbird stuff)
Listing the accounts
Pro trick: Thunderbird (and Seamonkey) have a module called
iteratorUtils.js that will help you deal with XPCOM oddities.
XPCOM? JSM?
Proof by example
Before:
for (var i = 0; i < accounts.Count(); i++) {
let account = accounts
.QueryElementAt(i, Ci.nsIMsgAccount);
After:
for each (let account in
fixIterator(MailServices.accounts.accounts,
Ci.nsIMsgAccount)) {
Advantages
- Thunderbird tends to use these enumerators a lot
- Works for any type of iterator:
Array,
Object,
nsISupportsArray,
nsISimpleEnumerator,
nsIEnumerator...
STOP IT!
First demo
See the list of accounts in your Thunderbird profile.
- The code is commented with links to various reference pages.
- Use the "email the author" link!
Moving on...
We would like to inspect the folders of a given account.
- Read MXR (always read MXR, never trust MDN)
Learn how to use MXR! What is MXR?
Moving on...
- Read MXR
- Figure out nsIMsgAccount has a
rootFolder property
- Figure out nsIMsgFolder has a
subFolders property
(nsISimpleEnumerator)
- Use the same recipe!
Learn how to use MXR!
Second demo
- Print out the folders of an account
- Example: find the inbox
let isInbox =
folder.getFlag(Ci.nsMsgFolderFlags.Inbox);
A common pattern
Same technique applicable to get the items in an address book (hint:
use MailServices.ab.directories, and then
directory.childCards).
Now talking about messages
Same old story
- Use folder.msgDatabase .EnumerateMessages()
- fixIterator that
- We get elements that are nsIMsgDBHdr instances
Examining a single message
A nsIMsgDBHdr contains everything to
display an entry in the message list: date, author, subject, but no
attachments, etc.
It is also the entry point to modify messages: mark them read, change tags,
etc. (use thunderbird-stdlib!).
Examining a single message
However, nsIMsgDBHdrs are just entry points.
Solution: get the entire message contents and analyze it. This is called
streaming the message.
Streaming a message
MsgHdrToMimeMessage(msgHdr, this,
function (aMsgHdr, aMimeMsg) {
if (aMimeMsg.has("reply-to"))
let atts = aMimeMsg.allUserAttachments;
}, true, {
aPartsOnDemand: true,
});
Streaming a message
Allows one to:
- know about all the headers,
- about all the parts in the message (including
attachments),
More stuff you can do
- displaying the message
- getting the contents of an attachment as a string
- opening an attachment in a tab...
Ask me!
Demo...
Bottom part of the second demo.
And now for something completely different
What if you want to search messages? Accross
all folders? According to
some criterion?
Gloda is your friend
- Gloda works on top of all the concepts described before
- Knows relationships between contacts, email adresses, messages,
threads, identities...
Gloda is your friend
- Give me all messages in this thread
- Give me the thread for this message
- Give me all messages from person X
- Give me emails with attachments
- Give me emails between given dates, with a given subject
Gloda is your friend
let query = Gloda.newQuery(
Gloda.NOUN_MESSAGE);
query.subjectMatches(searchTerm);
let myListener = {
};
let collection =
query.getCollection(myListener);
The query is asynchronous.
More Gloda
- Gloda returns GlodaMessage instances
- They have a folderMessage property =
nsIMsgDBHdr
- More queries possible (think Conversations...)
Get the thread a message belongs to
onQueryCompleted: function (aCollection) {
aCollection.items[0]
.conversation.getMessageCollection({
onQueryCompleted: function (aColl) {
},
});
},
Part four
(just kidding, it's a recap)
Thunderbird add-ons
- you can make cool stuff
- newer APIs to save your hair
- nsIMsgDBHdr is the central object. Use
thunderbird-stdlib.
- Gloda works on top of it by providing relationships between various
object
Final demo: Email Chat
- jquery, jquery-tmpl
- Gloda to find all messages in the thread
- Gloda live query!
- thunderbird-stdlib for the send function
End!
Send me an email or post to mozilla.dev.apps.thunderbird with any questions.
Q&A session