Bug 355520 - Show custom account colors in the message compose From field. r=freaktechnik
Differential Revision: https://phabricator.services.mozilla.com/D213173 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
9d69d1739e
Коммит
3974b3409d
|
@ -49,6 +49,7 @@ var { ExtensionParent } = ChromeUtils.importESModule(
|
|||
ChromeUtils.defineESModuleGetters(this, {
|
||||
BondOpenPGP: "chrome://openpgp/content/BondOpenPGP.sys.mjs",
|
||||
EnigmailKeyRing: "chrome://openpgp/content/modules/keyRing.sys.mjs",
|
||||
FolderTreeProperties: "resource:///modules/FolderTreeProperties.sys.mjs",
|
||||
FolderUtils: "resource:///modules/FolderUtils.sys.mjs",
|
||||
MailUtils: "resource:///modules/MailUtils.sys.mjs",
|
||||
SelectionUtils: "resource://gre/modules/SelectionUtils.sys.mjs",
|
||||
|
@ -379,6 +380,25 @@ const keyObserver = {
|
|||
},
|
||||
};
|
||||
|
||||
const accountObserver = {
|
||||
observe(subject, topic) {
|
||||
if (topic == "server-color-changed") {
|
||||
// Refresh the full list to make sure we're not dealing with stale data.
|
||||
const identityList = document.getElementById("msgIdentity");
|
||||
if (identityList) {
|
||||
const currentKey = identityList.getAttribute("identitykey");
|
||||
identityList.menupopup.replaceChildren();
|
||||
FillIdentityList(identityList);
|
||||
identityList.selectedItem = identityList.getElementsByAttribute(
|
||||
"identitykey",
|
||||
currentKey
|
||||
)[0];
|
||||
LoadIdentity(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Non translatable international shortcuts.
|
||||
var SHOW_TO_KEY = "T";
|
||||
var SHOW_CC_KEY = "C";
|
||||
|
@ -4590,9 +4610,12 @@ async function ComposeStartup() {
|
|||
messageEditor.addEventListener("paste", onPasteOrDrop);
|
||||
messageEditor.addEventListener("drop", onPasteOrDrop);
|
||||
|
||||
await FolderTreeProperties.ready;
|
||||
|
||||
const identityList = document.getElementById("msgIdentity");
|
||||
if (identityList) {
|
||||
FillIdentityList(identityList);
|
||||
Services.obs.addObserver(accountObserver, "server-color-changed");
|
||||
}
|
||||
|
||||
if (!params) {
|
||||
|
@ -5754,6 +5777,8 @@ function ComposeUnload() {
|
|||
// Stop observing dictionary removals.
|
||||
dictionaryRemovalObserver.removeObserver();
|
||||
|
||||
Services.obs.removeObserver(accountObserver, "server-color-changed");
|
||||
|
||||
if (gMsgCompose) {
|
||||
// Notify the SendListener that Send has been aborted and Stopped
|
||||
gMsgCompose.onSendNotPerformed(null, Cr.NS_ERROR_ABORT);
|
||||
|
@ -7656,6 +7681,7 @@ function toggleAttachmentAnimation() {
|
|||
function FillIdentityList(menulist) {
|
||||
const accounts = FolderUtils.allAccountsSorted(true);
|
||||
|
||||
let hasCustomColor = false;
|
||||
let accountHadSeparator = false;
|
||||
let firstAccountWithIdentities = true;
|
||||
for (const account of accounts) {
|
||||
|
@ -7686,6 +7712,13 @@ function FillIdentityList(menulist) {
|
|||
identity.fullAddress,
|
||||
account.incomingServer.prettyName
|
||||
);
|
||||
const color = FolderTreeProperties.getColor(
|
||||
account.incomingServer.rootFolder.URI
|
||||
);
|
||||
if (color) {
|
||||
hasCustomColor = true;
|
||||
}
|
||||
item.style.setProperty("--icon-color", color ?? "");
|
||||
item.setAttribute("identitykey", identity.key);
|
||||
item.setAttribute("accountkey", account.key);
|
||||
if (i == 0) {
|
||||
|
@ -7702,6 +7735,8 @@ function FillIdentityList(menulist) {
|
|||
}
|
||||
}
|
||||
|
||||
menulist.classList.toggle("has-custom-color", hasCustomColor);
|
||||
|
||||
menulist.menupopup.appendChild(document.createXULElement("menuseparator"));
|
||||
menulist.menupopup
|
||||
.appendChild(document.createXULElement("menuitem"))
|
||||
|
@ -9402,6 +9437,11 @@ function LoadIdentity(startup) {
|
|||
accountKey = identityElement.selectedItem.getAttribute("accountkey");
|
||||
identityElement.setAttribute("accountkey", accountKey);
|
||||
|
||||
identityElement.style.setProperty(
|
||||
"--icon-color",
|
||||
identityElement.selectedItem.style.getPropertyValue("--icon-color") ?? ""
|
||||
);
|
||||
|
||||
// Update the addressing options only if a new account was selected.
|
||||
if (prevKey != getCurrentAccountKey()) {
|
||||
hideIrrelevantAddressingOptions(accountKey, prevKey);
|
||||
|
|
|
@ -8,11 +8,17 @@ var {
|
|||
click_account_tree_row,
|
||||
get_account_tree_listitem,
|
||||
open_advanced_settings,
|
||||
remove_account,
|
||||
} = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/AccountManagerHelpers.sys.mjs"
|
||||
);
|
||||
|
||||
var { close_compose_window, compose_window_ready } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/ComposeHelpers.sys.mjs"
|
||||
);
|
||||
var { content_tab_e } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/ContentTabHelpers.sys.mjs"
|
||||
);
|
||||
|
||||
var { MailServices } = ChromeUtils.importESModule(
|
||||
"resource:///modules/MailServices.sys.mjs"
|
||||
);
|
||||
|
@ -20,13 +26,17 @@ var { FeedUtils } = ChromeUtils.importESModule(
|
|||
"resource:///modules/FeedUtils.sys.mjs"
|
||||
);
|
||||
|
||||
var { content_tab_e } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/ContentTabHelpers.sys.mjs"
|
||||
var { promise_new_window } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/WindowHelpers.sys.mjs"
|
||||
);
|
||||
|
||||
var gPopAccount, gOriginalAccountCount, gFeedAccount, gLocalAccount;
|
||||
var gPopAccount,
|
||||
gOriginalAccountCount,
|
||||
gFeedAccount,
|
||||
gLocalAccount,
|
||||
gComposeCtrl;
|
||||
|
||||
add_setup(function () {
|
||||
add_setup(async function () {
|
||||
// There may be pre-existing accounts from other tests.
|
||||
gOriginalAccountCount = MailServices.accounts.allServers.length;
|
||||
|
||||
|
@ -53,11 +63,15 @@ add_setup(function () {
|
|||
Assert.equal(
|
||||
MailServices.accounts.allServers.length,
|
||||
gOriginalAccountCount + 2,
|
||||
"there should be one more account"
|
||||
"there should be two more accounts"
|
||||
);
|
||||
|
||||
const composePromise = promise_new_window("msgcompose");
|
||||
EventUtils.synthesizeKey("n", { accelKey: true });
|
||||
gComposeCtrl = await compose_window_ready(composePromise);
|
||||
});
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
registerCleanupFunction(async function () {
|
||||
// Remove our test account to leave the profile clean.
|
||||
if (gPopAccount) {
|
||||
MailServices.accounts.removeAccount(gPopAccount);
|
||||
|
@ -72,6 +86,7 @@ registerCleanupFunction(function () {
|
|||
gOriginalAccountCount,
|
||||
"There should be only the original accounts left."
|
||||
);
|
||||
await close_compose_window(gComposeCtrl);
|
||||
});
|
||||
|
||||
add_task(async function test_pop_account_color() {
|
||||
|
@ -82,7 +97,8 @@ add_task(async function test_pop_account_color() {
|
|||
|
||||
add_task(async function test_feed_account_color() {
|
||||
await open_advanced_settings(async function (tab) {
|
||||
await subtest_account_color(tab, gFeedAccount);
|
||||
// Feed accounts don't have an identity to send email from.
|
||||
await subtest_account_color(tab, gFeedAccount, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -92,7 +108,14 @@ add_task(async function test_local_account_color() {
|
|||
});
|
||||
});
|
||||
|
||||
async function subtest_account_color(tab, account) {
|
||||
/**
|
||||
*
|
||||
* @param {XULElement} tab - The account settings tab.
|
||||
* @param {msIMsgAccount} account - The account ot test.
|
||||
* @param {boolean} [skipCompose=false] - If the test should skip checking for
|
||||
* the account color in the compose windows.
|
||||
*/
|
||||
async function subtest_account_color(tab, account, skipCompose = false) {
|
||||
const customColor = "#ff0000";
|
||||
const accountRow = get_account_tree_listitem(account.key, tab);
|
||||
const accountTree = content_tab_e(tab, "accounttree");
|
||||
|
@ -133,6 +156,18 @@ async function subtest_account_color(tab, account) {
|
|||
"The account icon in folder pane should have a custom color"
|
||||
);
|
||||
|
||||
if (!skipCompose) {
|
||||
Assert.equal(
|
||||
gComposeCtrl.document
|
||||
.querySelector(
|
||||
`#msgIdentityPopup menuitem[accountkey="${account.key}"]`
|
||||
)
|
||||
.style.getPropertyValue("--icon-color"),
|
||||
customColor,
|
||||
"The identity menuitem in the message compose should have a custom color"
|
||||
);
|
||||
}
|
||||
|
||||
// Switch back to account settings.
|
||||
tabmail.switchToTab(1);
|
||||
|
||||
|
@ -159,6 +194,17 @@ async function subtest_account_color(tab, account) {
|
|||
"The account icon in folder pane should not have a custom color"
|
||||
);
|
||||
|
||||
if (!skipCompose) {
|
||||
Assert.ok(
|
||||
!gComposeCtrl.document
|
||||
.querySelector(
|
||||
`#msgIdentityPopup menuitem[accountkey="${account.key}"]`
|
||||
)
|
||||
.style.getPropertyValue("--icon-color"),
|
||||
"The identity menuitem in the message compose should not have a custom color"
|
||||
);
|
||||
}
|
||||
|
||||
// Switch back to account settings.
|
||||
tabmail.switchToTab(1);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ reason = Cannot open the Format menu
|
|||
[browser_forwardUTF8.js]
|
||||
[browser_forwardedContent.js]
|
||||
[browser_forwardedEmlActions.js]
|
||||
[browser_identityColors.js]
|
||||
[browser_imageDisplay.js]
|
||||
[browser_imageInsertionDialog.js]
|
||||
[browser_indentOutdent.js]
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var { close_compose_window, compose_window_ready } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/ComposeHelpers.sys.mjs"
|
||||
);
|
||||
var { MailServices } = ChromeUtils.importESModule(
|
||||
"resource:///modules/MailServices.sys.mjs"
|
||||
);
|
||||
var { click_menus_in_sequence, promise_new_window } =
|
||||
ChromeUtils.importESModule(
|
||||
"resource://testing-common/mail/WindowHelpers.sys.mjs"
|
||||
);
|
||||
var { AccountManagerUtils } = ChromeUtils.importESModule(
|
||||
"resource:///modules/AccountManagerUtils.sys.mjs"
|
||||
);
|
||||
|
||||
var gPopAccount,
|
||||
gOriginalAccountCount,
|
||||
gLocalAccount,
|
||||
gComposeCtrl,
|
||||
popAMUtils,
|
||||
localAMUtils;
|
||||
|
||||
add_setup(async function () {
|
||||
// There may be pre-existing accounts from other tests.
|
||||
gOriginalAccountCount = MailServices.accounts.allServers.length;
|
||||
|
||||
// Create a POP server.
|
||||
const popServer = MailServices.accounts
|
||||
.createIncomingServer("nobody", "foo.invalid", "pop3")
|
||||
.QueryInterface(Ci.nsIPop3IncomingServer);
|
||||
|
||||
const identity = MailServices.accounts.createIdentity();
|
||||
identity.email = "tinderbox@foo.invalid";
|
||||
|
||||
gPopAccount = MailServices.accounts.createAccount();
|
||||
gPopAccount.incomingServer = popServer;
|
||||
gPopAccount.addIdentity(identity);
|
||||
popAMUtils = new AccountManagerUtils(gPopAccount);
|
||||
popAMUtils.updateServerColor("#ff0000");
|
||||
|
||||
// Get the local folder account.
|
||||
gLocalAccount = MailServices.accounts.findAccountForServer(
|
||||
MailServices.accounts.localFoldersServer
|
||||
);
|
||||
localAMUtils = new AccountManagerUtils(gLocalAccount);
|
||||
localAMUtils.updateServerColor("#0000ff");
|
||||
|
||||
Assert.equal(
|
||||
MailServices.accounts.allServers.length,
|
||||
gOriginalAccountCount + 1,
|
||||
"there should be one more account"
|
||||
);
|
||||
|
||||
const composePromise = promise_new_window("msgcompose");
|
||||
EventUtils.synthesizeKey("n", { accelKey: true });
|
||||
gComposeCtrl = await compose_window_ready(composePromise);
|
||||
});
|
||||
|
||||
registerCleanupFunction(async function () {
|
||||
// Remove our test account to leave the profile clean.
|
||||
if (gPopAccount) {
|
||||
MailServices.accounts.removeAccount(gPopAccount);
|
||||
gPopAccount = null;
|
||||
}
|
||||
Assert.equal(
|
||||
MailServices.accounts.allServers.length,
|
||||
gOriginalAccountCount,
|
||||
"There should be only the original accounts left."
|
||||
);
|
||||
await close_compose_window(gComposeCtrl);
|
||||
});
|
||||
|
||||
add_task(async function test_compose_identity_colors() {
|
||||
const rgb2hex = rgb =>
|
||||
`#${rgb
|
||||
.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
||||
.slice(1)
|
||||
.map(n => parseInt(n, 10).toString(16).padStart(2, "0"))
|
||||
.join("")}`;
|
||||
|
||||
const identityList = gComposeCtrl.document.getElementById("msgIdentity");
|
||||
identityList.selectedIndex = 1;
|
||||
Assert.ok(
|
||||
identityList.classList.contains("has-custom-color"),
|
||||
"The identity list should display custom account colors"
|
||||
);
|
||||
|
||||
let pseudoStyle = getComputedStyle(identityList._labelBox, "::before");
|
||||
Assert.equal(
|
||||
pseudoStyle.display,
|
||||
"block",
|
||||
"The ::before pseudo element should be visible"
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
rgb2hex(
|
||||
getComputedStyle(
|
||||
identityList.menupopup.querySelector(
|
||||
`menuitem[accountkey="${gPopAccount.key}"]`
|
||||
),
|
||||
"::before"
|
||||
).backgroundColor
|
||||
),
|
||||
"#ff0000",
|
||||
"The ::before pseudo element of the POP menulist item should use the correct color"
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
rgb2hex(
|
||||
getComputedStyle(
|
||||
identityList.menupopup.querySelector(
|
||||
`menuitem[accountkey="${gLocalAccount.key}"]`
|
||||
),
|
||||
"::before"
|
||||
).backgroundColor
|
||||
),
|
||||
"#0000ff",
|
||||
"The ::before pseudo element of the POP menulist item should use the correct color"
|
||||
);
|
||||
|
||||
// Switch to the pop account.
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
identityList,
|
||||
{},
|
||||
identityList.ownerGlobal
|
||||
);
|
||||
await click_menus_in_sequence(
|
||||
gComposeCtrl.document.getElementById("msgIdentityPopup"),
|
||||
[{ accountkey: gPopAccount.key }]
|
||||
);
|
||||
|
||||
pseudoStyle = getComputedStyle(identityList._labelBox, "::before");
|
||||
Assert.equal(
|
||||
pseudoStyle.display,
|
||||
"block",
|
||||
"The ::before pseudo element of the selected POP account should be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
rgb2hex(pseudoStyle.backgroundColor),
|
||||
"#ff0000",
|
||||
"The ::before pseudo element of the selected POP account should use the correct color"
|
||||
);
|
||||
|
||||
// Switch to the local account.
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
identityList,
|
||||
{},
|
||||
identityList.ownerGlobal
|
||||
);
|
||||
await click_menus_in_sequence(
|
||||
gComposeCtrl.document.getElementById("msgIdentityPopup"),
|
||||
[{ accountkey: gLocalAccount.key }]
|
||||
);
|
||||
|
||||
pseudoStyle = getComputedStyle(identityList._labelBox, "::before");
|
||||
Assert.equal(
|
||||
pseudoStyle.display,
|
||||
"block",
|
||||
"The ::before pseudo element of the selected LOCAL account should be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
rgb2hex(pseudoStyle.backgroundColor),
|
||||
"#0000ff",
|
||||
"The ::before pseudo element of the selected LOCAL account should use the correct color"
|
||||
);
|
||||
|
||||
// Clear colors.
|
||||
popAMUtils.resetServerColor();
|
||||
localAMUtils.resetServerColor();
|
||||
Assert.ok(
|
||||
!identityList.classList.contains("has-custom-color"),
|
||||
"The identity list should not display custom account colors"
|
||||
);
|
||||
|
||||
pseudoStyle = getComputedStyle(identityList._labelBox, "::before");
|
||||
Assert.equal(
|
||||
pseudoStyle.content,
|
||||
"none",
|
||||
"The ::before pseudo element of the identity list should not be visible"
|
||||
);
|
||||
});
|
|
@ -869,11 +869,46 @@ toolbarbutton.formatting-button {
|
|||
height: 14px;
|
||||
}
|
||||
|
||||
#msgIdentityPopup > menuitem[selected="true"] {
|
||||
#msgIdentityPopup > menuitem[selected="true"] {
|
||||
background-color: var(--autocomplete-popup-highlight-background);
|
||||
color: var(--autocomplete-popup-highlight-color);
|
||||
}
|
||||
|
||||
#msgIdentity.has-custom-color {
|
||||
--icon-color: var(--primary);
|
||||
|
||||
&[is="menulist-editable"]::part(label-box)::before {
|
||||
display: block;
|
||||
position: relative;
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
margin-inline-end: 1px;
|
||||
background-color: var(--icon-color);
|
||||
}
|
||||
|
||||
& #msgIdentityPopup > menuitem:not(:last-child) {
|
||||
--icon-color: var(--primary);
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
position: relative;
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
margin-inline: -3px 4px;
|
||||
background-color: var(--icon-color);
|
||||
}
|
||||
|
||||
&[selected="true"]::before {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--autocomplete-popup-highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#msgSubjectContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче