/* 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<imgIContainer>} 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<imgIContainer>} 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<imgIContainer>} 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);
}