<html>
  <head>
    <meta content="text/html; charset=windows-1252"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    Hi folks,<br>
    <br>
    :bwinton, :jb and I were discussing issues related to the
    composition process, and I thought I might as well post my summary
    of the situation here, so that other people can chime in. :ehsan
    should give us some insight on the situation, and I'm confident
    other people will have interesting things to say.<br>
    <br>
    Below is a (lengthy) summary of the situation, and represents my own
    point of view. If I appreciated wrongly the situation, please do
    correct me asap.<br>
    <br>
    ---<br>
    <br>
    The compose window in Thunderbird relies on three broadly defined
    components.<br>
    <ol>
      <li>The <editor> component from Gecko; it handles the
        editable area, i.e. where you type your message, the caret, what
        happens when you hit enter, the DOM tree, etc. The code lives in
        comm-central/mozilla/editor/. :ehsan and :kaze are working on it
        if I'm not mistaken.<br>
      </li>
      <li>The editor UI: all the small buttons to insert an image, set
        text in bold, italics, etc. The code lives in
        comm-central/editor/ui. It's horrible code, that hasn't changed
        for the past 10 years, and unlike wine, it doesn't get any
        better with age. AFAIK, no one's working on it, and we
        definitely need help with it. The number of steps required to
        merely insert an image is complete nonsense, and the process is
        <b>not intuitive</b>.</li>
      <li>The code for setting up a compose session and sending the
        message. It's all c++, and I'm thinking about
        comm-central/mailnews/compose/src/, most specifically
        nsMsgCompose.cpp and nsMsgSend.cpp.</li>
      <ul>
        <li>The code first initializes the composition window, does a
          lot of magic, sets up all the composition fields, both visible
          and hidden (recipients, subject, headers, quoted &
          reformatted text, signature, MDN, etc.). It talks to the
          nsIEditor that the <editor> implements to setup html /
          plaintext editing, the encoding, etc.<br>
        </li>
        <li>Then, nsMsgSend.cpp kicks in, walks the DOM tree, figures
          out which images should be attached, determines whether html +
          plaintext or just plaintext should be sent, changes the src
          attributes live in the <editor> instance so that <img
          src="blah.jpg"> becomes <img src=<a
            class="moz-txt-link-rfc2396E" href="cid:whatever">"cid:whatever"</a>>
          and then serializes it all according to the right encoding,
          wraps it, sends it.<br>
        </li>
      </ul>
    </ol>
    A few months ago, I worked on an experiment to see how much of these
    we could replace easily with JS parts. The goals are, roughly, as
    follows.<br>
    <ul>
      <li> Given that we don't have that many resources to devote to the
        composition UI (2.), this would allow us to cheaply get an
        updated, more intuitive UI.</li>
      <li>Make the composition code more accessible to developers.
        Hacking into that C++ code is insanely hard, the entry cost is
        high, and it's scary. Writing it in JS would make it more
        concise, lighter, and more hackable. We could also drop large
        chunks of code that make no sense today : the C++ code goes
        great lengths to figure out the best encoding to sent the
        outgoing message with. In compose in a tab, I settled for UTF8
        always, and saved myself a lot of trouble.</li>
      <li>Allow experimenting with new designs, such as compose in a
        tab.<br>
      </li>
    </ul>
    <ol>
      <li>This part doesn't change at all with my experiment.<br>
      </li>
      <li>The Thunderbird UI is replaced by a CKEditor instance.</li>
      <li>This is where I come in.</li>
      <ul>
        <li>I rewrote this part in JS, and I've implemented most
          required actions. This is either code that determines the
          recipients depending on the composition mode, streams the
          draft to insert its body into the edition area, re-uses the
          attachments from the draft, or the forwarded message, etc; or
          code that performs less pleasant tasks, such as rewrapping the
          text, quoting and rewrapping, or convert html to plaintext (do
          you realize that the component in Thunderbird that does html
          -> plaintext conversion for quoting is not even
          scriptable?).</li>
        <li>This part heavily relies on an <editor> being
          available, so I had to fake myself into a nsIEditor and a
          nsIEditorMailSupport. This roughly works, but I had to resort
          to the most vicious hacks to get this done (more details in an
          appendix).<br>
        </li>
      </ul>
    </ol>
    There are many problems, though.<br>
    <ul>
      <li>CKEditor tries to be cross-platform and hence overrides many
        builtin <editor> features, making them slower and more
        error-prone. For instance, CKEditor will have its own handling
        of the <Enter> key, and will break the DOM tree on its
        own, move the caret... CKEditor has its own spellchecking also.
        CKEditor is very much heavyweight, takes seconds to load, and
        doesn't necessarily fit well as the UI for a mail editor (issues
        with <blockquote>s).</li>
      <li>The Herculean undertaking that this represents. There are
        zillions of options and of possible behaviors; more than a sane
        man would get crazy trying to implement them all. I even
        regularly discover some new options myself: the thing with
        multiple identities for one single account, all preferences
        regarding composition (top-posting vs bottom-posting, signatures
        above/below the quote, signature / no signature, quote / no
        quote, font, color, html, plaintext, html + plaintext, utf8, no
        utf8), s/mime, attachment reminder, MDN, DSN, autosave,
        different composition modes (edit draft, reply, reply all, new,
        etc. I think there are 14 of them), initial attachments, were we
        called through mapi, command-line, drag&drop of attachments
        from: the filesystem, another email, an URL... I believe some of
        these options should go away, even if some users are going to
        crucify us for this. I don't think we have the manpower to
        undertake a rewrite of the composition process and still afford
        to keep that variety of customizations.</li>
      <li>There are hidden invariants all over the place. Specific
        design patterns that oblige you to re-use a specific object, and
        modify it in place. Very specific calling conventions. Hidden
        state that require you to call just the magic function so that
        it will fail after 10 lines because you're not implementing the
        right interfaces, but will still set the global variable to the
        right value. More generally, the expectation that the only place
        we will ever compose messages from is the composition window.<br>
      </li>
    </ul>
    This pretty much sums up the situation, and I hope this gives a
    clear overview. I've appended some gory details below for those of
    you who are interested. I'd love to have your opinion on this,
    correct me if you think I'm exaggerating, etc. These are statements,
    but they reflect my own feeling of the story, and I'm sure people
    have different point of views to oppose. I'd love to hear about
    them!<br>
    <br>
    jonathan<br>
    <br>
    Appendix 1 : the thing with global variables<br>
    <br>
    The workflow for sending a message goes like this.<br>
    - nsMsgComposeService creates a new nsMsgCompose instance, and
    creates a new composition window (all from C++),<br>
    - nsMsgCompose pokes into the editor from the new window,
    initializes it with various parameters, sets its mode to html or
    plaintext, etc, and sets a whole bunch of global variables,<br>
    - nsMsgCompose assembles all the parts, queries the editor, and
    passes the control flow to nsMsgSend<br>
    - nsMsgSend still pokes into the nsEditor (more specifically,
    nsEditorMailSupport).<br>
    <br>
    This all implies the existence of a composition window. I can't
    assemble the parts myself and talk directly to nsMsgSend because
    half the interfaces there are [noscript]. So what I had to do is use
    the SendMsg function from nsMsgCompose that assembles the parts and
    moves on to nsMsgSend.cpp. However, there are two global variables
    that have to be set if you want to send anything besides plaintext.
    These two global variables are only set when initializing the
    classic compose window. So I had to fake myself into a classic
    compose window, and implement just enough interfaces so that the two
    variables are initialized correctly, and then fail early.
    nsMsgCompose then *thinks* it's dealing with a regular compose
    session, and goes on with the right editor + html settings. This is
    wicked. (The two variables control whether there's html or plaintext
    (m_composeHTML) and if there's an editor that should be poked
    (m_editor)).<br>
      <br>
    I managed to hack around all these limitations, and somehow build a
    clean API on top of that; it took me months to improve it over and
    over; I consider this an achievement in itself. I'm even able to
    send attachments through JS! This still amazes me. Most of the code
    went into the quick reply feature of Thunderbird Conversations, and
    this is where I keep polishing the code over and over. However, the
    question of the editor UI still remains open.<br>
    <br>
  </body>
</html>