diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/components/extensions/parent/ext-identities.js | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mail/components/extensions/parent/ext-identities.js')
-rw-r--r-- | comm/mail/components/extensions/parent/ext-identities.js | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/comm/mail/components/extensions/parent/ext-identities.js b/comm/mail/components/extensions/parent/ext-identities.js new file mode 100644 index 0000000000..1b9e719ebe --- /dev/null +++ b/comm/mail/components/extensions/parent/ext-identities.js @@ -0,0 +1,360 @@ +/* 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/. */ + +ChromeUtils.defineModuleGetter( + this, + "MailServices", + "resource:///modules/MailServices.jsm" +); +ChromeUtils.defineESModuleGetters(this, { + DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", +}); + +function findIdentityAndAccount(identityId) { + for (let account of MailServices.accounts.accounts) { + for (let identity of account.identities) { + if (identity.key == identityId) { + return { account, identity }; + } + } + } + return null; +} + +function checkForProtectedProperties(details) { + const protectedProperties = ["id", "accountId"]; + for (let [key, value] of Object.entries(details)) { + // Check only properties explicitly provided. + if (value != null && protectedProperties.includes(key)) { + throw new ExtensionError( + `Setting the ${key} property of a MailIdentity is not supported.` + ); + } + } +} + +function updateIdentity(identity, details) { + for (let [key, value] of Object.entries(details)) { + // Update only properties explicitly provided. + if (value == null) { + continue; + } + // Map from WebExtension property names to nsIMsgIdentity property names. + switch (key) { + case "signatureIsPlainText": + identity.htmlSigFormat = !value; + break; + case "name": + identity.fullName = value; + break; + case "signature": + identity.htmlSigText = value; + break; + default: + identity[key] = value; + } + } +} + +/** + * @implements {nsIObserver} + */ +var identitiesTracker = new (class extends EventEmitter { + constructor() { + super(); + this.listenerCount = 0; + + this.identities = new Map(); + this.deferredNotifications = new ExtensionUtils.DefaultMap( + key => + new DeferredTask( + () => this.emitPendingNotification(key), + NOTIFICATION_COLLAPSE_TIME + ) + ); + + // Keep track of identities and their values, to suppress superfluous + // update notifications. The deferredTask timer is used to collapse multiple + // update notifications. + for (let account of MailServices.accounts.accounts) { + for (let identity of account.identities) { + this.identities.set( + identity.key, + convertMailIdentity(account, identity) + ); + } + } + } + + incrementListeners() { + this.listenerCount++; + if (this.listenerCount == 1) { + for (let topic of this._notifications) { + Services.obs.addObserver(this, topic); + } + Services.prefs.addObserver("mail.identity.", this); + } + } + decrementListeners() { + this.listenerCount--; + if (this.listenerCount == 0) { + for (let topic of this._notifications) { + Services.obs.removeObserver(this, topic); + } + Services.prefs.removeObserver("mail.identity.", this); + } + } + + emitPendingNotification(key) { + let ia = findIdentityAndAccount(key); + if (!ia) { + return; + } + + let oldValues = this.identities.get(key); + let newValues = convertMailIdentity(ia.account, ia.identity); + let changedValues = {}; + for (let propertyName of Object.keys(newValues)) { + if ( + !oldValues.hasOwnProperty(propertyName) || + oldValues[propertyName] != newValues[propertyName] + ) { + changedValues[propertyName] = newValues[propertyName]; + } + } + if (Object.keys(changedValues).length > 0) { + changedValues.accountId = ia.account.key; + changedValues.id = ia.identity.key; + let notification = + Object.keys(oldValues).length == 0 + ? "account-identity-added" + : "account-identity-updated"; + this.identities.set(key, newValues); + this.emit(notification, key, changedValues); + } + } + + // nsIObserver + _notifications = ["account-identity-added", "account-identity-removed"]; + + async observe(subject, topic, data) { + switch (topic) { + case "account-identity-added": + { + let key = data; + this.identities.set(key, {}); + this.deferredNotifications.get(key).arm(); + } + break; + + case "nsPref:changed": + { + let key = data.split(".").slice(2, 3).pop(); + + // Ignore update notifications for created identities, before they are + // added to an account (looks like they are cloned from a default + // identity). Also ignore notifications for deleted identities. + if ( + key && + this.identities.has(key) && + this.identities.get(key) != null + ) { + this.deferredNotifications.get(key).disarm(); + this.deferredNotifications.get(key).arm(); + } + } + break; + + case "account-identity-removed": + { + let key = data; + if ( + key && + this.identities.has(key) && + this.identities.get(key) != null + ) { + // Mark identities as deleted instead of removing them. + this.identities.set(key, null); + // Force any pending notification to be emitted. + await this.deferredNotifications.get(key).finalize(); + + this.emit("account-identity-removed", key); + } + } + break; + } + } +})(); + +this.identities = class extends ExtensionAPIPersistent { + PERSISTENT_EVENTS = { + // For primed persistent events (deactivated background), the context is only + // available after fire.wakeup() has fulfilled (ensuring the convert() function + // has been called). + + onCreated({ context, fire }) { + async function listener(event, key, identity) { + if (fire.wakeup) { + await fire.wakeup(); + } + fire.sync(key, identity); + } + identitiesTracker.on("account-identity-added", listener); + return { + unregister: () => { + identitiesTracker.off("account-identity-added", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + onUpdated({ context, fire }) { + async function listener(event, key, changedValues) { + if (fire.wakeup) { + await fire.wakeup(); + } + fire.sync(key, changedValues); + } + identitiesTracker.on("account-identity-updated", listener); + return { + unregister: () => { + identitiesTracker.off("account-identity-updated", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + onDeleted({ context, fire }) { + async function listener(event, key) { + if (fire.wakeup) { + await fire.wakeup(); + } + fire.sync(key); + } + identitiesTracker.on("account-identity-removed", listener); + return { + unregister: () => { + identitiesTracker.off("account-identity-removed", listener); + }, + convert(newFire, extContext) { + fire = newFire; + context = extContext; + }, + }; + }, + }; + + constructor(...args) { + super(...args); + identitiesTracker.incrementListeners(); + } + + onShutdown() { + identitiesTracker.decrementListeners(); + } + + getAPI(context) { + return { + identities: { + async list(accountId) { + let accounts = accountId + ? [MailServices.accounts.getAccount(accountId)] + : MailServices.accounts.accounts; + + let identities = []; + for (let account of accounts) { + for (let identity of account.identities) { + identities.push(convertMailIdentity(account, identity)); + } + } + return identities; + }, + async get(identityId) { + let ia = findIdentityAndAccount(identityId); + return ia ? convertMailIdentity(ia.account, ia.identity) : null; + }, + async delete(identityId) { + let ia = findIdentityAndAccount(identityId); + if (!ia) { + throw new ExtensionError(`Identity not found: ${identityId}`); + } + if ( + ia.account?.defaultIdentity && + ia.account.defaultIdentity.key == ia.identity.key + ) { + throw new ExtensionError( + `Identity ${identityId} is the default identity of account ${ia.account.key} and cannot be deleted` + ); + } + ia.account.removeIdentity(ia.identity); + }, + async create(accountId, details) { + let account = MailServices.accounts.getAccount(accountId); + if (!account) { + throw new ExtensionError(`Account not found: ${accountId}`); + } + // Abort and throw, if details include protected properties. + checkForProtectedProperties(details); + + let identity = MailServices.accounts.createIdentity(); + updateIdentity(identity, details); + account.addIdentity(identity); + return convertMailIdentity(account, identity); + }, + async update(identityId, details) { + let ia = findIdentityAndAccount(identityId); + if (!ia) { + throw new ExtensionError(`Identity not found: ${identityId}`); + } + // Abort and throw, if details include protected properties. + checkForProtectedProperties(details); + + updateIdentity(ia.identity, details); + return convertMailIdentity(ia.account, ia.identity); + }, + async getDefault(accountId) { + let account = MailServices.accounts.getAccount(accountId); + return convertMailIdentity(account, account?.defaultIdentity); + }, + async setDefault(accountId, identityId) { + let account = MailServices.accounts.getAccount(accountId); + if (!account) { + throw new ExtensionError(`Account not found: ${accountId}`); + } + for (let identity of account.identities) { + if (identity.key == identityId) { + account.defaultIdentity = identity; + return; + } + } + throw new ExtensionError( + `Identity ${identityId} not found for ${accountId}` + ); + }, + onCreated: new EventManager({ + context, + module: "identities", + event: "onCreated", + extensionApi: this, + }).api(), + onUpdated: new EventManager({ + context, + module: "identities", + event: "onUpdated", + extensionApi: this, + }).api(), + onDeleted: new EventManager({ + context, + module: "identities", + event: "onDeleted", + extensionApi: this, + }).api(), + }, + }; + } +}; |