/* 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/. */ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; let lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { FaviconUtils: "moz-src:///toolkit/modules/FaviconUtils.sys.mjs", ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs", }); XPCOMUtils.defineLazyServiceGetters(lazy, { Favicons: ["@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService], }); export const TaskbarTabsUtils = { /** * Checks if Taskbar Tabs has been enabled. * * @returns {bool} `true` if the Taskbar Tabs pref is enabled. */ isEnabled() { const pref = "browser.taskbarTabs.enabled"; return Services.prefs.getBoolPref(pref, false); }, isMSIX() { return ( AppConstants.platform === "win" && Services.sysinfo.getProperty("hasWinPackageId", false) ); }, /** * Returns a folder to store profile-specific Taskbar Tabs files. * * @returns {nsIFile} Folder to store Taskbar Tabs files. */ getTaskbarTabsFolder() { // Construct the path `[Profile]/taskbartabs/`. let folder = Services.dirsvc.get("ProfD", Ci.nsIFile); folder.append("taskbartabs"); return folder; }, /** * Checks if the window is a Taskbar Tabs window. * * @param {Window} aWin - The window to inspect. * @returns {bool} true if the window is a Taskbar Tabs window. */ isTaskbarTabWindow(aWin) { return aWin.document.documentElement.hasAttribute("taskbartab"); }, /** * Retrieves the Taskbar Tabs ID for the window. * * @param {DOMWindow} aWin - The window to retrieve the Taskbar Tabs ID. * @returns {string} The Taskbar Tabs ID for the window. */ getTaskbarTabIdFromWindow(aWin) { return aWin.document.documentElement.getAttribute("taskbartab"); }, /** * Retrieves an image container for the provided URI and decodes it remotely, * e.g. in the content process of aBrowser. * * May throw if an error occurs while decoding. * * @param {nsIFile} aFile - The file to parse the image from. * @param {number} aSize - The width/height of the image to decode. * @param {Browser} aBrowser - The browser to decode the image in. Can be * null if there isn't a specific content process to use. * @param {string} aMimeType - The MIME type to use when decoding the image. * @returns {Promise} An image container with the decoded * image. * @throws {Components.Exception} The image could not be decoded. */ async _remoteDecodeImageFromFile(aFile, aSize, aBrowser, aMimeType) { // We can't use a file URI since the content process wouldn't be able to // read it. Read the file now and create a data URI to read instead. let content = await IOUtils.read(aFile.path); let spec = `data:${aMimeType};base64,${content.toBase64()}`; let uri = Services.io.newURI(spec); return this._remoteDecodeImageFromURI(uri, aSize, aBrowser); }, /** * Retrieves an image container for the provided URI and decodes it remotely, * e.g. in the content process of aBrowser. * * May throw if an error occurs while decoding. * * @param {nsIURI} aUri - The URI to parse the image from. * @param {number} aSize - The width/height of the image to decode. * @param {Browser?} [aBrowser] - The browser to decode the image in. Can be * null if there isn't a specific content process to use. * @returns {Promise} An image container with the decoded * image. * @throws {TypeError} aUri is not an nsIURI. * @throws {Components.Exception} The image could not be decoded. */ async _remoteDecodeImageFromURI(aUri, aSize, aBrowser = null) { if (!(aUri instanceof Ci.nsIURI)) { throw new TypeError( "Invalid argument, `aUri` should be instance of `nsIURI`" ); } let params = { size: aSize }; if (aBrowser) { params.contentParentId = aBrowser.browsingContext.currentWindowContext.contentParentId; } let newUri = Services.io.newURI( lazy.FaviconUtils.getMozRemoteImageURL(aUri.spec, params) ); return unsafeDecodeImageFromAnyURI(newUri); }, /** * Retrieves an image container for the provided URI. The URI must be a local * URI, like chrome: or data:. (Note that being local _doesn't_ mean that it * is trusted---this usually should be used from tests or for images bundled * with the browser.) * * May throw if an error occurs while decoding. * * @param {nsIURI} aUri - The URI to parse the image from. Must be a local * URI, like data:, chrome:, or file:. * @returns {imgIContainer} A container of the icon retrieved, or the * default favicon. * @throws {TypeError} aUri is not an nsIURI. * @throws {Error} aUri is not a local URI. * @throws {Components.Exception} The image could not be decoded. */ async _imageFromLocalURI(aUri) { if (!(aUri instanceof Ci.nsIURI)) { throw new TypeError( "Invalid argument, `aUri` should be instance of `nsIURI`" ); } const protocolFlags = Services.io.getProtocolFlags(aUri.scheme); if (!(protocolFlags & Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE)) { throw new Error("Attempting to create an image from a non-local URI"); } return unsafeDecodeImageFromAnyURI(aUri); }, /** * Gets the favicon for aUri as a data URI. * * @param {nsIURI} aUri - The URI to look up the favicon for. * @returns {nsIURI} The data URI of the favicon. */ async getFaviconUri(aUri) { let favicon = await lazy.Favicons.getFaviconForPage(aUri); return favicon?.dataURI; }, /** * Gets the default favicon as an imgIContainer. This icon is used if no * other icons are available. * * @returns {imgIContainer} The default favicon. */ async getDefaultIcon() { return await TaskbarTabsUtils._imageFromLocalURI( lazy.Favicons.defaultFavicon ); }, /** * Gets the name that should be used for a new desktop entry on Linux. This * avoids duplicating the logic between TaskbarTabsWindowManager, where we * want the name before pinning, and TaskbarTabsPin, where it is actually * pinned. As such, this should be constant within a single session. * * @param {string} aTaskbarTabId - The ID of the taskbar tab. * @returns {string} The desktop entry name, excluding the '.desktop' suffix. */ _determineNewDesktopEntryName(aTaskbarTabId) { return `${lazy.ShellService.getGlibPrgname()}.webapp-${aTaskbarTabId}`; }, }; /** * Shared helper function for _remoteDecodeImageFromURI and _imageFromLocalURI; * fetches the given URI with the system principal and decodes it in the * current process. Do not use with untrusted images. * * @param {nsIURI} aUri - The URI to fetch and decode. * @returns {Promise} The parsed image container. * @throws {Components.Exception} The image could not be decoded. */ async function unsafeDecodeImageFromAnyURI(aUri) { const channel = Services.io.newChannelFromURI( aUri, null, Services.scriptSecurityManager.getSystemPrincipal(), null, Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, Ci.nsIContentPolicy.TYPE_IMAGE ); return ChromeUtils.fetchDecodedImage(aUri, channel); }