Developing Thunderbird Addons
Developing Thunderbird Addons
What can a web developer do with Thunderbird?
- Use HTML and Javascript...
- To create innovative code samples and user interfaces
- The Thunderbird client is based on Gecko too: anything you can do in
Firefox, you can do in Thunderbird
Showcase!
- Thunderbird Conversations
- Composition in a tab
- Email chat
- Mail summaries
- ... and many more!
Can I try them out?
- Yes!
- Demo addon available
online
- And it's restartless!
- Use Tools > Mozcamp Demo 2012
Outline of this talk
- Explain how addons are made
- Give you pointers: libraries, code snippets, doc, wiki pages
- Illustrate with more demos
- Hopefully you can get started!
Part one
(quick intro to Thunderbird)
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
The situation is much better now
- newer APIs (since Thunderbird 3),
- more active contributors
- new exciting projects
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.
- In Firefox, right-click, save link as
- In Thunderbird, Tools > Add-ons > Wrench Menu > Install add-on
from file
- Do not restart Thunderbird
- Visit the Tools menu
Part two
(getting started with addons)
Getting started
What is an addon?
- A user interface (XUL, CSS, HTML)
- Some logic (Javascript)
XUL?
- XML User Interface Language
- Good news! You don't have to learn that
Montrer le début du demo addon et dire que c'est que du HTML
The best way to get started
Apply every single piece of advice from “Setting up an extension development
environment” on MDN.
Then steal the demo addon!
The basics of addons
- A folder structure inside a ZIP file
- Some magic files
- Some Mozilla-specific details
Not going to cover that: there's excellent online documentation.
See Mozilla Developer
Network for more.
Lesson 1: how to open a tab in Thunderbird
1 window.openTab("chromeTab", {
2 chromePage: "chrome://demo/content/index.html",
3 });
- First argument = tab type (what is "chrome"?)
- Second argument = tab options
tab type = (chrome, content, folder, message, your own...)
tab options = (specific to each tab type)
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).
A security remainder
- 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
Part three
(more lessons)
One golden rule
Steal as much code as you can.
Parler des trucs un peu tricky sur les nsIMsgDBHdr
Lesson 2: listing the accounts
Sounds simple, right?
Problem...
- Many things in Thunderbird you want to iterate on...
- We have:
Array,
Object,
nsISupportsArray,
nsISimpleEnumerator,
nsIEnumerator...
STOP IT!
Fortunately
Here's a working snippet!
1 for each (let account in
2 fixIterator(MailServices.accounts.accounts,
3 Ci.nsIMsgAccount)) {
4
5 }
Some tricks here
- First trick: use MailServices, that
brings you in scope various "system services",
- Second trick: use fixIterator, that
allows you to iterate on pretty much anything
DEMO SLIDE
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!
Lesson 3: listing the folders
We would like to inspect the folders of a given account.
Pro tip: use mxr.mozilla.org
After reading MXR...
- Figure out nsIMsgAccount has a
rootFolder property
- Figure out nsIMsgFolder has a
subFolders property
(nsISimpleEnumerator)
- fixIterator comes to the rescue!
DEMO SLIDE
This demo recursively iterates over the folders in an account and finds the
Inbox
let isInbox =
folder.getFlag(Ci.nsMsgFolderFlags.Inbox);
More things you can do
Iterate on...
- Address books (MailServices.ab.directories)
- Contact cards (theAddressBook.childCards)
- Messages in a folder (theFolder.msgDatabase.EnumerateMessages())
Lesson 4: inspect a 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 SLIDE
Inspecting a message, displaying a message.
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
Lesson 5: search messages by subject
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...)
Lesson 6: Get the thread a message belongs to
onQueryCompleted: function (aCollection) {
aCollection.items[0]
.conversation.getMessageCollection({
onQueryCompleted: function (aColl) {
},
});
},
Lesson 7: all emails involving a particular
person
1 let query = Gloda.newQuery(Gloda.NOUN_MESSAGE);
2 let q1 = Gloda.newQuery(Gloda.NOUN_IDENTITY);
3 q1.kind("email");
4 q1.value("jonathan.protzenko@gmail.com");
5 q1.getCollection({
6
7 let q2 = Gloda.newQuery(Gloda.NOUN_MESSAGE);
8 q2.involves.apply(q2, aCollection.items);
9 q2.getCollection({
10
(read the full code)
Lesson 8: search attachments with a given
content-type
1 let query = Gloda.newQuery(Gloda.NOUN_MESSAGE);
2 query.attachmentTypes("application/pdf");
Watch the demo!
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
End!
Send me an email or post to mozilla.dev.apps.thunderbird with any questions.
Q&A session