From 9b8912dd85b549f0d4b0b4c430d3f186a2ef4422 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sun, 25 May 2014 18:51:59 -0500 Subject: [PATCH] Bug 942638 - Clean up the multi-message summary code, part 7: Move FormatDisplayName and friends to a JS module (to help Mail Summaries); r=mconley --- mail/base/content/msgHdrViewOverlay.js | 53 +--------- mail/base/content/multimessageview.js | 52 +++------- mail/base/modules/displayNameUtils.js | 128 +++++++++++++++++++++++++ mail/base/modules/moz.build | 1 + mail/installer/removed-files.in | 1 + 5 files changed, 143 insertions(+), 92 deletions(-) create mode 100644 mail/base/modules/displayNameUtils.js diff --git a/mail/base/content/msgHdrViewOverlay.js b/mail/base/content/msgHdrViewOverlay.js index 00629862ff..74e725587d 100644 --- a/mail/base/content/msgHdrViewOverlay.js +++ b/mail/base/content/msgHdrViewOverlay.js @@ -9,6 +9,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource:///modules/displayNameUtils.js"); Components.utils.import("resource:///modules/mailServices.js"); Components.utils.import("resource:///modules/gloda/utils.js"); let {Status: statusUtils} = @@ -1249,59 +1250,9 @@ function updateEmailAddressNode(emailAddressNode, address) UpdateEmailNodeDetails(address.emailAddress, emailAddressNode); } -/** - * Take an email address and compose a sensible display name based on the - * header display name and/or the display name from the address book. If no - * appropriate name can be made (e.g. there is no card for this address), - * returns |null|. - * - * @param aEmailAddress the email address to format - * @param aHeaderDisplayName the display name from the header, if any - * @param aContext the field being formatted (e.g. "to", "from") - * @param aCard the address book card, if any - * @return The formatted display name, or null - */ -function FormatDisplayName(aEmailAddress, aHeaderDisplayName, aContext, aCard) -{ - var displayName = null; - var identity = getBestIdentity(accountManager.allIdentities, aEmailAddress); - var card = aCard || getCardForEmail(aEmailAddress).card; - - // If this address is one of the user's identities... - if (aEmailAddress == identity.email) { - var bundle = document.getElementById("bundle_messenger"); - // ...pick a localized version of the word "Me" appropriate to this - // specific header; fall back to the version used by the "to" header - // if nothing else is available. - try { - displayName = bundle.getString("header" + aContext + "FieldMe"); - } catch (ex) { - displayName = bundle.getString("headertoFieldMe"); - } - - // Make sure we have an unambiguous name if there are multiple identities - if (accountManager.allIdentities.length > 1) - displayName += " <"+identity.email+">"; - } - - // If we don't have a card, refuse to generate a display name. Places calling - // this are then responsible for falling back to something else (e.g. the - // value from the message header). - if (card) { - if (!displayName && aHeaderDisplayName) - displayName = aHeaderDisplayName; - - // getProperty may return a "1" or "0" string, we want a boolean - if (!displayName || card.getProperty("PreferDisplayName", true) != false) - displayName = card.displayName || null; - } - - return displayName; -} - function UpdateEmailNodeDetails(aEmailAddress, aDocumentNode, aCardDetails) { // If we haven't been given specific details, search for a card. - var cardDetails = aCardDetails || getCardForEmail(aEmailAddress); + var cardDetails = aCardDetails || GetCardForEmail(aEmailAddress); aDocumentNode.cardDetails = cardDetails; if (!cardDetails.card) { diff --git a/mail/base/content/multimessageview.js b/mail/base/content/multimessageview.js index 52327295db..3323b01d36 100644 --- a/mail/base/content/multimessageview.js +++ b/mail/base/content/multimessageview.js @@ -6,13 +6,10 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource:///modules/gloda/gloda.js"); Components.utils.import("resource:///modules/gloda/connotent.js"); Components.utils.import("resource:///modules/gloda/mimemsg.js"); +Components.utils.import("resource:///modules/displayNameUtils.js"); Components.utils.import("resource:///modules/mailServices.js"); Components.utils.import("resource:///modules/templateUtils.js"); -// This is executed in the context of the message summary page, not main chrome, -// but we need to access a few things from the main window. -var global = window.top; - var gMessenger = Components.classes["@mozilla.org/messenger;1"] .createInstance(Components.interfaces.nsIMessenger); @@ -26,34 +23,6 @@ XPCOMUtils.defineLazyGetter(this, "formatString", function() { }; }); -/** - * Format the display name for the multi-message/thread summaries. First, try - * using FormatDisplayName, then fall back to the header's display name or the - * address. - * - * @param aHeaderParser An instance of |nsIMsgHeaderParser|. - * @param aHeaderValue The raw header value. - * @param aContext The context of the header field (e.g. "to", "from"). - * @return The formatted display name. - */ -function _mm_FormatDisplayName(aHeaderParser, aHeaderValue, aContext) { - let addresses = {}; - let fullNames = {}; - let names = {}; - let numAddresses = aHeaderParser.parseHeadersWithArray(aHeaderValue, - addresses, names, fullNames); - - if (numAddresses > 0) { - return global.FormatDisplayName( - addresses.value[0], names.value[0], aContext - ) || names.value[0] || addresses.value[0]; - } - else { - // Something strange happened, just return the raw header value. - return aHeaderValue; - } -} - /** * The MultiMessageSummary class is responsible for populating the message pane * with a reasonable summary of a set of messages. @@ -124,7 +93,8 @@ MultiMessageSummary.prototype = { // Enable/disable the archive button as appropriate. let archiveBtn = document.getElementById("hdrArchiveButton"); - archiveBtn.collapsed = !global.gFolderDisplay.canArchiveSelectedMessages; + archiveBtn.collapsed = !window.top.gFolderDisplay + .canArchiveSelectedMessages; let summarizer = this._summarizers[aType]; if (!summarizer) @@ -205,8 +175,8 @@ MultiMessageSummary.prototype = { let authorNode = document.createElement("span"); authorNode.classList.add("author"); - authorNode.textContent = _mm_FormatDisplayName( - MailServices.headerParser, message.mime2DecodedAuthor, "from" + authorNode.textContent = FormatDisplayNameList( + message.mime2DecodedAuthor, "from" ); if (aOptions && aOptions.showSubject) { @@ -218,7 +188,7 @@ MultiMessageSummary.prototype = { subjectNode.textContent = message.mime2DecodedSubject || formatString("noSubject"); subjectNode.addEventListener("click", function() { - global.gFolderDisplay.selectMessages(thread); + window.top.gFolderDisplay.selectMessages(thread); }, false); itemHeaderNode.appendChild(subjectNode); @@ -246,8 +216,8 @@ MultiMessageSummary.prototype = { authorNode.classList.add("primary_header", "link"); authorNode.addEventListener("click", function() { - global.gFolderDisplay.selectMessage(message); - global.document.getElementById("messagepane").focus(); + window.top.gFolderDisplay.selectMessage(message); + window.top.document.getElementById("messagepane").focus(); }, false); itemHeaderNode.appendChild(authorNode); } @@ -569,9 +539,9 @@ MultipleSelectionSummarizer.prototype = { let threads = {}; let numThreads = 0; for (let [,msgHdr] in Iterator(aMessages)) { - let viewThreadId = global.gFolderDisplay.view.dbView - .getThreadContainingMsgHdr(msgHdr) - .threadKey; + let viewThreadId = window.top.gFolderDisplay.view.dbView + .getThreadContainingMsgHdr(msgHdr) + .threadKey; if (!(viewThreadId in threads)) { threads[viewThreadId] = [msgHdr]; numThreads++; diff --git a/mail/base/modules/displayNameUtils.js b/mail/base/modules/displayNameUtils.js new file mode 100644 index 0000000000..c627fc3142 --- /dev/null +++ b/mail/base/modules/displayNameUtils.js @@ -0,0 +1,128 @@ +/* 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/. */ + +Components.utils.import("resource:///modules/iteratorUtils.jsm"); +Components.utils.import("resource:///modules/mailServices.js"); +Components.utils.import("resource:///modules/StringBundle.js"); + +var EXPORTED_SYMBOLS = [ + "GetCardForEmail", "FormatDisplayName", "FormatDisplayNameList", +]; + +// XXX: Maybe the strings for this file should go in a separate bundle? +var gMessengerBundle = new StringBundle( + "chrome://messenger/locale/messenger.properties" +); + +/** + * Returns an object with two properties, .book and .card. If the email address + * is found in the address books, then the book will contain an nsIAbDirectory, + * and card will contain an nsIAbCard. If the email address is not found, both + * items will contain null. + * + * @param aEmailAddress The address to look for. + * @return An object with two properties, .book and .card. + */ +function GetCardForEmail(aEmailAddress) { + // Email address is searched for in any of the address books that support + // the cardForEmailAddress function. + // Future expansion could be to domain matches + let books = MailServices.ab.directories; + for (let book in fixIterator(books, Components.interfaces.nsIAbDirectory)) { + try { + let card = book.cardForEmailAddress(aEmailAddress); + if (card) + return { book: book, card: card }; + } + catch (ex) {} + } + + return { book: null, card: null }; +} + +function _getIdentityForAddress(aEmailAddress) { + let emailAddress = aEmailAddress.toLowerCase(); + for (let identity in fixIterator(MailServices.accounts.allIdentities, + Components.interfaces.nsIMsgIdentity)) { + if (!identity.email) + continue; + if (emailAddress == identity.email.toLowerCase()) + return identity; + } + return null; +} + +/** + * Take an email address and compose a sensible display name based on the + * header display name and/or the display name from the address book. If no + * appropriate name can be made (e.g. there is no card for this address), + * returns |null|. + * + * @param aEmailAddress The email address to format. + * @param aHeaderDisplayName The display name from the header, if any. + * @param aContext The field being formatted (e.g. "to", "from"). + * @param aCard The address book card, if any. + * @return The formatted display name, or null. + */ +function FormatDisplayName(aEmailAddress, aHeaderDisplayName, aContext, aCard) +{ + var displayName = null; + var identity = _getIdentityForAddress(aEmailAddress); + var card = aCard || GetCardForEmail(aEmailAddress).card; + + // If this address is one of the user's identities... + if (identity) { + // ...pick a localized version of the word "Me" appropriate to this + // specific header; fall back to the version used by the "to" header + // if nothing else is available. + try { + displayName = gMessengerBundle.getString("header" + aContext + "FieldMe"); + } catch (e) { + displayName = gMessenberBundle.getString("headertoFieldMe"); + } + + // Make sure we have an unambiguous name if there are multiple identities + if (MailServices.accounts.allIdentities.length > 1) + displayName += " <" + identity.email + ">"; + } + + // If we don't have a card, refuse to generate a display name. Places calling + // this are then responsible for falling back to something else (e.g. the + // value from the message header). + if (card) { + if (!displayName && aHeaderDisplayName) + displayName = aHeaderDisplayName; + + // getProperty may return a "1" or "0" string, we want a boolean + if (!displayName || card.getProperty("PreferDisplayName", true) != false) + displayName = card.displayName || null; + } + + return displayName; +} + +/** + * Format the display name from a list of addresses. First, try using + * FormatDisplayName, then fall back to the header's display name or the + * address. + * + * @param aHeaderValue The raw header value. + * @param aContext The context of the header field (e.g. "to", "from"). + * @return The formatted display name. + */ +function FormatDisplayNameList(aHeaderValue, aContext) { + let addresses = {}, fullNames = {}, names = {}; + let numAddresses = MailServices.headerParser.parseHeadersWithArray( + aHeaderValue, addresses, names, fullNames + ); + + if (numAddresses > 0) { + return FormatDisplayName(addresses.value[0], names.value[0], aContext) || + names.value[0] || addresses.value[0]; + } + else { + // Something strange happened, just return the raw header value. + return aHeaderValue; + } +} diff --git a/mail/base/modules/moz.build b/mail/base/modules/moz.build index 7a6cdf4816..f427d2c3a2 100644 --- a/mail/base/modules/moz.build +++ b/mail/base/modules/moz.build @@ -6,6 +6,7 @@ EXTRA_JS_MODULES += [ 'attachmentChecker.js', 'dbViewWrapper.js', + 'displayNameUtils.js', 'distribution.js', 'glodaWebSearch.js', 'MailConsts.js', diff --git a/mail/installer/removed-files.in b/mail/installer/removed-files.in index 8f0aa1f4e4..9e560bab9d 100644 --- a/mail/installer/removed-files.in +++ b/mail/installer/removed-files.in @@ -577,6 +577,7 @@ regxpcom.exe modules/attachmentChecker.js modules/ctypes.jsm modules/dbViewWrapper.js + modules/displayNameutils.js modules/debug.js modules/errUtils.js modules/folderUtils.jsm