diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mail/base/content/mailContext.js | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/comm/mail/base/content/mailContext.js b/comm/mail/base/content/mailContext.js new file mode 100644 index 0000000000..7b2a050b1e --- /dev/null +++ b/comm/mail/base/content/mailContext.js @@ -0,0 +1,822 @@ +/* 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/. */ + +// mailCommon.js +/* globals commandController */ + +// about:3pane and about:message must BOTH provide these: + +/* globals goDoCommand */ // globalOverlay.js +/* globals gDBView, gFolder, gViewWrapper, messengerBundle */ + +/* globals gEncryptedURIService */ // mailCommon.js + +var { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +XPCOMUtils.defineLazyModuleGetters(this, { + calendarDeactivator: + "resource:///modules/calendar/calCalendarDeactivator.jsm", + EnigmailURIs: "chrome://openpgp/content/modules/uris.jsm", + MailUtils: "resource:///modules/MailUtils.jsm", + PhishingDetector: "resource:///modules/PhishingDetector.jsm", + TagUtils: "resource:///modules/TagUtils.jsm", +}); + +/** + * Called by ContextMenuParent if this window is about:3pane, or is + * about:message but not contained by about:3pane. + * + * @returns {boolean} true if this function opened the context menu + */ +function openContextMenu({ data, target }, browser) { + if (window.browsingContext.parent != window.browsingContext.top) { + // Not sure how we'd get here, but let's not continue if we do. + return false; + } + + if (browser.getAttribute("context") != "mailContext") { + return false; + } + + mailContextMenu.setAsMessagePaneContextMenu(data, target.browsingContext); + let screenX = data.context.screenXDevPx / window.devicePixelRatio; + let screenY = data.context.screenYDevPx / window.devicePixelRatio; + let popup = document.getElementById("mailContext"); + popup.openPopupAtScreen(screenX, screenY, true); + + return true; +} + +var mailContextMenu = { + /** + * @type {XULPopupElement} + */ + _menupopup: null, + + // Commands handled by commandController. + _commands: { + "mailContext-editDraftMsg": "cmd_editDraftMsg", + "mailContext-newMsgFromTemplate": "cmd_newMsgFromTemplate", + "mailContext-editTemplateMsg": "cmd_editTemplateMsg", + "mailContext-openConversation": "cmd_openConversation", + "mailContext-replyNewsgroup": "cmd_replyGroup", + "mailContext-replySender": "cmd_replySender", + "mailContext-replyAll": "cmd_replyall", + "mailContext-replyList": "cmd_replylist", + "mailContext-forward": "cmd_forward", + "mailContext-forwardAsInline": "cmd_forwardInline", + "mailContext-forwardAsAttachment": "cmd_forwardAttachment", + "mailContext-multiForwardAsAttachment": "cmd_forwardAttachment", + "mailContext-redirect": "cmd_redirect", + "mailContext-cancel": "cmd_cancel", + "mailContext-editAsNew": "cmd_editAsNew", + "mailContext-addNewTag": "cmd_addTag", + "mailContext-manageTags": "cmd_manageTags", + "mailContext-tagRemoveAll": "cmd_removeTags", + "mailContext-markReadByDate": "cmd_markReadByDate", + "mailContext-markFlagged": "cmd_markAsFlagged", + "mailContext-archive": "cmd_archive", + "mailContext-moveToFolderAgain": "cmd_moveToFolderAgain", + "mailContext-decryptToFolder": "cmd_copyDecryptedTo", + "mailContext-delete": "cmd_deleteMessage", + "mailContext-ignoreThread": "cmd_killThread", + "mailContext-ignoreSubthread": "cmd_killSubthread", + "mailContext-watchThread": "cmd_watchThread", + "mailContext-saveAs": "cmd_saveAsFile", + "mailContext-print": "cmd_print", + "mailContext-downloadSelected": "cmd_downloadSelected", + }, + + // More commands handled by commandController, except these ones get + // disabled instead of hidden. + _alwaysVisibleCommands: { + "mailContext-markRead": "cmd_markAsRead", + "mailContext-markUnread": "cmd_markAsUnread", + "mailContext-markThreadAsRead": "cmd_markThreadAsRead", + "mailContext-markAllRead": "cmd_markAllRead", + "mailContext-markAsJunk": "cmd_markAsJunk", + "mailContext-markAsNotJunk": "cmd_markAsNotJunk", + "mailContext-recalculateJunkScore": "cmd_recalculateJunkScore", + }, + + /** + * If we have overridden the selection for the context menu. + * + * @see `setOverrideSelection` + * @type {boolean} + */ + _selectionIsOverridden: false, + + init() { + this._menupopup = document.getElementById("mailContext"); + this._menupopup.addEventListener("popupshowing", this); + this._menupopup.addEventListener("popuphidden", this); + this._menupopup.addEventListener("command", this); + }, + + handleEvent(event) { + switch (event.type) { + case "popupshowing": + this.onPopupShowing(event); + break; + case "popuphidden": + this.onPopupHidden(event); + break; + case "command": + this.onCommand(event); + break; + } + }, + + onPopupShowing(event) { + if (event.target == this._menupopup) { + this.fillMailContextMenu(event); + } + }, + + onPopupHidden(event) { + if (event.target == this._menupopup) { + this.clearOverrideSelection(); + } + }, + + onCommand(event) { + this.onMailContextMenuCommand(event); + }, + + /** + * Override the selection that this context menu should operate on. The + * effect lasts until `clearOverrideSelection` is called by `onPopupHidden`. + * + * @param {integer} index - The index of the row to use as selection. + */ + setOverrideSelection(index) { + this._selectionIsOverridden = true; + window.threadPane.saveSelection(); + window.threadTree._selection.selectEventsSuppressed = true; + window.threadTree._selection.select(index); + }, + + /** + * Has the real selection been overridden by a right-click on a message that + * wasn't selected? + * + * @type {boolean} + */ + get selectionIsOverridden() { + return this._selectionIsOverridden; + }, + + /** + * Clear the overriding selection, and go back to the previous selection. + */ + clearOverrideSelection() { + if (!window.threadTree) { + return; + } + if (this._selectionIsOverridden) { + window.threadTree._selection.selectEventsSuppressed = true; + window.threadPane.restoreSelection({ notify: false }); + this._selectionIsOverridden = false; + window.threadTree.invalidate(); + } + window.threadTree + .querySelector(".context-menu-target") + ?.classList.remove("context-menu-target"); + window.threadTree._selection.selectEventsSuppressed = false; + window.threadTree.table.body.focus(); + }, + + setAsThreadPaneContextMenu() { + delete this.browsingContext; + delete this.context; + delete this.selectionInfo; + this.inThreadTree = true; + + for (let id of [ + "mailContext-openInBrowser", + "mailContext-openLinkInBrowser", + "mailContext-copylink", + "mailContext-savelink", + "mailContext-reportPhishingURL", + "mailContext-addemail", + "mailContext-composeemailto", + "mailContext-copyemail", + "mailContext-copyimage", + "mailContext-saveimage", + "mailContext-copy", + "mailContext-selectall", + "mailContext-searchTheWeb", + ]) { + document.getElementById(id).hidden = true; + } + }, + + setAsMessagePaneContextMenu({ context, selectionInfo }, browsingContext) { + function showItem(id, show) { + let item = document.getElementById(id); + if (item) { + item.hidden = !show; + } + } + + delete this.inThreadTree; + this.browsingContext = browsingContext; + this.context = context; + this.selectionInfo = selectionInfo; + + // showItem("mailContext-openInBrowser", false); + showItem( + "mailContext-openLinkInBrowser", + context.onLink && !context.onMailtoLink + ); + showItem("mailContext-copylink", context.onLink && !context.onMailtoLink); + showItem("mailContext-savelink", context.onLink); + showItem( + "mailContext-reportPhishingURL", + context.onLink && !context.onMailtoLink + ); + showItem("mailContext-addemail", context.onMailtoLink); + showItem("mailContext-composeemailto", context.onMailtoLink); + showItem("mailContext-copyemail", context.onMailtoLink); + showItem("mailContext-copyimage", context.onImage); + showItem("mailContext-saveimage", context.onLoadedImage); + showItem( + "mailContext-copy", + selectionInfo && !selectionInfo.docSelectionIsCollapsed + ); + showItem("mailContext-selectall", true); + showItem( + "mailContext-searchTheWeb", + selectionInfo && !selectionInfo.docSelectionIsCollapsed + ); + + let searchTheWeb = document.getElementById("mailContext-searchTheWeb"); + if (!searchTheWeb.hidden) { + let key = "openSearch.label"; + let abbrSelection; + if (selectionInfo.text.length > 15) { + key += ".truncated"; + abbrSelection = selectionInfo.text.slice(0, 15); + } else { + abbrSelection = selectionInfo.text; + } + + searchTheWeb.label = messengerBundle.formatStringFromName(key, [ + Services.search.defaultEngine.name, + abbrSelection, + ]); + } + }, + + fillMailContextMenu(event) { + function showItem(id, show) { + let item = document.getElementById(id); + if (item) { + item.hidden = !show; + } + } + + function enableItem(id, enabled) { + let item = document.getElementById(id); + item.disabled = !enabled; + } + + function checkItem(id, checked) { + let item = document.getElementById(id); + if (item) { + // Convert truthy/falsy to boolean before string. + item.setAttribute("checked", !!checked); + } + } + + function setSingleSelection(id, show = true) { + showItem(id, numSelectedMessages == 1 && show); + enableItem(id, numSelectedMessages == 1); + } + + // Hide things that don't work yet. + for (let id of [ + "mailContext-openInBrowser", + "mailContext-recalculateJunkScore", + ]) { + showItem(id, false); + } + + let onSpecialItem = + this.context?.isContentSelected || + this.context?.onCanvas || + this.context?.onLink || + this.context?.onImage || + this.context?.onAudio || + this.context?.onVideo || + this.context?.onTextInput; + + for (let id of ["mailContext-tags", "mailContext-mark"]) { + showItem(id, !onSpecialItem); + } + + // Ask commandController about the commands it controls. + for (let [id, command] of Object.entries(this._commands)) { + showItem( + id, + !onSpecialItem && commandController.isCommandEnabled(command) + ); + } + for (let [id, command] of Object.entries(this._alwaysVisibleCommands)) { + showItem(id, !onSpecialItem); + enableItem(id, commandController.isCommandEnabled(command)); + } + + let inAbout3Pane = !!window.threadTree; + let inThreadTree = !!this.inThreadTree; + + let message = + gFolder || gViewWrapper.isSynthetic + ? gDBView?.hdrForFirstSelectedMessage + : top.messenger.msgHdrFromURI(window.gMessageURI); + let folder = message?.folder; + let isDummyMessage = !gViewWrapper.isSynthetic && !folder; + + let numSelectedMessages = isDummyMessage ? 1 : gDBView.numSelected; + let isNewsgroup = folder?.isSpecialFolder( + Ci.nsMsgFolderFlags.Newsgroup, + true + ); + let canMove = + numSelectedMessages >= 1 && !isNewsgroup && folder?.canDeleteMessages; + let canCopy = numSelectedMessages >= 1; + + setSingleSelection("mailContext-openNewTab", inThreadTree); + setSingleSelection("mailContext-openNewWindow", inThreadTree); + setSingleSelection( + "mailContext-openContainingFolder", + (!isDummyMessage && !inAbout3Pane) || gViewWrapper.isSynthetic + ); + setSingleSelection("mailContext-forward", !onSpecialItem); + setSingleSelection("mailContext-forwardAsMenu", !onSpecialItem); + showItem( + "mailContext-multiForwardAsAttachment", + numSelectedMessages > 1 && + commandController.isCommandEnabled("cmd_forwardAttachment") + ); + + if (isDummyMessage) { + showItem("mailContext-tags", false); + } else { + showItem("mailContext-tags", true); + this._initMessageTags(); + } + + showItem("mailContext-mark", !isDummyMessage); + checkItem("mailContext-markFlagged", message?.isFlagged); + + setSingleSelection("mailContext-copyMessageUrl", !!isNewsgroup); + // Disable move if we can't delete message(s) from this folder. + showItem("mailContext-moveMenu", canMove && !onSpecialItem); + showItem("mailContext-copyMenu", canCopy && !onSpecialItem); + + top.initMoveToFolderAgainMenu( + document.getElementById("mailContext-moveToFolderAgain") + ); + + // Show only if a message is actively selected in the DOM. + // extractFromEmail can't work on dummy messages. + showItem( + "mailContext-calendar-convert-menu", + numSelectedMessages == 1 && + !isDummyMessage && + calendarDeactivator.isCalendarActivated + ); + + document.l10n.setAttributes( + document.getElementById("mailContext-delete"), + message.flags & Ci.nsMsgMessageFlags.IMAPDeleted + ? "mail-context-undelete-messages" + : "mail-context-delete-messages", + { + count: numSelectedMessages, + } + ); + + checkItem( + "mailContext-ignoreThread", + folder?.msgDatabase.isIgnored(message?.messageKey) + ); + checkItem( + "mailContext-ignoreSubthread", + folder && message.flags & Ci.nsMsgMessageFlags.Ignored + ); + checkItem( + "mailContext-watchThread", + folder?.msgDatabase.isWatched(message?.messageKey) + ); + + showItem( + "mailContext-downloadSelected", + window.threadTree && numSelectedMessages > 1 + ); + + let lastItem; + for (let child of document.getElementById("mailContext").children) { + if (child.localName == "menuseparator") { + child.hidden = !lastItem || lastItem.localName == "menuseparator"; + } + if (!child.hidden) { + lastItem = child; + } + } + if (lastItem.localName == "menuseparator") { + lastItem.hidden = true; + } + + // The rest of this block sends menu information to WebExtensions. + + let selectionInfo = this.selectionInfo; + let isContentSelected = selectionInfo + ? !selectionInfo.docSelectionIsCollapsed + : false; + let textSelected = selectionInfo ? selectionInfo.text : ""; + let isTextSelected = !!textSelected.length; + + let tabmail = top.document.getElementById("tabmail"); + let subject = { + menu: event.target, + tab: tabmail ? tabmail.currentTabInfo : top, + isContentSelected, + isTextSelected, + onTextInput: this.context?.onTextInput, + onLink: this.context?.onLink, + onImage: this.context?.onImage, + onEditable: this.context?.onEditable, + srcUrl: this.context?.mediaURL, + linkText: this.context?.linkTextStr, + linkUrl: this.context?.linkURL, + selectionText: isTextSelected ? selectionInfo.fullText : undefined, + pageUrl: this.browsingContext?.currentURI?.spec, + }; + + if (inThreadTree) { + subject.displayedFolder = folder; + subject.selectedMessages = gDBView.getSelectedMsgHdrs(); + } + + subject.context = subject; + subject.wrappedJSObject = subject; + + Services.obs.notifyObservers(subject, "on-prepare-contextmenu"); + Services.obs.notifyObservers(subject, "on-build-contextmenu"); + }, + + onMailContextMenuCommand(event) { + // If commandController handles this command, ask it to do so. + if (event.target.id in this._commands) { + commandController.doCommand(this._commands[event.target.id], event); + return; + } + if (event.target.id in this._alwaysVisibleCommands) { + commandController.doCommand( + this._alwaysVisibleCommands[event.target.id], + event + ); + return; + } + + switch (event.target.id) { + // Links + // case "mailContext-openInBrowser": + // this._openInBrowser(); + // break; + case "mailContext-openLinkInBrowser": + // Only called in about:message. + top.openLinkExternally(this.context.linkURL); + break; + case "mailContext-copylink": + goDoCommand("cmd_copyLink"); + break; + case "mailContext-savelink": + top.saveURL( + this.context.linkURL, // URL + null, // originalURL + this.context.linkTextStr, // fileName + null, // filePickerTitleKey + true, // shouldBypassCache + false, // skipPrompt + null, // referrerInfo + null, // cookieJarSettings + this.browsingContext.window.document, // sourceDocument + null, // isContentWindowPrivate, + Services.scriptSecurityManager.getSystemPrincipal() // principal + ); + break; + case "mailContext-reportPhishingURL": + PhishingDetector.reportPhishingURL(this.context.linkURL); + break; + case "mailContext-addemail": + top.addEmail(this.context.linkURL); + break; + case "mailContext-composeemailto": + top.composeEmailTo( + this.context.linkURL, + gFolder + ? MailServices.accounts.getFirstIdentityForServer(gFolder.server) + : null + ); + break; + case "mailContext-copyemail": { + let addresses = top.getEmail(this.context.linkURL); + Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(Ci.nsIClipboardHelper) + .copyString(addresses); + break; + } + + // Images + case "mailContext-copyimage": + goDoCommand("cmd_copyImageContents"); + break; + case "mailContext-saveimage": + top.saveURL( + this.context.imageInfo.currentSrc, // URL + null, // originalURL + this.context.linkTextStr, // fileName + "SaveImageTitle", // filePickerTitleKey + true, // shouldBypassCache + false, // skipPrompt + null, // referrerInfo + null, // cookieJarSettings + this.browsingContext.window?.document, // sourceDocument + null, // isContentWindowPrivate, + Services.scriptSecurityManager.getSystemPrincipal() // principal + ); + break; + + // Edit + case "mailContext-copy": + goDoCommand("cmd_copy"); + break; + case "mailContext-selectall": + goDoCommand("cmd_selectAll"); + break; + + // Search + case "mailContext-searchTheWeb": + top.openWebSearch(this.selectionInfo.text); + break; + + // Open messages + case "mailContext-openNewTab": + top.OpenMessageInNewTab(gDBView.hdrForFirstSelectedMessage, { + event, + viewWrapper: gViewWrapper, + }); + break; + case "mailContext-openNewWindow": + top.MsgOpenNewWindowForMessage( + gDBView.hdrForFirstSelectedMessage, + gViewWrapper + ); + break; + case "mailContext-openContainingFolder": + MailUtils.displayMessageInFolderTab(gDBView.hdrForFirstSelectedMessage); + break; + + // Move/copy/archive/convert/delete + // (Move and Copy sub-menus are handled in the default case.) + case "mailContext-copyMessageUrl": { + let message = gDBView.hdrForFirstSelectedMessage; + let server = message?.folder?.server; + + if (!server) { + return; + } + + // TODO let backend construct URL and return as attribute + let url = + server.socketType == Ci.nsMsgSocketType.SSL ? "snews://" : "news://"; + url += server.hostName + ":" + server.port + "/" + message.messageId; + + Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(Ci.nsIClipboardHelper) + .copyString(url); + break; + } + + // Calendar Convert sub-menu + case "mailContext-calendar-convert-event-menuitem": + top.calendarExtract.extractFromEmail( + gDBView.hdrForFirstSelectedMessage, + true + ); + break; + case "mailContext-calendar-convert-task-menuitem": + top.calendarExtract.extractFromEmail( + gDBView.hdrForFirstSelectedMessage, + false + ); + break; + + // Save/print/download + default: { + if ( + document.getElementById("mailContext-moveMenu").contains(event.target) + ) { + commandController.doCommand("cmd_moveMessage", event.target._folder); + } else if ( + document.getElementById("mailContext-copyMenu").contains(event.target) + ) { + commandController.doCommand("cmd_copyMessage", event.target._folder); + } else if ( + document + .getElementById("mailContext-decryptToFolder") + .contains(event.target) + ) { + commandController.doCommand( + "cmd_copyDecryptedTo", + event.target._folder + ); + } + break; + } + } + }, + + // Tags sub-menu + + /** + * Refresh the contents of the tag popup menu/panel. + * Used for example for appmenu/Message/Tag panel. + * + * @param {Element} parent - Parent element that will contain the menu items. + * @param {string} [elementName] - Type of menu item, e.g. "menuitem", "toolbarbutton". + * @param {string} [classes] - Classes to set on the menu items. + */ + _initMessageTags() { + let parent = document.getElementById("mailContext-tagpopup"); + // Remove any existing non-static items (clear tags list before rebuilding it). + // There is a separator element above the dynamically added tag elements, so + // remove dynamically added elements below the separator. + while (parent.lastElementChild.localName == "menuitem") { + parent.lastElementChild.remove(); + } + + // Create label and accesskey for the static "remove all tags" item. + let removeItem = document.getElementById("mailContext-tagRemoveAll"); + removeItem.label = messengerBundle.GetStringFromName( + "mailnews.tags.remove" + ); + + // Rebuild the list. + let message = gDBView.hdrForFirstSelectedMessage; + let currentTags = message + ? message.getStringProperty("keywords").split(" ") + : []; + let index = 1; + + for (let tagInfo of MailServices.tags.getAllTags()) { + let msgHasTag = currentTags.includes(tagInfo.key); + if (tagInfo.ordinal.includes("~AUTOTAG") && !msgHasTag) { + return; + } + + let item = document.createXULElement("menuitem"); + item.accessKey = index < 10 ? index : ""; + item.label = messengerBundle.formatStringFromName( + "mailnews.tags.format", + [item.accessKey, tagInfo.tag] + ); + item.setAttribute("type", "checkbox"); + if (msgHasTag) { + item.setAttribute("checked", "true"); + } + item.value = tagInfo.key; + item.addEventListener("command", event => + this._toggleMessageTag( + tagInfo.key, + item.getAttribute("checked") == "true" + ) + ); + if (tagInfo.color) { + item.style.color = tagInfo.color; + } + parent.appendChild(item); + + index++; + } + }, + + removeAllMessageTags() { + let selectedMessages = gDBView.getSelectedMsgHdrs(); + if (!selectedMessages.length) { + return; + } + + let messages = []; + let allKeys = MailServices.tags + .getAllTags() + .map(t => t.key) + .join(" "); + let prevHdrFolder = null; + + // This crudely handles cross-folder virtual folders with selected + // messages that spans folders, by coalescing consecutive messages in the + // selection that happen to be in the same folder. nsMsgSearchDBView does + // this better, but nsIMsgDBView doesn't handle commands with arguments, + // and untag takes a key argument. Furthermore, we only delete known tags, + // keeping other keywords like (non)junk intact. + for (let i = 0; i < selectedMessages.length; ++i) { + let msgHdr = selectedMessages[i]; + if (prevHdrFolder != msgHdr.folder) { + if (prevHdrFolder) { + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); + } + messages = []; + prevHdrFolder = msgHdr.folder; + } + messages.push(msgHdr); + } + if (prevHdrFolder) { + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); + } + }, + + _toggleMessageTag(key, addKey) { + let messages = []; + let selectedMessages = gDBView.getSelectedMsgHdrs(); + let toggler = addKey + ? "addKeywordsToMessages" + : "removeKeywordsFromMessages"; + let prevHdrFolder = null; + // this crudely handles cross-folder virtual folders with selected messages + // that spans folders, by coalescing consecutive msgs in the selection + // that happen to be in the same folder. nsMsgSearchDBView does this + // better, but nsIMsgDBView doesn't handle commands with arguments, + // and (un)tag takes a key argument. + for (let i = 0; i < selectedMessages.length; ++i) { + let msgHdr = selectedMessages[i]; + if (prevHdrFolder != msgHdr.folder) { + if (prevHdrFolder) { + prevHdrFolder[toggler](messages, key); + } + messages = []; + prevHdrFolder = msgHdr.folder; + } + messages.push(msgHdr); + } + if (prevHdrFolder) { + prevHdrFolder[toggler](messages, key); + } + }, + + /** + * Toggle the state of a message tag on the selected messages (based on the + * state of the first selected message, like for starring). + * + * @param {number} keyNumber - The number (1 through 9) associated with the tag. + */ + _toggleMessageTagKey(keyNumber) { + let msgHdr = gDBView.hdrForFirstSelectedMessage; + if (!msgHdr) { + return; + } + + let tagArray = MailServices.tags.getAllTags(); + if (keyNumber > tagArray.length) { + return; + } + + let key = tagArray[keyNumber - 1].key; + let curKeys = msgHdr.getStringProperty("keywords").split(" "); + if (msgHdr.label) { + curKeys.push("$label" + msgHdr.label); + } + let addKey = !curKeys.includes(key); + + this._toggleMessageTag(key, addKey); + }, + + addTag() { + top.openDialog( + "chrome://messenger/content/newTagDialog.xhtml", + "", + "chrome,titlebar,modal,centerscreen", + { + result: "", + okCallback: (name, color) => { + MailServices.tags.addTag(name, color, ""); + let key = MailServices.tags.getKeyForTag(name); + TagUtils.addTagToAllDocumentSheets(key, color); + + this._toggleMessageTag(key, true); + return true; + }, + } + ); + }, +}; |