tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TaskbarTabsPin.sys.mjs (8130B)


      1 /* vim: se cin sw=2 ts=2 et filetype=javascript :
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 let lazy = {};
      7 
      8 ChromeUtils.defineESModuleGetters(lazy, {
      9  ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
     10  TaskbarTabsUtils: "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs",
     11 });
     12 
     13 ChromeUtils.defineLazyGetter(lazy, "logConsole", () => {
     14  return console.createInstance({
     15    prefix: "TaskbarTabs",
     16    maxLogLevel: "Warn",
     17  });
     18 });
     19 
     20 /**
     21 * Provides functionality to pin and unping Taskbar Tabs.
     22 */
     23 export const TaskbarTabsPin = {
     24  /**
     25   * Pins the provided Taskbar Tab to the taskbar.
     26   *
     27   * @param {TaskbarTab} aTaskbarTab - A Taskbar Tab to pin to the taskbar.
     28   * @param {TaskbarTabsRegistry} aRegistry - The registry to track pin resources with.
     29   * @returns {Promise} Resolves once finished.
     30   */
     31  async pinTaskbarTab(aTaskbarTab, aRegistry) {
     32    lazy.logConsole.info("Pinning Taskbar Tab to the taskbar.");
     33 
     34    try {
     35      let iconPath = await createTaskbarIconFromFavicon(aTaskbarTab);
     36 
     37      let shortcut = await createShortcut(aTaskbarTab, iconPath, aRegistry);
     38 
     39      await lazy.ShellService.pinShortcutToTaskbar(
     40        aTaskbarTab.id,
     41        "Programs",
     42        shortcut
     43      );
     44      Glean.webApp.pin.record({ result: "Success" });
     45    } catch (e) {
     46      lazy.logConsole.error(`An error occurred while pinning: ${e.message}`);
     47      Glean.webApp.pin.record({ result: e.name ?? "Unknown exception" });
     48    }
     49  },
     50 
     51  /**
     52   * Unpins the provided Taskbar Tab from the taskbar.
     53   *
     54   * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to unpin from the taskbar.
     55   * @param {TaskbarTabsRegistry} aRegistry - remove pinned resource tracking from.
     56   * @returns {Promise} Resolves once finished.
     57   */
     58  async unpinTaskbarTab(aTaskbarTab, aRegistry) {
     59    let unpinError = null;
     60    let removalError = null;
     61 
     62    try {
     63      lazy.logConsole.info("Unpinning Taskbar Tab from the taskbar.");
     64 
     65      let { relativePath } = await generateShortcutInfo(aTaskbarTab);
     66 
     67      try {
     68        lazy.ShellService.unpinShortcutFromTaskbar("Programs", relativePath);
     69      } catch (e) {
     70        lazy.logConsole.error(`Failed to unpin shortcut: ${e.message}`);
     71        unpinError = e;
     72      }
     73 
     74      let iconFile = getIconFile(aTaskbarTab);
     75 
     76      lazy.logConsole.debug(`Deleting ${relativePath}`);
     77      lazy.logConsole.debug(`Deleting ${iconFile.path}`);
     78 
     79      await Promise.all([
     80        lazy.ShellService.deleteShortcut("Programs", relativePath).then(() => {
     81          // Only update if that didn't throw an error.
     82          aRegistry.patchTaskbarTab(aTaskbarTab, {
     83            shortcutRelativePath: null,
     84          });
     85        }),
     86        IOUtils.remove(iconFile.path),
     87      ]);
     88    } catch (e) {
     89      lazy.logConsole.error(
     90        `An error occurred while uninstalling: ${e.message}`
     91      );
     92      removalError = e;
     93    }
     94 
     95    const message = e => (e ? (e.name ?? "Unknown exception") : "Success");
     96    Glean.webApp.unpin.record({
     97      result: message(unpinError),
     98      removal_result: message(removalError),
     99    });
    100  },
    101 
    102  /**
    103   * Gets a Localization object to use when creating shortcuts.
    104   *
    105   * @returns {Localization} An object to access localized strings.
    106   */
    107  _getLocalization() {
    108    return new Localization(["branding/brand.ftl", "browser/taskbartabs.ftl"]);
    109  },
    110 };
    111 
    112 /**
    113 * Fetches the favicon from the provided Taskbar Tab's start url, and saves it
    114 * to an icon file.
    115 *
    116 * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to generate an icon file for.
    117 * @returns {Promise<nsIFile>} The created icon file.
    118 */
    119 async function createTaskbarIconFromFavicon(aTaskbarTab) {
    120  lazy.logConsole.info("Creating Taskbar Tabs shortcut icon.");
    121 
    122  let url = Services.io.newURI(aTaskbarTab.startUrl);
    123  let imgContainer = await lazy.TaskbarTabsUtils.getFavicon(url);
    124 
    125  let iconFile = getIconFile(aTaskbarTab);
    126 
    127  lazy.logConsole.debug(`Using icon path: ${iconFile.path}`);
    128 
    129  await IOUtils.makeDirectory(iconFile.parent.path);
    130 
    131  await lazy.ShellService.createWindowsIcon(iconFile, imgContainer);
    132 
    133  return iconFile;
    134 }
    135 
    136 /**
    137 * Creates a shortcut that opens Firefox with relevant Taskbar Tabs flags.
    138 *
    139 * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to generate a shortcut for.
    140 * @param {nsIFile} aFileIcon - The icon file to use for the shortcut.
    141 * @param {TaskbarTabsRegistry} aRegistry - The registry used to save the shortcut path.
    142 * @returns {Promise<string>} The path to the created shortcut.
    143 */
    144 async function createShortcut(aTaskbarTab, aFileIcon, aRegistry) {
    145  lazy.logConsole.info("Creating Taskbar Tabs shortcut.");
    146 
    147  let { relativePath, description } = await generateShortcutInfo(aTaskbarTab);
    148  lazy.logConsole.debug(
    149    `Using shortcut path relative to Programs folder: ${relativePath}`
    150  );
    151 
    152  let targetfile = Services.dirsvc.get("XREExeF", Ci.nsIFile);
    153  let profileFolder = Services.dirsvc.get("ProfD", Ci.nsIFile);
    154 
    155  await lazy.ShellService.createShortcut(
    156    targetfile,
    157    [
    158      "-taskbar-tab",
    159      aTaskbarTab.id,
    160      "-new-window",
    161      aTaskbarTab.startUrl, // In case Taskbar Tabs is disabled, provide fallback url.
    162      "-profile",
    163      profileFolder.path,
    164      "-container",
    165      aTaskbarTab.userContextId,
    166    ],
    167    description,
    168    aFileIcon,
    169    0,
    170    aTaskbarTab.id, // AUMID
    171    "Programs",
    172    relativePath
    173  );
    174 
    175  // Only update if that didn't throw an error.
    176  aRegistry.patchTaskbarTab(aTaskbarTab, {
    177    shortcutRelativePath: relativePath,
    178  });
    179 
    180  return relativePath;
    181 }
    182 
    183 /**
    184 * Gets the path to the shortcut relative to the Start Menu folder,
    185 * as well as the description that should be attached to the shortcut.
    186 *
    187 * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to get the path of.
    188 * @returns {Promise<{description: string, relativePath: string}>} The description
    189 * and relative path of the shortcut.
    190 */
    191 async function generateShortcutInfo(aTaskbarTab) {
    192  const l10n = TaskbarTabsPin._getLocalization();
    193 
    194  let basename = sanitizeFilename(aTaskbarTab.name + ".lnk");
    195  let dirname = await l10n.formatValue("taskbar-tab-shortcut-folder");
    196  dirname = sanitizeFilename(dirname, { allowDirectoryNames: true });
    197 
    198  const description = await l10n.formatValue(
    199    "taskbar-tab-shortcut-description",
    200    { name: aTaskbarTab.name }
    201  );
    202 
    203  return {
    204    description,
    205    relativePath: dirname + "\\" + basename,
    206  };
    207 }
    208 
    209 /**
    210 * Cleans up the filename so it can be saved safely. This means replacing invalid names
    211 * (e.g. DOS devices) with others, or replacing invalid characters (e.g. asterisks on
    212 * Windows) with underscores.
    213 *
    214 * @param {string} aWantedName - The name to validate and sanitize. Make sure
    215 * that aWantedName has an extension if it will be saved with one.
    216 * @param {object} aOptions - Options to affect the sanitization.
    217 * @param {boolean} aOptions.allowDirectoryNames - Indicates that the name will be used
    218 * as a directory. If so, the validation rules may be slightly more lax.
    219 * @returns {string} The sanitized name.
    220 */
    221 function sanitizeFilename(aWantedName, { allowDirectoryNames = false } = {}) {
    222  const mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
    223 
    224  let flags =
    225    Ci.nsIMIMEService.VALIDATE_SANITIZE_ONLY |
    226    Ci.nsIMIMEService.VALIDATE_DONT_COLLAPSE_WHITESPACE |
    227    // Don't add .download to the name if it ends with .lnk.
    228    Ci.nsIMIMEService.VALIDATE_ALLOW_INVALID_FILENAMES;
    229 
    230  if (allowDirectoryNames) {
    231    flags |= Ci.nsIMIMEService.VALIDATE_ALLOW_DIRECTORY_NAMES;
    232  }
    233 
    234  return mimeService.validateFileNameForSaving(aWantedName, "", flags);
    235 }
    236 
    237 /**
    238 * Generates a file path to use for the Taskbar Tab icon file.
    239 *
    240 * @param {TaskbarTab} aTaskbarTab - A Taskbar Tab to generate an icon file path for.
    241 * @returns {nsIFile} The icon file path for the Taskbar Tab.
    242 */
    243 function getIconFile(aTaskbarTab) {
    244  let iconPath = lazy.TaskbarTabsUtils.getTaskbarTabsFolder();
    245  iconPath.append("icons");
    246  iconPath.append(aTaskbarTab.id + ".ico");
    247  return iconPath;
    248 }