nsIEditor is now builtinclass

Ehsan Akhgari ehsan.akhgari at gmail.com
Wed Aug 27 21:16:59 UTC 2014

On 2014-08-26, 5:11 PM, Jonathan Protzenko wrote:
> Here's the full explanation for the earlier initEditor breakage I
> mentioned in my original mail below.
> tl;dr: nsIEditor is, as Ehsan kindly pointed out to me, now builtinclass
> (can't be implemented in JS); a consequence is that clients who wish to
> send HTML emails now really need to abide by one very specific state
> protocol. A way to workaround this would be to make the editor field of
> nsIMsgCompose read-write. Thoughts?
> Here's the long story.
> My use-case is as follows. Thunderbird-stdlib currently offers a
> sendMessage function with a nice Javascript-oriented API that hides the
> atrocities of the underlying nsMsgCompose / nsMsgComposeSession /
> nsIMsgComposeParams / nsIMsgCompFields / nsIEditor / nsIMsgProgress /
> nsIStateListener classes and instead provides (what I believe is) a
> moderately simple function with intuitive parameters [1].
> This function can either send a blob of plain text, or take something
> that looks more or less like an iframe and send its inner contents as an
> HTML mail. It places no further requirements on the state of the iframe
> before sending, meaning that the client code really can send whatever
> they want. It is entirely decoupled from the act of setting up a
> composition window.
> Internally, the mail sending code is in nsMsgCompose; the code relies on
> its m_editor field [2], and uses, for instance, the outputToString
> method of nsIEditor [4] to figure out the HTML that ought to be put in
> the message (also, it uses the getEmbeddedObjects method of nsIEditor to
> figure out the inline images it should attach [3]). When sending a
> message, I create an nsMsgCompose object. I hence wish to set its
> m_editor field for my HTML message to be sent properly. The only
> possible way to set m_editor to something other than nullptr [5] is to
> call InitEditor. Quite unfortunately, initEditor also does something
> else, namely quoting the original message's body and adding the user's
> signature, if any, into the iframe [6].

OK, I think I now understand _what_ your code is doing.  Let's agree 
that it is a hack that can break because of many things, such as changes 
to the nsIEditor interface (the issue at hand) and other assumption 
you're making along the way (such as what InitEditor does exactly, etc.)

> This contradicts my earlier claim of having a stateless, composable
> "send" function that makes no further assumptions on the state of the
> original editor/iframe and is able to just send anything without
> requiring the client code to go through a certain protocol.
> The way I work around this currently is to create a fake nsIEditor
> instance, that forwards calls to outputToString and getEmbeddedObjects
> to the real nsIEditor, and ignores everything else, hence rendering the
> code that quotes the original message into the iframe effectless. The
> code for my sendMessage function hence creates a fake editor, calls
> InitEditor only to have m_editor set, and proceeds with calling the
> actual sendMsg function.

OK, yes this all makes sense.  FWIW making interfaces buildinclass is 
something that we need to do because some classes are not supposed to 
implemented by JS code in Thunderbird/add-ons/etc. (nsIEditor is an 
example) and it enables us to make assumptions about the nature of these 
classes safely.

What I still don't understand is why you're doing all of this, IOW what 
is the problem that you are trying to solve.  Sorry if it's obviously 
mentioned somewhere but I don't really understand the Thunderbird side 
of this setup, so perhaps I'm missing something.

> A few days ago, nsIEditor was made builtinclass [7], meaning that one
> can no longer implement nsIEditor in JS. This means that I must ask
> clients of my code to:
> - set up a composition session (nsIMsgCompose) parameterized over a
> nsIMsgComposeParams object (required at initialization-time of
> nsMsgCompose);
> - modify in place this nsIMsgComposeParams object to reflect the choice
> of recipients, identity, etc. for sending the email;
> - call my sendMessage function and pass it the original nsIMsgCompose
> object.

Again, what is it that you're trying to achieve?  (I'm interested in the 
use case, not how you achieve it in the implementation.)  Perhaps we 
should provide appropriate extension points for you to use either on the 
Thunderbird side or the Gecko side (depending on what you want to do.)

> Also, if clients of my code do not wish to have a message quoted into
> the iframe (say, because the iframe contains something already), they no
> longer have this option.
> This is not very satisfactory, as it requires clients of my code to:
> - be familiar with complex interfaces;
> - go through a state protocol;
> - manipulate the same object in an imperative manner.
> A way to restore the previous behavior would be to make the m_editor
> field of nsIMsgCompose read-write, hence no longer requiring me to call
> InitEditor with all its nasty side-effects. I will file a bug with the
> corresponding patch soon.

I don't think that is a good solution.  Really you should be assuming 
less about the internals of the editor.  These types of assumptions can 
break very easily, as you have noticed. :/

More information about the tb-planning mailing list