298 lines
9.4 KiB
JavaScript
298 lines
9.4 KiB
JavaScript
/* 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";
|
|
|
|
let lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
|
|
TaskbarTabsUtils: "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "logConsole", () => {
|
|
return console.createInstance({
|
|
prefix: "TaskbarTabs",
|
|
maxLogLevel: "Warn",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Provides functionality to pin and unping Taskbar Tabs.
|
|
*/
|
|
export const TaskbarTabsPin = {
|
|
/**
|
|
* Pins the provided Taskbar Tab to the taskbar.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - A Taskbar Tab to pin to the taskbar.
|
|
* @param {imgIContainer} aIcon - The icon to show with this Taskbar Tab.
|
|
* @param {object} [aDetails] - Additional options for this pin.
|
|
* @param {DOMWindow} [aDetails.window] - The window to associate any UI with.
|
|
* @returns {Promise} Resolves once finished.
|
|
*/
|
|
async pinTaskbarTab(aTaskbarTab, aIcon, { window = null } = {}) {
|
|
lazy.logConsole.info("Pinning Taskbar Tab to the taskbar.");
|
|
|
|
let shortcut = null;
|
|
try {
|
|
let iconPath = await createTaskbarIcon(aTaskbarTab, aIcon);
|
|
|
|
shortcut = await createShortcut(aTaskbarTab, iconPath, window);
|
|
|
|
if (AppConstants.platform === "win" && !lazy.TaskbarTabsUtils.isMSIX()) {
|
|
await lazy.ShellService.pinShortcutToTaskbar(
|
|
aTaskbarTab.id,
|
|
"Programs",
|
|
shortcut
|
|
);
|
|
}
|
|
Glean.webApp.pin.record({ result: "Success" });
|
|
} catch (e) {
|
|
lazy.logConsole.error(`An error occurred while pinning: ${e.message}`);
|
|
Glean.webApp.pin.record({ result: e.name ?? "Unknown exception" });
|
|
}
|
|
|
|
return shortcut;
|
|
},
|
|
|
|
/**
|
|
* Unpins the provided Taskbar Tab from the taskbar.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to unpin from the taskbar.
|
|
* @returns {Promise} Resolves once finished.
|
|
*/
|
|
async unpinTaskbarTab(aTaskbarTab) {
|
|
let unpinError = null;
|
|
let removalError = null;
|
|
|
|
let isMSIX = lazy.TaskbarTabsUtils.isMSIX();
|
|
try {
|
|
lazy.logConsole.info("Unpinning Taskbar Tab from the taskbar.");
|
|
|
|
let relativePath = aTaskbarTab.shortcutRelativePath;
|
|
|
|
try {
|
|
if (AppConstants.platform === "win" && !isMSIX) {
|
|
lazy.ShellService.unpinShortcutFromTaskbar("Programs", relativePath);
|
|
}
|
|
} catch (e) {
|
|
lazy.logConsole.error(`Failed to unpin shortcut: ${e.message}`);
|
|
unpinError = e;
|
|
}
|
|
|
|
let iconFile = getIconFile(aTaskbarTab);
|
|
|
|
lazy.logConsole.debug(`Deleting ${relativePath}`);
|
|
lazy.logConsole.debug(`Deleting ${iconFile.path}`);
|
|
|
|
let deleteShortcut = Promise.resolve();
|
|
if (relativePath && AppConstants.platform === "win" && !isMSIX) {
|
|
deleteShortcut = lazy.ShellService.deleteShortcut(
|
|
"Programs",
|
|
relativePath
|
|
);
|
|
} else if (relativePath && AppConstants.platform === "win" && isMSIX) {
|
|
deleteShortcut =
|
|
lazy.ShellService.requestDeleteSecondaryTile(relativePath);
|
|
} else if (relativePath && AppConstants.platform === "linux") {
|
|
deleteShortcut = lazy.ShellService.deleteLinuxDesktopEntry(
|
|
relativePath.replace(/\.desktop$/, "")
|
|
);
|
|
}
|
|
|
|
await Promise.all([deleteShortcut, IOUtils.remove(iconFile.path)]);
|
|
} catch (e) {
|
|
lazy.logConsole.error(
|
|
`An error occurred while uninstalling: ${e.message}`
|
|
);
|
|
removalError = e;
|
|
}
|
|
|
|
const message = e => (e ? (e.name ?? "Unknown exception") : "Success");
|
|
Glean.webApp.unpin.record({
|
|
result: message(unpinError),
|
|
removal_result: message(removalError),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Gets a Localization object to use when creating shortcuts.
|
|
*
|
|
* @returns {Localization} An object to access localized strings.
|
|
*/
|
|
_getLocalization() {
|
|
return new Localization(["branding/brand.ftl", "browser/taskbartabs.ftl"]);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Fetches the favicon from the provided Taskbar Tab's start url, and saves it
|
|
* to an icon file.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to generate an icon file for.
|
|
* @param {imgIContainer} aIcon - The icon to associate with this Taskbar Tab.
|
|
* @returns {Promise<nsIFile>} The created icon file.
|
|
*/
|
|
async function createTaskbarIcon(aTaskbarTab, aIcon) {
|
|
lazy.logConsole.info("Creating Taskbar Tabs shortcut icon.");
|
|
|
|
let iconFile = getIconFile(aTaskbarTab);
|
|
|
|
lazy.logConsole.debug(`Using icon path: ${iconFile.path}`);
|
|
|
|
await IOUtils.makeDirectory(iconFile.parent.path);
|
|
|
|
await lazy.ShellService.writeShortcutIcon(iconFile, aIcon);
|
|
|
|
return iconFile;
|
|
}
|
|
|
|
/**
|
|
* Creates a shortcut that opens Firefox with relevant Taskbar Tabs flags.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to generate a shortcut for.
|
|
* @param {nsIFile} aFileIcon - The icon file to use for the shortcut.
|
|
* @param {TaskbarTabsRegistry} aRegistry - The registry used to save the shortcut path.
|
|
* @param {DOMWindow?} aWindow - The window to associate any UI with, or null
|
|
* if no window is available.
|
|
* @returns {Promise<string>} The path to the created shortcut.
|
|
*/
|
|
async function createShortcut(aTaskbarTab, aFileIcon, aRegistry, aWindow) {
|
|
lazy.logConsole.info("Creating Taskbar Tabs shortcut.");
|
|
|
|
let targetfile = Services.dirsvc.get("XREExeF", Ci.nsIFile);
|
|
let profileFolder = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
|
|
|
let args = [
|
|
"-taskbar-tab",
|
|
aTaskbarTab.id,
|
|
"-new-window",
|
|
aTaskbarTab.startUrl, // In case Taskbar Tabs is disabled, provide fallback url.
|
|
"-profile",
|
|
profileFolder.path,
|
|
"-container",
|
|
aTaskbarTab.userContextId.toString(),
|
|
];
|
|
|
|
if (AppConstants.platform === "win" && lazy.TaskbarTabsUtils.isMSIX()) {
|
|
let secondaryTileId = "taskbartab-" + aTaskbarTab.id;
|
|
await lazy.ShellService.requestCreateAndPinSecondaryTile(
|
|
secondaryTileId,
|
|
aTaskbarTab.name,
|
|
aFileIcon.path,
|
|
args
|
|
);
|
|
|
|
return secondaryTileId;
|
|
}
|
|
|
|
if (AppConstants.platform === "win") {
|
|
// non-MSIX
|
|
let { relativePath, description } = await generateShortcutInfo(aTaskbarTab);
|
|
lazy.logConsole.debug(
|
|
`Using shortcut path relative to Programs folder: ${relativePath}`
|
|
);
|
|
|
|
await lazy.ShellService.createShortcut(
|
|
targetfile,
|
|
args,
|
|
description,
|
|
aFileIcon,
|
|
0,
|
|
aTaskbarTab.id, // AUMID
|
|
"Programs",
|
|
relativePath
|
|
);
|
|
|
|
return relativePath;
|
|
}
|
|
|
|
if (AppConstants.platform === "linux") {
|
|
let appId = lazy.TaskbarTabsUtils._determineNewDesktopEntryName(
|
|
aTaskbarTab.id
|
|
);
|
|
await lazy.ShellService.createLinuxDesktopEntry(
|
|
appId,
|
|
aTaskbarTab.name,
|
|
args,
|
|
aFileIcon.path,
|
|
{ window: aWindow }
|
|
);
|
|
|
|
let relativePath = appId + ".desktop";
|
|
return relativePath;
|
|
}
|
|
|
|
throw new Error("Don't know how to create a shortcut on this platform");
|
|
}
|
|
|
|
/**
|
|
* Gets the path to the shortcut relative to the Start Menu folder,
|
|
* as well as the description that should be attached to the shortcut.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to get the path of.
|
|
* @returns {Promise<{description: string, relativePath: string}>} The description
|
|
* and relative path of the shortcut.
|
|
*/
|
|
async function generateShortcutInfo(aTaskbarTab) {
|
|
const l10n = TaskbarTabsPin._getLocalization();
|
|
|
|
let basename = sanitizeFilename(aTaskbarTab.name + ".lnk");
|
|
let dirname = await l10n.formatValue("taskbar-tab-shortcut-folder");
|
|
dirname = sanitizeFilename(dirname, { allowDirectoryNames: true });
|
|
|
|
const description = await l10n.formatValue(
|
|
"taskbar-tab-shortcut-description",
|
|
{ name: aTaskbarTab.name }
|
|
);
|
|
|
|
return {
|
|
description,
|
|
relativePath: dirname + "\\" + basename,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Cleans up the filename so it can be saved safely. This means replacing invalid names
|
|
* (e.g. DOS devices) with others, or replacing invalid characters (e.g. asterisks on
|
|
* Windows) with underscores.
|
|
*
|
|
* @param {string} aWantedName - The name to validate and sanitize. Make sure
|
|
* that aWantedName has an extension if it will be saved with one.
|
|
* @param {object} aOptions - Options to affect the sanitization.
|
|
* @param {boolean} aOptions.allowDirectoryNames - Indicates that the name will be used
|
|
* as a directory. If so, the validation rules may be slightly more lax.
|
|
* @returns {string} The sanitized name.
|
|
*/
|
|
function sanitizeFilename(aWantedName, { allowDirectoryNames = false } = {}) {
|
|
const mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
|
|
|
let flags =
|
|
Ci.nsIMIMEService.VALIDATE_SANITIZE_ONLY |
|
|
Ci.nsIMIMEService.VALIDATE_DONT_COLLAPSE_WHITESPACE |
|
|
// Don't add .download to the name if it ends with .lnk.
|
|
Ci.nsIMIMEService.VALIDATE_ALLOW_INVALID_FILENAMES;
|
|
|
|
if (allowDirectoryNames) {
|
|
flags |= Ci.nsIMIMEService.VALIDATE_ALLOW_DIRECTORY_NAMES;
|
|
}
|
|
|
|
return mimeService.validateFileNameForSaving(aWantedName, "", flags);
|
|
}
|
|
|
|
/**
|
|
* Generates a file path to use for the Taskbar Tab icon file.
|
|
*
|
|
* @param {TaskbarTab} aTaskbarTab - A Taskbar Tab to generate an icon file path for.
|
|
* @returns {nsIFile} The icon file path for the Taskbar Tab.
|
|
*/
|
|
function getIconFile(aTaskbarTab) {
|
|
let iconPath = lazy.TaskbarTabsUtils.getTaskbarTabsFolder();
|
|
iconPath.append("icons");
|
|
iconPath.append(
|
|
aTaskbarTab.id + "." + lazy.ShellService.shortcutIconType.extension
|
|
);
|
|
return iconPath;
|
|
}
|