Bug 842632, part 1: Distinguish between building address strings for display or MIME, r=IanN, sr=Neil.
☠☠ backed out by 5e1f20de9a5a ☠ ☠
authorJoshua Cranmer <Pidgeot18@gmail.com>
Wed, 18 Sep 2013 22:53:28 -0500
changeset 16934 b5b27527f40b9a13251f19f5df2f9ae7e4f59707
parent 16933 6ec8a0de056778f0cc148c8a1fd17bfe5d81b5b3
child 16935 20149c337716bbf8a361895f801af51e0bf914d7
push id1074
push userbugzilla@standard8.plus.com
push dateMon, 03 Feb 2014 22:47:23 +0000
treeherdercomm-beta@6b791b5369ed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersIanN, Neil
bugs842632
Bug 842632, part 1: Distinguish between building address strings for display or MIME, r=IanN, sr=Neil. Even though seamonkey is a CLOSED TREE.
mail/components/addrbook/content/abCommon.js
mail/components/compose/content/addressingWidgetOverlay.js
mailnews/addrbook/content/abMailListDialog.js
mailnews/addrbook/src/nsAbAutoCompleteSearch.js
mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js
mailnews/compose/src/nsMsgCompFields.cpp
mailnews/compose/src/nsMsgCompose.cpp
mailnews/compose/src/nsSmtpProtocol.cpp
mailnews/compose/test/unit/test_splitRecipients.js
mailnews/db/msgdb/src/nsMsgHdr.cpp
mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp
mailnews/mime/public/MimeHeaderParser.h
mailnews/mime/public/moz.build
mailnews/mime/public/nsIMsgHeaderParser.idl
mailnews/mime/src/MimeHeaderParser.cpp
mailnews/mime/src/moz.build
mailnews/mime/src/nsMsgHeaderParser.cpp
mailnews/mime/src/nsMsgHeaderParser.h
mailnews/mime/test/unit/test_nsIMsgHeaderParser1.js
mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
mailnews/mime/test/unit/xpcshell.ini
suite/mailnews/addrbook/abCommon.js
suite/mailnews/compose/addressingWidgetOverlay.js
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -544,17 +544,17 @@ function GenerateAddressFromCard(card)
   if (card.isMailList)
   {
     var directory = GetDirectoryFromURI(card.mailListURI);
     email = directory.description || card.displayName;
   }
   else
     email = card.primaryEmail;
 
-  return MailServices.headerParser.makeFullAddress(card.displayName, email);
+  return MailServices.headerParser.makeMimeAddress(card.displayName, email);
 }
 
 function GetDirectoryFromURI(uri)
 {
   return MailServices.ab.getDirectory(uri);
 }
 
 // returns null if abURI is not a mailing list URI
--- a/mail/components/compose/content/addressingWidgetOverlay.js
+++ b/mail/components/compose/content/addressingWidgetOverlay.js
@@ -139,17 +139,22 @@ function Recipients2CompFields(msgCompFi
 
         switch (recipientType)
         {
           case "addr_to"    :
           case "addr_cc"    :
           case "addr_bcc"   :
           case "addr_reply" :
             try {
-              recipient = MailServices.headerParser.reformatUnquotedAddresses(fieldValue);
+              let headerParser = MailServices.headerParser;
+              recipient = [headerParser.makeMimeAddress(fullValue.name,
+                                                        fullValue.email) for
+                  (fullValue of
+                    headerParser.makeFromDisplayAddress(fieldValue, {}))]
+                .join(", ");
             } catch (ex) {recipient = fieldValue;}
             break;
         }
 
         switch (recipientType)
         {
           case "addr_to"          : addrTo += to_Sep + recipient; to_Sep = ",";               break;
           case "addr_cc"          : addrCc += cc_Sep + recipient; cc_Sep = ",";               break;
@@ -284,28 +289,18 @@ function awSetInputAndPopup(inputValue, 
         _awSetInputAndPopup(addressArray[index], popupValue, parentNode, templateNode);
   }
 }
 
 function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templateNode)
 {
   if (popupValue)
   {
-    var recipient;
-    for (var index = 0; index < inputArray.length; index++)
-    {
-      recipient = null;
-      try {
-        recipient =
-          MailServices.headerParser.unquotePhraseOrAddrWString(inputArray[index], true);
-      } catch (ex) {};
-      if (!recipient)
-        recipient = inputArray[index];
+    for (let recipient of inputArray)
       _awSetInputAndPopup(recipient, popupValue, parentNode, templateNode);
-    }
   }
 }
 
 function awRemoveRecipients(msgCompFields, recipientType, recipientsList)
 {
   if (!msgCompFields || !recipientsList)
     return;
 
--- a/mailnews/addrbook/content/abMailListDialog.js
+++ b/mailnews/addrbook/content/abMailListDialog.js
@@ -107,29 +107,27 @@ function GetListValue(mailList, doAdd)
         // of elements in the addressLists attribute
       }
     }
     else if (cardproperty)
     {
       cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
       if (cardproperty)
       {
-        var addresses = {};
-        var names = {};
-        var fullNames = {};
-        var numAddresses = MailServices.headerParser.parseHeadersWithArray(fieldValue, addresses, names, fullNames);
-        for (var j = 0; j < numAddresses; j++)
+        let addrObjects = MailServices.headerParser
+                                      .makeFromDisplayAddress(fieldValue, {});
+        for (let j = 0; j < addrObjects.length; j++)
         {
           if (j > 0)
           {
             cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
             cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
           }
-          cardproperty.primaryEmail = addresses.value[j];
-          cardproperty.displayName = names.value[j];
+          cardproperty.primaryEmail = addrObjects[j].email;
+          cardproperty.displayName = addrObjects[j].name || addrObjects[j].email;
 
           if (doAdd || (doAdd == false && pos >= oldTotal))
             mailList.addressLists.appendElement(cardproperty, false);
         }
         pos++;
       }
     }
     i++;
@@ -265,18 +263,18 @@ function OnLoadEditList()
       let listbox = document.getElementById('addressingWidget');
       let newListBoxNode = listbox.cloneNode(false);
       let templateNode = listbox.querySelector("listitem");
 
       top.MAX_RECIPIENTS = 0;
       for (let i = 0; i < total; i++)
       {
         let card = gEditList.addressLists.queryElementAt(i, Components.interfaces.nsIAbCard);
-        let address = MailServices.headerParser.makeFullAddress(card.displayName,
-                                                                card.primaryEmail);
+        let address = MailServices.headerParser.makeMailboxObject(
+          card.displayName, card.primaryEmail).toString();
         SetInputValue(address, newListBoxNode, templateNode);
       }
       listbox.parentNode.replaceChild(newListBoxNode, listbox);
     }
   }
 
   // Is this directory read-only? If so, we now need to set all the fields to
   // read-only.
@@ -530,22 +528,32 @@ function DropOnAddressListTree(event)
       continue;
 
     DropListAddress(event.target, address);
   }
 }
 
 function DropListAddress(target, address)
 {
-    awClickEmptySpace(target, true);    //that will automatically set the focus on a new available row, and make sure is visible
-    if (top.MAX_RECIPIENTS == 0)
+  // Set focus on a new available, visible row.
+  awClickEmptySpace(target, true);
+  if (top.MAX_RECIPIENTS == 0)
     top.MAX_RECIPIENTS = 1;
-  var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
-    lastInput.value = address;
+
+  // Break apart the MIME-ready header address into individual addressees to
+  // add to the dialog.
+  let addresses = {}, names = {}, fullNames = {};
+  MailServices.headerParser.parseHeadersWithArray(address, addresses, names,
+    fullNames);
+  for (let full of fullNames.value)
+  {
+    let lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+    lastInput.value = full;
     awAppendNewRow(true);
+  }
 }
 
 /* Allows extensions to register a listener function for
  * when a mailing list is loaded.  The listener function
  * should take two parameters - the first being the
  * mailing list being loaded, the second one being the
  * current window document.
  */
--- a/mailnews/addrbook/src/nsAbAutoCompleteSearch.js
+++ b/mailnews/addrbook/src/nsAbAutoCompleteSearch.js
@@ -286,26 +286,20 @@ nsAbAutoCompleteSearch.prototype = {
    *                       for this result.
    * @param isPrimaryEmail Is the emailToUse the primary email? Set to true if
    *                       it is the case. For mailing lists set it to true.
    * @param result         The result to add the new entry to.
    */
   _addToResult: function _addToResult(commentColumn, directory, card,
                                       emailToUse, isPrimaryEmail, result) {
     var emailAddress =
-      this._parser.makeFullAddress(card.displayName,
-                                   card.isMailList ?
-                                   card.getProperty("Notes", "") || card.displayName :
-                                   emailToUse);
-
-    // The old code used to try it manually. I think if the parser can't work
-    // out the address from what we've supplied, then its busted and we're not
-    // going to do any better doing it manually.
-    if (!emailAddress)
-      return;
+      this._parser.makeMailboxObject(card.displayName,
+                                     card.isMailList ?
+                                     card.getProperty("Notes", "") || card.displayName :
+                                     emailToUse).toString();
 
     // If it is a duplicate, then just return and don't add it. The
     // _checkDuplicate function deals with it all for us.
     if (this._checkDuplicate(directory, card, emailAddress, result))
       return;
 
     // Find out where to insert the card.
     var insertPosition = 0;
--- a/mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js
+++ b/mailnews/addrbook/src/nsAbLDAPAutoCompleteSearch.js
@@ -107,25 +107,21 @@ nsAbLDAPAutoCompleteSearch.prototype = {
     var lcEmailAddress = emailAddress.toLocaleLowerCase();
 
     return this._result._searchResults.some(function(result) {
       return result.value.toLocaleLowerCase() == lcEmailAddress;
     });
   },
 
   _addToResult: function _addToResult(card) {
-    var emailAddress =
-      this._parser.makeFullAddress(card.displayName, card.isMailList ?
-        card.getProperty("Notes", "") || card.displayName : card.primaryEmail);
-
-    // The old code used to try it manually. I think if the parser can't work
-    // out the address from what we've supplied, then its busted and we're not
-    // going to do any better doing it manually.
-    if (!emailAddress)
-      return;
+    let emailAddress =
+      this._parser.makeMailboxObject(card.displayName,
+                                     card.isMailList ?
+                                     card.getProperty("Notes", "") || card.displayName :
+                                     card.primaryEmail).toString();
 
     // If it is a duplicate, then just return and don't add it. The
     // _checkDuplicate function deals with it all for us.
     if (this._checkDuplicate(card, emailAddress))
       return;
 
     // Find out where to insert the card.
     var insertPosition = 0;
--- a/mailnews/compose/src/nsMsgCompFields.cpp
+++ b/mailnews/compose/src/nsMsgCompFields.cpp
@@ -11,16 +11,19 @@
 #include "prmem.h"
 #include "nsIFileChannel.h"
 #include "nsIMsgMdnGenerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMsgMimeCID.h"
 #include "nsIMimeConverter.h"
 #include "nsArrayEnumerator.h"
 #include "nsMemory.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+using namespace mozilla::mailnews;
 
 /* the following macro actually implement addref, release and query interface for our component. */
 NS_IMPL_ISUPPORTS1(nsMsgCompFields, nsIMsgCompFields)
 
 nsMsgCompFields::nsMsgCompFields()
 {
   int16_t i;
   for (i = 0; i < MSG_MAX_HEADERS; i ++)
@@ -535,35 +538,29 @@ nsMsgCompFields::SplitRecipients(const n
     char * pNames = names;
     char * pAddresses = addresses;
     PRUnichar** result = (PRUnichar**) NS_Alloc(sizeof(PRUnichar*) * numAddresses);
     if (!result)
       return NS_ERROR_OUT_OF_MEMORY;
 
     for (i = 0; i < numAddresses; ++i)
     {
-      nsCString fullAddress;
       nsAutoString recipient;
       if (!aEmailAddressOnly)
       {
-        nsCString decodedName;
-        converter->DecodeMimeHeaderToUTF8(nsDependentCString(pNames),
-                                          GetCharacterSet(), false, true,
-                                          decodedName);
-        rv = parser->MakeFullAddressString((!decodedName.IsEmpty() ?
-                                            decodedName.get() : pNames),
-                                           pAddresses,
-                                           getter_Copies(fullAddress));
+        nsString decodedName;
+        converter->DecodeMimeHeader(pNames, GetCharacterSet(), false, true,
+                                    decodedName);
+        if (decodedName.IsEmpty())
+          CopyUTF8toUTF16(pNames, decodedName);
+        MakeDisplayAddress(decodedName, NS_ConvertUTF8toUTF16(pAddresses),
+                           recipient);
       }
-      if (NS_SUCCEEDED(rv) && !aEmailAddressOnly)
-        rv = ConvertToUnicode("UTF-8", fullAddress, recipient);
       else
-        rv = ConvertToUnicode("UTF-8", nsDependentCString(pAddresses), recipient);
-      if (NS_FAILED(rv))
-        break;
+        CopyUTF8toUTF16(pAddresses, recipient);
 
       result[i] = ToNewUnicode(recipient);
       if (!result[i])
       {
         rv = NS_ERROR_OUT_OF_MEMORY;
         break;
       }
 
@@ -616,28 +613,23 @@ nsresult nsMsgCompFields::SplitRecipient
 
     for (uint32_t i = 0; i < numAddresses; ++i)
     {
       nsCString fullAddress;
       nsCString decodedName;
       converter->DecodeMimeHeaderToUTF8(nsDependentCString(pNames),
                                         GetCharacterSet(), false, true,
                                         decodedName);
-      rv = parser->MakeFullAddressString((!decodedName.IsEmpty() ?
-                                          decodedName.get() : pNames),
-                                         pAddresses,
-                                         getter_Copies(fullAddress));
+      if (decodedName.IsEmpty())
+        decodedName.Assign(pNames);
+      MakeMimeAddress(decodedName, nsDependentCString(pAddresses), fullAddress);
 
       nsMsgRecipient msgRecipient;
 
-      rv = ConvertToUnicode("UTF-8",
-                            NS_SUCCEEDED(rv) ? fullAddress.get() : pAddresses,
-                            msgRecipient.mAddress);
-      if (NS_FAILED(rv))
-        return rv;
+      CopyUTF8toUTF16(fullAddress, msgRecipient.mAddress);
 
       rv = ConvertToUnicode("UTF-8", pAddresses, msgRecipient.mEmail);
       if (NS_FAILED(rv))
         return rv;
 
       aResult.AppendElement(msgRecipient);
 
       pNames += PL_strlen(pNames) + 1;
--- a/mailnews/compose/src/nsMsgCompose.cpp
+++ b/mailnews/compose/src/nsMsgCompose.cpp
@@ -73,18 +73,21 @@
 #include "nsStringStream.h"
 #include "nsIMutableArray.h"
 #include "nsArrayUtils.h"
 #include "nsIMsgWindow.h"
 #include "nsITextToSubURI.h"
 #include "nsIAbManager.h"
 #include "nsCRT.h"
 #include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
 #include "nsISelection.h"
 
+using namespace mozilla::mailnews;
+
 static void GetReplyHeaderInfo(int32_t* reply_header_type,
                                nsString& reply_header_locale,
                                nsString& reply_header_authorwrote,
                                nsString& reply_header_ondate,
                                nsString& reply_header_separator,
                                nsString& reply_header_colon,
                                nsString& reply_header_originalmessage)
 {
@@ -1043,22 +1046,17 @@ nsresult nsMsgCompose::_SendMsg(MSG_Deli
     nsString fullName;
     nsString organization;
 
     identity->GetEmail(email);
     identity->GetFullName(fullName);
     identity->GetOrganization(organization);
 
     nsCString sender;
-    nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
-    if (parser) {
-      // convert to UTF8 before passing to MakeFullAddressString
-      parser->MakeFullAddressString(NS_ConvertUTF16toUTF8(fullName).get(),
-                                    email.get(), getter_Copies(sender));
-    }
+    MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), email, sender);
 
     m_compFields->SetFrom(sender.IsEmpty() ? email.get() : sender.get());
     m_compFields->SetOrganization(organization);
     mMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID);
     if (mMsgSend)
     {
       nsCString bodyString(m_compFields->GetBody());
 
@@ -4752,19 +4750,18 @@ nsMsgCompose::CheckAndPopulateRecipients
                     if (bIsMailList)
                       rv = existingCard->GetPropertyAsAString(kNotesProperty, newRecipient.mEmail);
                     else
                       rv = existingCard->GetPrimaryEmail(newRecipient.mEmail);
 
                     if (NS_FAILED(rv))
                       return rv;
 
-                    if (parser)
-                      parser->MakeFullAddress(pDisplayName, newRecipient.mEmail,
-                                              newRecipient.mAddress);
+                    MakeMimeAddress(pDisplayName, newRecipient.mEmail,
+                                    newRecipient.mAddress);
 
                     if (newRecipient.mAddress.IsEmpty())
                     {
                       // oops, parser problem! I will try to do my best...
                       newRecipient.mAddress = pDisplayName;
                       newRecipient.mAddress.AppendLiteral(" <");
                       if (bIsMailList)
                       {
@@ -5407,26 +5404,23 @@ NS_IMETHODIMP nsMsgCompose::CheckCharset
   }
 
   return NS_OK;
 }
 
 nsMsgMailList::nsMsgMailList(nsIAbDirectory* directory) :
   mDirectory(directory)
 {
-  nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
-
   nsString listName, listDescription;
   mDirectory->GetDirName(listName);
   mDirectory->GetDescription(listDescription);
 
-  if (parser)
-    parser->MakeFullAddress(listName,
-                            listDescription.IsEmpty() ? listName : listDescription,
-                            mFullName);
+  MakeDisplayAddress(listName,
+                     listDescription.IsEmpty() ? listName : listDescription,
+                     mFullName);
 
   if (mFullName.IsEmpty())
   {
       //oops, parser problem! I will try to do my best...
       mFullName = listName;
       mFullName.AppendLiteral(" <");
       if (listDescription.IsEmpty())
         mFullName += listName;
--- a/mailnews/compose/src/nsSmtpProtocol.cpp
+++ b/mailnews/compose/src/nsSmtpProtocol.cpp
@@ -39,27 +39,30 @@
 #include "nsIPrefService.h"
 #include "nsISSLSocketControl.h"
 #include "nsComposeStrings.h"
 #include "nsIStringBundle.h"
 #include "nsMsgCompUtils.h"
 #include "nsIMsgWindow.h"
 #include "MailNewsTypes2.h" // for nsMsgSocketType and nsMsgAuthMethod
 #include "nsIIDNService.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
 #include "mozilla/Services.h"
 #include "nsINetAddr.h"
 
 #ifndef XP_UNIX
 #include <stdarg.h>
 #endif /* !XP_UNIX */
 
 #undef PostMessage // avoid to collision with WinUser.h
 
 static PRLogModuleInfo *SMTPLogModule = nullptr;
 
+using namespace mozilla::mailnews;
+
 /* the output_buffer_size must be larger than the largest possible line
  * 2000 seems good for news
  *
  * jwz: I increased this to 4k since it must be big enough to hold the
  * entire button-bar HTML, and with the new "mailto" format, that can
  * contain arbitrarily long header fields like "references".
  *
  * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
@@ -599,27 +602,19 @@ nsresult nsSmtpProtocol::SendHeloRespons
   senderIdentity->GetEmail(emailAddress);
 
   if (emailAddress.IsEmpty())
   {
     m_urlErrorState = NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS;
     return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
   }
 
-  nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
   nsCString fullAddress;
-  if (parser)
-  {
-    // pass nullptr for the name, since we just want the email.
-    //
-    // seems a little weird that we are passing in the emailAddress
-    // when that's the out parameter
-    parser->MakeFullAddressString(nullptr, emailAddress.get(),
-                                  getter_Copies(fullAddress));
-  }
+  // Quote the email address before passing it to the SMTP server.
+  MakeMimeAddress(EmptyCString(), emailAddress, fullAddress);
 
   buffer = "MAIL FROM:<";
   buffer += fullAddress;
   buffer += ">";
 
   if (TestFlag(SMTP_EHLO_DSN_ENABLED))
   {
     bool requestDSN = false;
--- a/mailnews/compose/test/unit/test_splitRecipients.js
+++ b/mailnews/compose/test/unit/test_splitRecipients.js
@@ -13,27 +13,27 @@ const splitRecipientsTests =
     { recipients: "me@foo.invalid, me2@foo.invalid",
       emailAddressOnly: false,
       count: 2,
       result: [ "me@foo.invalid", "me2@foo.invalid" ]
     },
     { recipients: '"foo bar" <me@foo.invalid>',
       emailAddressOnly: false,
       count: 1,
-      result: [ '"foo bar" <me@foo.invalid>' ]
+      result: [ 'foo bar <me@foo.invalid>' ]
     },
     { recipients: '"foo bar" <me@foo.invalid>',
       emailAddressOnly: true,
       count: 1,
       result: [ 'me@foo.invalid' ]
     },
     { recipients: '"foo bar" <me@foo.invalid>, "bar foo" <me2@foo.invalid>',
       emailAddressOnly: false,
       count: 2,
-      result: [ '"foo bar" <me@foo.invalid>', '"bar foo" <me2@foo.invalid>' ]
+      result: [ 'foo bar <me@foo.invalid>', 'bar foo <me2@foo.invalid>' ]
     },
     { recipients: '"foo bar" <me@foo.invalid>, "bar foo" <me2@foo.invalid>',
       emailAddressOnly: true,
       count: 2,
       result: [ "me@foo.invalid", "me2@foo.invalid" ]
     },
     { recipients: "A Group:Ed Jones <c@a.invalid>,joe@where.invalid,John <jdoe@one.invalid>;",
       emailAddressOnly: false,
@@ -58,32 +58,32 @@ const splitRecipientsTests =
     { recipients: 'a@xxx.invalid; B <b@xxx.invalid>',
       emailAddressOnly: false,
       count: 2,
       result: [ "a@xxx.invalid", 'B <b@xxx.invalid>' ]
     },
     { recipients: '"A " <a@xxx.invalid>; b@xxx.invalid',
       emailAddressOnly: false,
       count: 2,
-      result: [ '"A " <a@xxx.invalid>', "b@xxx.invalid" ]
+      result: [ 'A  <a@xxx.invalid>', "b@xxx.invalid" ]
     },
     { recipients: 'A <a@xxx.invalid>; B <b@xxx.invalid>',
       emailAddressOnly: false,
       count: 2,
       result: [ "A <a@xxx.invalid>", 'B <b@xxx.invalid>' ]
     },
     { recipients: "A (this: is, a comment;) <a.invalid>; g:   (this: is, <a> comment;) C <c.invalid>, d.invalid;",
       emailAddressOnly: false,
       count: 3,
-      result: [ '"A (this: is, a comment;)" <a.invalid>', '"g: (this: is, <a> comment;) C" <c.invalid>', "d.invalid" ]
+      result: [ 'A (this: is, a comment;) <a.invalid>', 'g: (this: is, <a> comment;) C <c.invalid>', "d.invalid" ]
     },
-    { recipients: 'Mary Smith <mary@x.invalid>, extra:;, group:jdoe@example.invalid; Who? <one@y.invalid>; <boss@nil.invalid>, "Giant; \"Big\" Box" <sysservices@example.invalid>,         ',
+    { recipients: 'Mary Smith <mary@x.invalid>, extra:;, group:jdoe@example.invalid; Who? <one@y.invalid>; <boss@nil.invalid>, "Giant; \\"Big\\" Box" <sysservices@example.invalid>,         ',
       emailAddressOnly: false,
       count: 6,
-      result: [ "Mary Smith <mary@x.invalid>", "extra:;", "group:jdoe@example.invalid;", "Who? <one@y.invalid>", "boss@nil.invalid", '"Giant; \"Big\" Box" <sysservices@example.invalid>' ]
+      result: [ "Mary Smith <mary@x.invalid>", "extra:;", "group:jdoe@example.invalid;", "Who? <one@y.invalid>", "boss@nil.invalid", 'Giant; \"Big\" Box <sysservices@example.invalid>' ]
       },
     { recipients: 'Undisclosed recipients: a@foo.invalid ;;extra:;',
       emailAddressOnly: true,
       count: 2,
       result: [ '\"Undisclosed recipients: a\"@foo.invalid ;', 'extra:;' ]
     },
     { recipients: 'Undisclosed recipients:;;extra:a@foo.invalid;',
       emailAddressOnly: true,
--- a/mailnews/db/msgdb/src/nsMsgHdr.cpp
+++ b/mailnews/db/msgdb/src/nsMsgHdr.cpp
@@ -1,22 +1,24 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "msgCore.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
 #include "nsMsgHdr.h"
 #include "nsMsgDatabase.h"
 #include "nsMsgUtils.h"
-#include "nsIMsgHeaderParser.h"
 #include "nsIMsgThread.h"
 #include "nsMsgMimeCID.h"
 #include "nsIMimeConverter.h"
 
+using namespace mozilla::mailnews;
+
 NS_IMPL_ISUPPORTS1(nsMsgHdr, nsIMsgDBHdr)
 
 #define FLAGS_INITED 0x1
 #define CACHED_VALUES_INITED 0x2
 #define REFERENCES_INITED 0x4
 #define THREAD_PARENT_INITED 0x8
 
 nsMsgHdr::nsMsgHdr(nsMsgDatabase *db, nsIMdbRow *dbRow)
@@ -399,48 +401,26 @@ NS_IMETHODIMP nsMsgHdr::SetRecipients(co
 
 nsresult nsMsgHdr::BuildRecipientsFromArray(const char *names, const char *addresses, uint32_t numAddresses, nsAutoCString& allRecipients)
 {
   NS_ENSURE_ARG_POINTER(names);
   NS_ENSURE_ARG_POINTER(addresses);
   nsresult ret = NS_OK;
   const char *curName = names;
   const char *curAddress = addresses;
-  nsIMsgHeaderParser *headerParser = m_mdb->GetHeaderParser();
 
   for (uint32_t i = 0; i < numAddresses; i++, curName += strlen(curName) + 1, curAddress += strlen(curAddress) + 1)
   {
     if (i > 0)
       allRecipients += ", ";
 
-    if (headerParser)
-    {
-       nsCString fullAddress;
-       ret = headerParser->MakeFullAddressString(curName, curAddress,
-                                                 getter_Copies(fullAddress));
-       if (NS_SUCCEEDED(ret) && !fullAddress.IsEmpty())
-       {
-          allRecipients += fullAddress;
-          continue;
-       }
-    }
-
-        // Just in case the parser failed...
-    if (strlen(curName))
-    {
-      allRecipients += curName;
-      allRecipients += ' ';
-    }
-
-    if (strlen(curAddress))
-    {
-      allRecipients += '<';
-      allRecipients += curAddress;
-      allRecipients += '>';
-    }
+    nsCString fullAddress;
+    MakeMimeAddress(nsDependentCString(curName),
+      nsDependentCString(curAddress), fullAddress);
+    allRecipients += fullAddress;
   }
 
   return ret;
 }
 
 NS_IMETHODIMP nsMsgHdr::SetRecipientsArray(const char *names, const char *addresses, uint32_t numAddresses)
 {
 	nsresult ret;
--- a/mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp
+++ b/mailnews/extensions/mdn/src/nsMsgMdnGenerator.cpp
@@ -16,28 +16,30 @@
 #include "nsMailHeaders.h"
 #include "nsMsgLocalFolderHdrs.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsISmtpService.h"  // for actually sending the message...
 #include "nsMsgCompCID.h"
 #include "nsComposeStrings.h"
 #include "nsISmtpServer.h"
 #include "nsIPrompt.h"
-#include "nsIMsgHeaderParser.h"
 #include "nsIMsgCompUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIStringBundle.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsMsgUtils.h"
 #include "nsNetUtil.h"
 #include "nsIMsgDatabase.h"
 #include "mozilla/Services.h"
 #include "nsIArray.h"
 #include "nsArrayUtils.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+using namespace mozilla::mailnews;
 
 #define MDN_NOT_IN_TO_CC          ((int) 0x0001)
 #define MDN_OUTSIDE_DOMAIN        ((int) 0x0002)
 
 #define HEADER_RETURN_PATH          "Return-Path"
 #define HEADER_DISPOSITION_NOTIFICATION_TO  "Disposition-Notification-To"
 #define HEADER_APPARENTLY_TO        "Apparently-To"
 #define HEADER_ORIGINAL_RECIPIENT     "Original-Recipient"
@@ -456,26 +458,20 @@ nsresult nsMsgMdnGenerator::CreateFirstP
     bool conformToStandard = false;
     if (compUtils)
       compUtils->GetMsgMimeConformToStandard(&conformToStandard);
 
     nsString fullName;
     m_identity->GetFullName(fullName);
 
     nsCString fullAddress;
-    nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
-    if (parser)
-    {
-        // convert fullName to UTF8 before passing it to MakeFullAddressString
-        parser->MakeFullAddressString(NS_ConvertUTF16toUTF8(fullName).get(),
-                                      m_email.get(), getter_Copies(fullAddress));
-    }
+    // convert fullName to UTF8 before passing it to MakeMimeAddress
+    MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), m_email, fullAddress);
 
-    convbuf = nsMsgI18NEncodeMimePartIIStr(
-        (!fullAddress.IsEmpty()) ? fullAddress.get(): m_email.get(),
+    convbuf = nsMsgI18NEncodeMimePartIIStr(fullAddress.get(),
         true, m_charset.get(), 0, conformToStandard);
 
     parm = PR_smprintf("From: %s" CRLF, convbuf ? convbuf : m_email.get());
 
     rv = FormatStringFromName(NS_LITERAL_STRING("MsgMdnMsgSentTo").get(), NS_ConvertASCIItoUTF16(m_email).get(),
                             getter_Copies(firstPart1));
     if (NS_FAILED(rv))
         return rv;
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/public/MimeHeaderParser.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MimeHeaderParser_h__
+#define MimeHeaderParser_h__
+
+#include "nsStringGlue.h"
+
+namespace mozilla {
+namespace mailnews {
+
+/**
+ * Given a name and an email, both encoded in UTF-8, produce a string suitable
+ * for writing in an email header by quoting where necessary.
+ *
+ * If name is not empty, the output string will be name <email>. If it is empty,
+ * the output string is just the email. Note that this DOES NOT do any RFC 2047
+ * encoding.
+ */
+void MakeMimeAddress(const nsACString &aName, const nsACString &aEmail,
+                     nsACString &full);
+
+/**
+ * Given a name and an email, produce a string suitable for writing in an email
+ * header by quoting where necessary.
+ *
+ * If name is not empty, the output string will be name <email>. If it is empty,
+ * the output string is just the email. Note that this DOES NOT do any RFC 2047
+ * encoding.
+ */
+void MakeMimeAddress(const nsAString &aName, const nsAString &aEmail,
+                     nsAString &full);
+
+/**
+ * Given a name and an email, both encoded in UTF-8, produce a string suitable
+ * for displaying in UI.
+ *
+ * If name is not empty, the output string will be name <email>. If it is empty,
+ * the output string is just the email.
+ */
+void MakeDisplayAddress(const nsAString &aName, const nsAString &aEmail,
+                        nsAString &full);
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
--- a/mailnews/mime/public/moz.build
+++ b/mailnews/mime/public/moz.build
@@ -20,9 +20,10 @@ EXPORTS += [
     'nsIMimeContentTypeHandler.h',
     'nsIMimeObjectClassAccess.h',
     'nsMailHeaders.h',
     'nsMsgMimeCID.h',
 ]
 
 EXPORTS.mozilla.mailnews += [
     'MimeEncoder.h',
+    'MimeHeaderParser.h',
 ]
--- a/mailnews/mime/public/nsIMsgHeaderParser.idl
+++ b/mailnews/mime/public/nsIMsgHeaderParser.idl
@@ -9,21 +9,53 @@
 interface nsISimpleEnumerator;
 
 %{C++
 
 #define NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID \
   "@mozilla.org/messenger/headerparser;1"
 %}
 
+/**
+ * A structured representation of a single address.
+ *
+ * This is meant to be correspond to the address production from RFC 5322.
+ */
+[scriptable, uuid(15f9cc44-afdf-48c8-9de7-3ca0f18fc3b6)]
+interface msgIAddressObject : nsISupports {
+  /// The name of the mailbox.
+  readonly attribute AString name;
+
+  /// The email of the mailbox.
+  readonly attribute AString email;
+
+  /// Return a string form of this object that is suitable for display.
+  AString toString();
+};
+
 /* 
  * nsIMsgRFCParser Interface declaration 
  */ 
-[scriptable, uuid(a67d4d96-e5cb-458d-9311-794d5d1a9832)]
+[scriptable, uuid(4da84fa8-bd45-45e7-9c98-3ba2be88343f)]
 interface nsIMsgHeaderParser : nsISupports {
+  /// Return a structured mailbox object having the given name and email.
+  msgIAddressObject makeMailboxObject(in AString aName, in AString aEmail);
+
+  /**
+   * Return an array of structured mailbox for the given display name string.
+   *
+   * The string is expected to be a comma-separated sequence of strings that
+   * would be produced by msgIAddressObject::toString(). For example, the string
+   * "Bond, James <agent007@mi5.invalid>" would produce one address object,
+   * while the string "webmaster@nowhere.invalid, child@nowhere.invalid" would
+   * produce two address objects.
+   */
+  void makeFromDisplayAddress(in AString aDisplayAddresses,
+                              out unsigned long count,
+                              [retval, array, size_is(count)] out msgIAddressObject addresses);
 
   void parseHeadersWithArray(in wstring aLine, 
                              [array, size_is(count)] out wstring aEmailAddresses,
                              [array, size_is(count)] out wstring aNames,
                              [array, size_is(count)] out wstring aFullNames,
                              [retval] out unsigned long count);
 
 
@@ -103,39 +135,27 @@ interface nsIMsgHeaderParser : nsISuppor
    *                            Addresses in this list will not be added to the
    *                            result.
    * @return                    A string based on the original without the
    *                            duplicate addresses.
    */
   AUTF8String removeDuplicateAddresses(in AUTF8String aAddrs,
                                        in AUTF8String aOtherAddrs);
 
-  /**
-   * Given an e-mail address and a person's name, cons them together into a
-   * single string, doing all the necessary quoting.
-   *
-   * @param  aName     The name of the sender.
-   * @param  aAddress  The address of the sender.
-   * @return           A string of the form name <address>.
-   */
-  AString makeFullAddress(in AString aName, in AString aAddress);
-
-  /**
-   * Given an e-mail address and a person's name, cons them together into a
-   * single string, doing all the necessary quoting (const char* version).
-   *
-   * @param  aName     The name of the sender. This should be in UTF-8.
-   * @param  aAddress  The address of the sender. This should be in UTF-8.
-   * @return           A string of the form name <address>.
-   */
-  [noscript] string makeFullAddressString(in string aName, in string aAddress);
-
   /* This function removes the quoting if you want to show the
      names to users. e.g. summary file, address book. If preserveIntegrity is set to true,
      quote will not be removed in case the name part of the email contains a comma.
   */
   [noscript] string unquotePhraseOrAddr (in string line, in boolean preserveIntegrity);
   wstring unquotePhraseOrAddrWString (in wstring line, in boolean preserveIntegrity);
 
   /* Given a string, will make it safe to use by adding missing quote and escaping needed quote */
   wstring reformatUnquotedAddresses(in wstring line);
+
+  /**
+   * Given a name and email address, produce a string that is suitable for
+   * emitting in a MIME header (after applying RFC 2047 encoding).
+   *
+   * @note This is a temporary method.
+   */
+  AString makeMimeAddress(in AString aName, in AString aEmail);
 };
 
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/src/MimeHeaderParser.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "nsCOMPtr.h"
+#include "nsIMsgHeaderParser.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace mailnews {
+
+void MakeMimeAddress(const nsACString &aName, const nsACString &aEmail,
+                     nsACString &full)
+{
+  nsAutoString utf16Address;
+  MakeMimeAddress(NS_ConvertUTF8toUTF16(aName), NS_ConvertUTF8toUTF16(aEmail),
+                  utf16Address);
+
+  CopyUTF16toUTF8(utf16Address, full);
+}
+
+void MakeMimeAddress(const nsAString &aName, const nsAString &aEmail,
+                     nsAString &full)
+{
+  nsCOMPtr<nsIMsgHeaderParser> headerParser =
+    do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
+
+  headerParser->MakeMimeAddress(aName, aEmail, full);
+}
+
+void MakeDisplayAddress(const nsAString &aName, const nsAString &aEmail,
+                        nsAString &full)
+{
+  nsCOMPtr<nsIMsgHeaderParser> headerParser =
+    do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
+
+  nsCOMPtr<msgIAddressObject> object;
+  headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(object));
+  object->ToString(full);
+}
+
+} // namespace mailnews
+} // namespace mozilla
--- a/mailnews/mime/src/moz.build
+++ b/mailnews/mime/src/moz.build
@@ -31,16 +31,17 @@ SOURCES += [
     'mimecont.cpp',
     'mimecryp.cpp',
     'mimecth.cpp',
     'mimedrft.cpp',
     'mimeebod.cpp',
     'mimeenc.cpp',
     'mimeeobj.cpp',
     'mimehdrs.cpp',
+    'MimeHeaderParser.cpp',
     'mimei.cpp',
     'mimeiimg.cpp',
     'mimeleaf.cpp',
     'mimemalt.cpp',
     'mimemapl.cpp',
     'mimemcms.cpp',
     'mimemdig.cpp',
     'mimemmix.cpp',
--- a/mailnews/mime/src/nsMsgHeaderParser.cpp
+++ b/mailnews/mime/src/nsMsgHeaderParser.cpp
@@ -5,16 +5,17 @@
 
 #include "msgCore.h"    // precompiled header...
 #include "nsMsgHeaderParser.h"
 #include "nsISimpleEnumerator.h"
 #include "comi18n.h"
 #include "prmem.h"
 #include <ctype.h>
 #include "nsAlgorithm.h"
+#include "nsMsgUtils.h"
 #include "nsStringGlue.h"
 #include <algorithm>
 
 nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar ** aOutgoingEmailAddress, PRUnichar ** aOutgoingName,
                           PRUnichar ** aOutgoingFullName, nsIMsgHeaderParser *aParser);
 
 /*
  * Macros used throughout the RFC-822 parsing code.
@@ -64,17 +65,16 @@ nsMsgHeaderParser::~nsMsgHeaderParser()
 
 NS_IMPL_ISUPPORTS1(nsMsgHeaderParser, nsIMsgHeaderParser)
 
 // helper function called by ParseHeadersWithArray
 nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar ** aOutgoingEmailAddress, PRUnichar ** aOutgoingName,
                           PRUnichar ** aOutgoingFullName, nsIMsgHeaderParser *aParser)
 {
   NS_ENSURE_ARG(aParser);
-  nsresult rv = NS_OK;
 
   *aOutgoingFullName = nullptr;
   *aOutgoingEmailAddress = nullptr;
   *aOutgoingName = nullptr;
 
   nsAutoCString result;
   if (aAddress && aAddress[0])
   {
@@ -85,32 +85,31 @@ nsresult FillResultsArray(const char * a
   if (aName && aName[0])
   {
     MIME_DecodeMimeHeader(aName, NULL, false, true, result);
     *aOutgoingName = ToNewUnicode(NS_ConvertUTF8toUTF16(!result.IsEmpty() ? result.get() : aName));
   }
 
   nsCString fullAddress;
   nsCString unquotedAddress;
-  rv = aParser->MakeFullAddressString(aName, aAddress,
-                                      getter_Copies(fullAddress));
-  if (NS_SUCCEEDED(rv) && !fullAddress.IsEmpty())
+  fullAddress.Adopt(msg_make_full_address(aName, aAddress));
+  if (!fullAddress.IsEmpty())
   {
     MIME_DecodeMimeHeader(fullAddress.get(), nullptr, false, true, result);
     if (!result.IsEmpty())
       fullAddress = result;
     aParser->UnquotePhraseOrAddr(fullAddress.get(), true, getter_Copies(unquotedAddress));
     if (!unquotedAddress.IsEmpty())
       fullAddress = unquotedAddress;
     *aOutgoingFullName = ToNewUnicode(NS_ConvertUTF8toUTF16(fullAddress));
   }
   else
     *aOutgoingFullName = nullptr;
 
-  return rv;
+  return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgHeaderParser::ParseHeadersWithArray(const PRUnichar * aLine, PRUnichar *** aEmailAddresses,
                                                        PRUnichar *** aNames, PRUnichar *** aFullNames, uint32_t * aNumAddresses)
 {
   char * names = nullptr;
   char * addresses = nullptr;
   uint32_t numAddresses = 0;
@@ -190,26 +189,17 @@ nsMsgHeaderParser::RemoveDuplicateAddres
 {
   nsCString res;
   res.Adopt(msg_remove_duplicate_addresses(aAddrs, aOtherAddrs));
   aResult.Assign(res);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsMsgHeaderParser::MakeFullAddressString(const char *aName,
-                                         const char *aAddress, char **aResult)
-{
-  NS_ENSURE_ARG_POINTER(aResult);
-  *aResult = msg_make_full_address(aName, aAddress);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsMsgHeaderParser::MakeFullAddress(const nsAString &aName,
+nsMsgHeaderParser::MakeMimeAddress(const nsAString &aName,
                                    const nsAString &aAddress, nsAString &aResult)
 {
   nsCString utf8Str;
   utf8Str.Adopt(msg_make_full_address(NS_ConvertUTF16toUTF8(aName).get(),
                                       NS_ConvertUTF16toUTF8(aAddress).get()));
   CopyUTF8toUTF16(utf8Str, aResult);
   return NS_OK;
 }
@@ -1558,8 +1548,122 @@ msg_make_full_address(const char* name, 
   s += L;
   if (nl > 0)
     *s++ = '>';
   *s = 0;
   L = (s - buf) + 1;
   buf = (char *)PR_Realloc (buf, L);
   return buf;
 }
+
+class MsgAddressObject MOZ_FINAL : public msgIAddressObject
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MSGIADDRESSOBJECT
+
+  MsgAddressObject(const nsAString &aName, const nsAString &aEmail);
+
+private:
+  nsString mName;
+  nsString mEmail;
+};
+
+MsgAddressObject::MsgAddressObject(const nsAString &aName,
+    const nsAString &aEmail)
+: mName(aName),
+  mEmail(aEmail)
+{
+}
+
+NS_IMPL_ISUPPORTS1(MsgAddressObject, msgIAddressObject)
+
+NS_IMETHODIMP MsgAddressObject::GetName(nsAString &name)
+{
+  name = mName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP MsgAddressObject::GetEmail(nsAString &email)
+{
+  email = mEmail;
+  return NS_OK;
+}
+
+NS_IMETHODIMP MsgAddressObject::ToString(nsAString &display)
+{
+  nsMsgHeaderParser headerParser;
+  nsAutoString quotedString;
+  headerParser.MakeMimeAddress(mName, mEmail, quotedString);
+  headerParser.UnquotePhraseOrAddrWString(quotedString.get(), false,
+    getter_Copies(display));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgHeaderParser::MakeMailboxObject(const nsAString &aName,
+    const nsAString &aEmail, msgIAddressObject **retval)
+{
+  nsCOMPtr<msgIAddressObject> object = new MsgAddressObject(aName, aEmail);
+  object.forget(retval);
+  return NS_OK;
+}
+
+static MsgAddressObject *MakeSingleAddress(
+    const nsAString &aDisplay)
+{
+  // This is a wasteful copy, but the internal API does not have RFindChar on
+  // nsAString, only nsString.
+  nsString display(aDisplay);
+  // Strip leading/trailing whitespace
+  MsgCompressWhitespace(display);
+  nsCOMPtr<msgIAddressObject> object;
+  int32_t addrstart = display.RFindChar('<');
+  if (addrstart != -1)
+  {
+    // Adjust is used to strip off exactly one space char if it's present.
+    int32_t adjust = addrstart == 0 ? 0 : 1;
+    int32_t addrend = display.RFindChar('>');
+    return new MsgAddressObject(
+      StringHead(display, addrstart - adjust),
+      Substring(display, addrstart + 1, addrend - addrstart - 1));
+  }
+  else
+  {
+    return new MsgAddressObject(EmptyString(), display);
+  }
+}
+
+NS_IMETHODIMP nsMsgHeaderParser::MakeFromDisplayAddress(
+    const nsAString &aDisplay, uint32_t *count, msgIAddressObject ***retval)
+{
+  // We split on every comma, so long as a @ exists before that comma.
+  nsCOMArray<msgIAddressObject> addresses;
+  int32_t lastComma = -1;
+  while (!aDisplay.IsEmpty() && lastComma < (int32_t)aDisplay.Length())
+  {
+    // Find the next , that follows an email address (which must have an @).
+    int32_t atSign = aDisplay.FindChar('@', lastComma + 1);
+    // If there is no @, just consume the rest of the string as the "address"
+    if (atSign == -1)
+      atSign = aDisplay.Length() - 1;
+    int32_t nextComma = aDisplay.FindChar(',', atSign + 1);
+    if (nextComma == -1)
+      nextComma = aDisplay.Length();
+
+    // The substring from [lastComma + 1, nextComma) is an email address.
+    addresses.AppendElement(MakeSingleAddress(
+      Substring(aDisplay, lastComma + 1, nextComma - (lastComma + 1))));
+    
+    // Move lastComma along
+    lastComma = nextComma;
+  }
+
+  // Add all the elements to the output
+  msgIAddressObject **out = (msgIAddressObject **)NS_Alloc(
+    sizeof(msgIAddressObject*) * addresses.Length());
+  for (uint32_t i = 0; i < addresses.Length(); i++)
+    NS_IF_ADDREF(out[i] = addresses[i]);
+
+  *count = addresses.Length();
+  *retval = out;
+  return NS_OK;
+}
--- a/mailnews/mime/src/nsMsgHeaderParser.h
+++ b/mailnews/mime/src/nsMsgHeaderParser.h
@@ -11,16 +11,17 @@
 
 #ifndef nsMSGRFCPARSER_h__
 #define nsMSGRFCPARSER_h__
 
 #include "msgCore.h"
 #include "nsIMsgHeaderParser.h" /* include the interface we are going to support */
 #include "nsIMimeConverter.h"
 #include "comi18n.h"
+#include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 
  /*
   * RFC-822 parser
   */
 
 class nsMsgHeaderParser: public nsIMsgHeaderParser 
 {
--- a/mailnews/mime/test/unit/test_nsIMsgHeaderParser1.js
+++ b/mailnews/mime/test/unit/test_nsIMsgHeaderParser1.js
@@ -19,16 +19,16 @@ function run_test() {
     ["Joe Q. Public", "john.q.public@example.com",
      "\"Joe Q. Public\" <john.q.public@example.com>"],
     ["Giant; \"Big\" Box", "sysservices@example.net",
      "\"Giant; \\\"Big\\\" Box\" <sysservices@example.net>"],
   ];
 
   // Test - empty strings
 
-  do_check_eq(MailServices.headerParser.makeFullAddress("", ""), "");
+  do_check_eq(MailServices.headerParser.makeMimeAddress("", ""), "");
 
-  // Test - makeFullAddressWString
+  // Test - makeMimeAddress
 
   for (let i = 0; i < checks.length; ++i)
-    do_check_eq(MailServices.headerParser.makeFullAddress(checks[i][0], checks[i][1]),
+    do_check_eq(MailServices.headerParser.makeMimeAddress(checks[i][0], checks[i][1]),
                 checks[i][2]);
 }
new file mode 100644
--- /dev/null
+++ b/mailnews/mime/test/unit/test_nsIMsgHeaderParser4.js
@@ -0,0 +1,39 @@
+/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Test suite for nsIMsgHeaderParser::makeFromDisplayAddress
+ */
+
+Components.utils.import("resource:///modules/mailServices.js");
+
+function run_test() {
+  const checks =
+  [
+    { displayString: "",
+      addresses: [] },
+    { displayString: "test@foo.invalid",
+      addresses: [["", "test@foo.invalid"]] },
+    { displayString: "test@foo.invalid, test2@foo.invalid",
+      addresses: [["", "test@foo.invalid"],
+                  ["", "test2@foo.invalid"]] },
+    { displayString: "John Doe <test@foo.invalid>",
+      addresses: [["John Doe", "test@foo.invalid"]] },
+    { displayString: "Doe, John <test@foo.invalid>",
+      addresses: [["Doe, John", "test@foo.invalid"]] },
+    { displayString: "Doe, John <test@foo.invalid>, Bond, James <test2@foo.invalid>",
+      addresses: [["Doe, John", "test@foo.invalid"],
+                  ["Bond, James", "test2@foo.invalid"]] },
+  ];
+
+  // Test -  strings
+
+  for (let i = 0; i < checks.length; ++i) {
+    dump("Test " + i + "\n");
+    let addrs = MailServices.headerParser.makeFromDisplayAddress(checks[i].displayString, {});
+    let checkaddrs = checks[i].addresses;
+    do_check_eq(addrs.length, checkaddrs.length);
+    for (let j = 0; j < addrs.length; j++) {
+      do_check_eq(addrs[j].name, checkaddrs[j][0]);
+      do_check_eq(addrs[j].email, checkaddrs[j][1]);
+    }
+  }
+}
--- a/mailnews/mime/test/unit/xpcshell.ini
+++ b/mailnews/mime/test/unit/xpcshell.ini
@@ -1,20 +1,21 @@
 [DEFAULT]
 head = head_mime.js
 tail = tail_mime.js
 
 [test_EncodeMimePartIIStr_UTF8.js]
-[test_hidden_attachments.js]
+[test_alternate_p7m_handling.js]
 [test_attachment_size.js]
 [test_badContentType.js]
+[test_bug493544.js]
+[test_hidden_attachments.js]
+[test_message_attachment.js]
 [test_mimeContentType.js]
 [test_mimeStreaming.js]
 [test_nsIMsgHeaderParser1.js]
 [test_nsIMsgHeaderParser2.js]
 [test_nsIMsgHeaderParser3.js]
-[test_parser.js]
-[test_text_attachment.js]
-[test_message_attachment.js]
+[test_nsIMsgHeaderParser4.js]
 [test_parseHeadersWithArray.js]
+[test_parser.js]
 [test_rfc822_body.js]
-[test_bug493544.js]
-[test_alternate_p7m_handling.js]
+[test_text_attachment.js]
--- a/suite/mailnews/addrbook/abCommon.js
+++ b/suite/mailnews/addrbook/abCommon.js
@@ -565,17 +565,17 @@ function GenerateAddressFromCard(card)
   var email;
   if (card.isMailList) 
   {
     var directory = GetDirectoryFromURI(card.mailListURI);
     email = directory.description || card.displayName;
   }
   else 
     email = card.primaryEmail;
-  return MailServices.headerParser.makeFullAddress(card.displayName, email);
+  return MailServices.headerParser.makeMimeAddress(card.displayName, email);
 }
 
 function GetDirectoryFromURI(uri)
 {
   return MailServices.ab.getDirectory(uri);
 }
 
 // returns null if abURI is not a mailing list URI
--- a/suite/mailnews/compose/addressingWidgetOverlay.js
+++ b/suite/mailnews/compose/addressingWidgetOverlay.js
@@ -109,17 +109,18 @@ function Recipients2CompFields(msgCompFi
 
         switch (recipientType)
         {
           case "addr_to"    :
           case "addr_cc"    :
           case "addr_bcc"   :
           case "addr_reply" :
             try {
-              recipient = gMimeHeaderParser.reformatUnquotedAddresses(fieldValue);
+              let headerParser = MailServices.headerParser;
+              recipient = [headerParser.makeMimeAddress(fullValue.name, fullValue.email) for (fullValue of headerParser.makeFromDisplayAddress(fieldValue, {}))].join(", ");
             } catch (ex) {recipient = fieldValue;}
             break;
         }
 
         switch (recipientType)
         {
           case "addr_to"          : addrTo += to_Sep + recipient; to_Sep = ",";               break;
           case "addr_cc"          : addrCc += cc_Sep + recipient; cc_Sep = ",";               break;
@@ -147,18 +148,16 @@ function Recipients2CompFields(msgCompFi
   }
   else
     dump("Message Compose Error: msgCompFields is null (ExtractRecipients)");
 }
 
 function CompFields2Recipients(msgCompFields)
 {
   if (msgCompFields) {
-    gMimeHeaderParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
-
     var listbox = document.getElementById('addressingWidget');
     var newListBoxNode = listbox.cloneNode(false);
     var listBoxColsClone = listbox.firstChild.cloneNode(true);
     newListBoxNode.appendChild(listBoxColsClone);
     var templateNode = listbox.getElementsByTagName("listitem")[0];
     // dump("replacing child in comp fields 2 recips \n");
     listbox.parentNode.replaceChild(newListBoxNode, listbox);
     
@@ -202,18 +201,16 @@ function CompFields2Recipients(msgCompFi
     // If it's a new message, we need to add an extra empty recipient.
     if (!havePrimaryRecipient)
       _awSetInputAndPopup("", "addr_to", newListBoxNode, templateNode);
     awFitDummyRows(2);
 
     // CompFields2Recipients is called whenever a user replies or edits an existing message.
     // We want to add all of the recipients for this message to the ignore list for spell check 
     addRecipientsToIgnoreList((gCurrentIdentity ? gCurrentIdentity.identityName + ', ' : '') + msgTo + ', ' + msgCC + ', ' + msgBCC);
-
-    gMimeHeaderParser = null; //Release the mime parser
   }
 }
 
 function awSetInputAndPopupId(inputElem, popupElem, rowNumber)
 {
   popupElem.id = "addressCol1#" + rowNumber;
   inputElem.id = "addressCol2#" + rowNumber;
   inputElem.setAttribute("aria-labelledby", popupElem.id);
@@ -260,29 +257,18 @@ function awSetInputAndPopup(inputValue, 
         _awSetInputAndPopup(addressArray[index], popupValue, parentNode, templateNode);
   }
 }
 
 function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templateNode)
 {
   if (popupValue)
   {
-    var recipient;
-    for (var index = 0; index < inputArray.length; index++)
-    {
-      recipient = null;
-      if (gMimeHeaderParser)
-        try {
-          recipient =
-            gMimeHeaderParser.unquotePhraseOrAddrWString(inputArray[index], true);
-        } catch (ex) {};
-      if (!recipient)
-        recipient = inputArray[index];
+    for (let recipient of inputArray)
       _awSetInputAndPopup(recipient, popupValue, parentNode, templateNode);
-    }
   }
 }
 
 function awRemoveRecipients(msgCompFields, recipientType, recipientsList)
 {
   if (!msgCompFields)
     return;