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-spaces.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.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 '')
-rw-r--r-- | comm/mail/components/extensions/parent/ext-spaces.js | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/comm/mail/components/extensions/parent/ext-spaces.js b/comm/mail/components/extensions/parent/ext-spaces.js new file mode 100644 index 0000000000..3f2ade0404 --- /dev/null +++ b/comm/mail/components/extensions/parent/ext-spaces.js @@ -0,0 +1,364 @@ +/* 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 { ExtensionSupport } = ChromeUtils.import( + "resource:///modules/ExtensionSupport.jsm" +); +var { ExtensionCommon } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionCommon.sys.mjs" +); +var { ExtensionParent } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionParent.sys.mjs" +); +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineModuleGetter( + this, + "getIconData", + "resource:///modules/ExtensionToolbarButtons.jsm" +); + +XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]); + +var windowURLs = ["chrome://messenger/content/messenger.xhtml"]; + +/** + * Return the paths to the 16px and 32px icons defined in the manifest of this + * extension, if any. + * + * @param {ExtensionData} extension - the extension to retrieve the path object for + */ +function getManifestIcons(extension) { + if (extension.manifest.icons) { + let { icon: icon16 } = ExtensionParent.IconDetails.getPreferredIcon( + extension.manifest.icons, + extension, + 16 + ); + let { icon: icon32 } = ExtensionParent.IconDetails.getPreferredIcon( + extension.manifest.icons, + extension, + 32 + ); + return { + 16: extension.baseURI.resolve(icon16), + 32: extension.baseURI.resolve(icon32), + }; + } + return null; +} + +/** + * Convert WebExtension SpaceButtonProperties into a NativeButtonProperties + * object required by the gSpacesToolbar.* functions. + * + * @param {SpaceData} spaceData - @see mail/components/extensions/parent/ext-mail.js + * @returns {NativeButtonProperties} - @see mail/base/content/spacesToolbar.js + */ +function getNativeButtonProperties({ + extension, + defaultUrl, + buttonProperties, +}) { + const normalizeColor = color => { + if (typeof color == "string") { + let col = InspectorUtils.colorToRGBA(color); + if (!col) { + throw new ExtensionError(`Invalid color value: "${color}"`); + } + return [col.r, col.g, col.b, Math.round(col.a * 255)]; + } + return color; + }; + + let hasThemeIcons = + buttonProperties.themeIcons && buttonProperties.themeIcons.length > 0; + + // If themeIcons have been defined, ignore manifestIcons as fallback and use + // themeIcons for the default theme as well, following the behavior of + // WebExtension action buttons. + let fallbackManifestIcons = hasThemeIcons + ? null + : getManifestIcons(extension); + + // Use _normalize() to bypass cache. + let icons = ExtensionParent.IconDetails._normalize( + { + path: buttonProperties.defaultIcons || fallbackManifestIcons, + themeIcons: hasThemeIcons ? buttonProperties.themeIcons : null, + }, + extension + ); + let iconStyles = new Map(getIconData(icons, extension).style); + + let badgeStyles = new Map(); + let bgColor = normalizeColor(buttonProperties.badgeBackgroundColor); + if (bgColor) { + badgeStyles.set( + "--spaces-button-badge-bg-color", + `rgba(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]}, ${bgColor[3] / 255})` + ); + } + + return { + title: buttonProperties.title || extension.name, + url: defaultUrl, + badgeText: buttonProperties.badgeText, + badgeStyles, + iconStyles, + }; +} + +ExtensionSupport.registerWindowListener("ext-spaces", { + chromeURLs: windowURLs, + onLoadWindow: async window => { + await new Promise(resolve => { + if (window.gSpacesToolbar.isLoaded) { + resolve(); + } else { + window.addEventListener("spaces-toolbar-ready", resolve, { + once: true, + }); + } + }); + // Add buttons of all extension spaces to the toolbar of each newly opened + // normal window. + for (let spaceData of spaceTracker.getAll()) { + if (!spaceData.extension) { + continue; + } + let nativeButtonProperties = getNativeButtonProperties(spaceData); + await window.gSpacesToolbar.createToolbarButton( + spaceData.spaceButtonId, + nativeButtonProperties + ); + } + }, +}); + +this.spaces = class extends ExtensionAPI { + /** + * Match a WebExtension Space object against the provided queryInfo. + * + * @param {Space} space - @see mail/components/extensions/schemas/spaces.json + * @param {QueryInfo} queryInfo - @see mail/components/extensions/schemas/spaces.json + * @returns {boolean} + */ + matchSpace(space, queryInfo) { + if (queryInfo.id != null && space.id != queryInfo.id) { + return false; + } + if (queryInfo.name != null && space.name != queryInfo.name) { + return false; + } + if (queryInfo.isBuiltIn != null && space.isBuiltIn != queryInfo.isBuiltIn) { + return false; + } + if ( + queryInfo.isSelfOwned != null && + space.isSelfOwned != queryInfo.isSelfOwned + ) { + return false; + } + if ( + queryInfo.extensionId != null && + space.extensionId != queryInfo.extensionId + ) { + return false; + } + return true; + } + + async onShutdown(isAppShutdown) { + if (isAppShutdown) { + return; + } + + let extensionId = this.extension.id; + for (let spaceData of spaceTracker.getAll()) { + if (spaceData.extension?.id != extensionId) { + continue; + } + for (let window of ExtensionSupport.openWindows) { + if (windowURLs.includes(window.location.href)) { + await window.gSpacesToolbar.removeToolbarButton( + spaceData.spaceButtonId + ); + } + } + spaceTracker.remove(spaceData); + } + } + + getAPI(context) { + let { tabManager } = context.extension; + let self = this; + + return { + spaces: { + async create(name, defaultUrl, buttonProperties) { + if (spaceTracker.fromSpaceName(name, context.extension)) { + throw new ExtensionError( + `Failed to create space with name ${name}: Space already exists for this extension.` + ); + } + + defaultUrl = context.uri.resolve(defaultUrl); + if (!/((^https:)|(^http:)|(^moz-extension:))/i.test(defaultUrl)) { + throw new ExtensionError( + `Failed to create space with name ${name}: Invalid default url.` + ); + } + + try { + let spaceData = await spaceTracker.create( + name, + defaultUrl, + buttonProperties, + context.extension + ); + + let nativeButtonProperties = getNativeButtonProperties(spaceData); + for (let window of ExtensionSupport.openWindows) { + if (windowURLs.includes(window.location.href)) { + await window.gSpacesToolbar.createToolbarButton( + spaceData.spaceButtonId, + nativeButtonProperties + ); + } + } + + return spaceTracker.convert(spaceData, context.extension); + } catch (error) { + throw new ExtensionError( + `Failed to create space with name ${name}: ${error}` + ); + } + }, + async remove(spaceId) { + let spaceData = spaceTracker.fromSpaceId(spaceId); + if (!spaceData) { + throw new ExtensionError( + `Failed to remove space with id ${spaceId}: Unknown id.` + ); + } + if (spaceData.extension?.id != context.extension.id) { + throw new ExtensionError( + `Failed to remove space with id ${spaceId}: Space does not belong to this extension.` + ); + } + + try { + for (let window of ExtensionSupport.openWindows) { + if (windowURLs.includes(window.location.href)) { + await window.gSpacesToolbar.removeToolbarButton( + spaceData.spaceButtonId + ); + } + } + spaceTracker.remove(spaceData); + } catch (ex) { + throw new ExtensionError( + `Failed to remove space with id ${spaceId}: ${ex.message}` + ); + } + }, + async update(spaceId, updatedDefaultUrl, updatedButtonProperties) { + let spaceData = spaceTracker.fromSpaceId(spaceId); + if (!spaceData) { + throw new ExtensionError( + `Failed to update space with id ${spaceId}: Unknown id.` + ); + } + if (spaceData.extension?.id != context.extension.id) { + throw new ExtensionError( + `Failed to update space with id ${spaceId}: Space does not belong to this extension.` + ); + } + + let changes = false; + if (updatedDefaultUrl) { + updatedDefaultUrl = context.uri.resolve(updatedDefaultUrl); + if ( + !/((^https:)|(^http:)|(^moz-extension:))/i.test(updatedDefaultUrl) + ) { + throw new ExtensionError( + `Failed to update space with id ${spaceId}: Invalid default url.` + ); + } + spaceData.defaultUrl = updatedDefaultUrl; + changes = true; + } + + if (updatedButtonProperties) { + for (let [key, value] of Object.entries(updatedButtonProperties)) { + if (value != null) { + spaceData.buttonProperties[key] = value; + changes = true; + } + } + } + + if (changes) { + let nativeButtonProperties = getNativeButtonProperties(spaceData); + try { + for (let window of ExtensionSupport.openWindows) { + if (windowURLs.includes(window.location.href)) { + await window.gSpacesToolbar.updateToolbarButton( + spaceData.spaceButtonId, + nativeButtonProperties + ); + } + } + spaceTracker.update(spaceData); + } catch (error) { + throw new ExtensionError( + `Failed to update space with id ${spaceId}: ${error}` + ); + } + } + }, + async open(spaceId, windowId) { + let spaceData = spaceTracker.fromSpaceId(spaceId); + if (!spaceData) { + throw new ExtensionError( + `Failed to open space with id ${spaceId}: Unknown id.` + ); + } + + let window = await getNormalWindowReady(context, windowId); + let space = window.gSpacesToolbar.spaces.find( + space => space.button.id == spaceData.spaceButtonId + ); + + let tabmail = window.document.getElementById("tabmail"); + let currentTab = tabmail.selectedTab; + let nativeTabInfo = window.gSpacesToolbar.openSpace(tabmail, space); + return tabManager.convert(nativeTabInfo, currentTab); + }, + async get(spaceId) { + let spaceData = spaceTracker.fromSpaceId(spaceId); + if (!spaceData) { + throw new ExtensionError( + `Failed to get space with id ${spaceId}: Unknown id.` + ); + } + return spaceTracker.convert(spaceData, context.extension); + }, + async query(queryInfo) { + let allSpaceData = [...spaceTracker.getAll()]; + return allSpaceData + .map(spaceData => + spaceTracker.convert(spaceData, context.extension) + ) + .filter(space => self.matchSpace(space, queryInfo)); + }, + }, + }; + } +}; |