nabled.
   *
   * @returns {bool} `true` if the Taskbar Tabs pref is enabled.
   */
  isEnabled() {
    const pref = "browser.taskbarTabs.enabled";
    return Services.prefs.getBoolPref(pref, 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 a favicon image container for the provided URL.
   *
   * @param {nsIURI} aUri - The URI to retrieve a favicon for.
   * @returns {imgIContainer} A container of the favicon retrieved, or the
   * default favicon.
   */
  async getFavicon(aUri) {
    let favicon = await lazy.Favicons.getFaviconForPage(aUri);

    let imgContainer;
    if (favicon) {
      try {
        if (favicon.mimeType !== "image/svg+xml") {
          // Image containers retrieved via `getImageFromUri` error when
          // attempting to scale via `scaleImage`. Since we have the raw image
          // data from the favicon service and the image container decoded from
          // it scales without error, use it instead.
          let img = lazy.imgTools.decodeImageFromArrayBuffer(
            new Uint8Array(favicon.rawData).buffer,
            favicon.mimeType
          );
          imgContainer = scaleImage(img);
        } else {
          // `imgITools::decodeImage*` APIs don't work with SVG images.
          imgContainer = getImageFromUri(favicon.dataURI);
        }
      } catch (e) {
        lazy.logConsole.error(
          `${e.message}, falling through to default favicon.`
        );
      }
    }

    if (!imgContainer) {
      lazy.logConsole.debug(
        `Unable to retrieve icon for ${aUri.spec}, using default favicon at ${lazy.Favicons.defaultFavicon.spec}.`
      );
      imgContainer = await getImageFromUri(lazy.Favicons.defaultFavicon);
    }

    return imgContainer;
  },
};

/**
 * Attempts to scale the provided image container to a 256x256 image.
 *
 * @param {imgIContainer} aImgContainer - The image container to scale from.
 * @returns {imgIContainer} The scaled image container on success, or the
 * provided image container on failure.
 */
function scaleImage(aImgContainer) {
  try {
    const istream = lazy.imgTools.encodeScaledImage(
      aImgContainer,
      "image/png",
      256,
      256
    );
    const size = istream.available();

    // Use a binary input stream to grab the bytes.
    const bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
      Ci.nsIBinaryInputStream
    );
    bis.setInputStream(istream);

    const bytes = bis.readBytes(size);
    if (size != bytes.length) {
      throw new Error("Didn't read expected number of bytes");
    }
    aImgContainer = lazy.imgTools.decodeImageFromBuffer(
      bytes,
      bytes.length,
      "image/png"
    );
  } catch (e) {
    lazy.logConsole.error(
      "Failed to scale the favicon image, continuing with the unscaled image container."
    );
  }

  return aImgContainer;
}

/**
 * Retrieves an image given a URI.
 *
 * @param {nsIURI} aUri - The URI to retrieve an image from.
 * @returns {Promise<imgIContainer>} Resolves to an image container.
 */
async function getImageFromUri(aUri) {
  // Creating the Taskbar Tabs icon should not result in a network request.
  const protocolFlags = Services.io.getProtocolFlags(aUri.scheme);
  if (!(protocolFlags & Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE)) {
    throw new Error(
      `Scheme "${aUri.scheme}" is not supported for creating a Taskbar Tab icon, URI should be local`
    );
  }

  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);
}
PK