TaskbarTabsUtils.sys.mjs (5382B)
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 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 7 8 let lazy = {}; 9 10 XPCOMUtils.defineLazyServiceGetters(lazy, { 11 Favicons: ["@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService], 12 imgTools: ["@mozilla.org/image/tools;1", Ci.imgITools], 13 }); 14 15 ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { 16 return console.createInstance({ 17 prefix: "TaskbarTabs", 18 maxLogLevel: "Warn", 19 }); 20 }); 21 22 export const TaskbarTabsUtils = { 23 /** 24 * Checks if Taskbar Tabs has been enabled. 25 * 26 * @returns {bool} `true` if the Taskbar Tabs pref is enabled. 27 */ 28 isEnabled() { 29 const pref = "browser.taskbarTabs.enabled"; 30 return Services.prefs.getBoolPref(pref, false); 31 }, 32 33 /** 34 * Returns a folder to store profile-specific Taskbar Tabs files. 35 * 36 * @returns {nsIFile} Folder to store Taskbar Tabs files. 37 */ 38 getTaskbarTabsFolder() { 39 // Construct the path `[Profile]/taskbartabs/`. 40 let folder = Services.dirsvc.get("ProfD", Ci.nsIFile); 41 folder.append("taskbartabs"); 42 return folder; 43 }, 44 45 /** 46 * Checks if the window is a Taskbar Tabs window. 47 * 48 * @param {Window} aWin - The window to inspect. 49 * @returns {bool} true if the window is a Taskbar Tabs window. 50 */ 51 isTaskbarTabWindow(aWin) { 52 return aWin.document.documentElement.hasAttribute("taskbartab"); 53 }, 54 55 /** 56 * Retrieves the Taskbar Tabs ID for the window. 57 * 58 * @param {DOMWindow} aWin - The window to retrieve the Taskbar Tabs ID. 59 * @returns {string} The Taskbar Tabs ID for the window. 60 */ 61 getTaskbarTabIdFromWindow(aWin) { 62 return aWin.document.documentElement.getAttribute("taskbartab"); 63 }, 64 65 /** 66 * Retrieves a favicon image container for the provided URL. 67 * 68 * @param {nsIURI} aUri - The URI to retrieve a favicon for. 69 * @returns {imgIContainer} A container of the favicon retrieved, or the 70 * default favicon. 71 */ 72 async getFavicon(aUri) { 73 let favicon = await lazy.Favicons.getFaviconForPage(aUri); 74 75 let imgContainer; 76 if (favicon) { 77 try { 78 if (favicon.mimeType !== "image/svg+xml") { 79 // Image containers retrieved via `getImageFromUri` error when 80 // attempting to scale via `scaleImage`. Since we have the raw image 81 // data from the favicon service and the image container decoded from 82 // it scales without error, use it instead. 83 let img = lazy.imgTools.decodeImageFromArrayBuffer( 84 new Uint8Array(favicon.rawData).buffer, 85 favicon.mimeType 86 ); 87 imgContainer = scaleImage(img); 88 } else { 89 // `imgITools::decodeImage*` APIs don't work with SVG images. 90 imgContainer = getImageFromUri(favicon.dataURI); 91 } 92 } catch (e) { 93 lazy.logConsole.error( 94 `${e.message}, falling through to default favicon.` 95 ); 96 } 97 } 98 99 if (!imgContainer) { 100 lazy.logConsole.debug( 101 `Unable to retrieve icon for ${aUri.spec}, using default favicon at ${lazy.Favicons.defaultFavicon.spec}.` 102 ); 103 imgContainer = await getImageFromUri(lazy.Favicons.defaultFavicon); 104 } 105 106 return imgContainer; 107 }, 108 }; 109 110 /** 111 * Attempts to scale the provided image container to a 256x256 image. 112 * 113 * @param {imgIContainer} aImgContainer - The image container to scale from. 114 * @returns {imgIContainer} The scaled image container on success, or the 115 * provided image container on failure. 116 */ 117 function scaleImage(aImgContainer) { 118 try { 119 const istream = lazy.imgTools.encodeScaledImage( 120 aImgContainer, 121 "image/png", 122 256, 123 256 124 ); 125 const size = istream.available(); 126 127 // Use a binary input stream to grab the bytes. 128 const bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance( 129 Ci.nsIBinaryInputStream 130 ); 131 bis.setInputStream(istream); 132 133 const bytes = bis.readBytes(size); 134 if (size != bytes.length) { 135 throw new Error("Didn't read expected number of bytes"); 136 } 137 aImgContainer = lazy.imgTools.decodeImageFromBuffer( 138 bytes, 139 bytes.length, 140 "image/png" 141 ); 142 } catch (e) { 143 lazy.logConsole.error( 144 "Failed to scale the favicon image, continuing with the unscaled image container." 145 ); 146 } 147 148 return aImgContainer; 149 } 150 151 /** 152 * Retrieves an image given a URI. 153 * 154 * @param {nsIURI} aUri - The URI to retrieve an image from. 155 * @returns {Promise<imgIContainer>} Resolves to an image container. 156 */ 157 async function getImageFromUri(aUri) { 158 // Creating the Taskbar Tabs icon should not result in a network request. 159 const protocolFlags = Services.io.getProtocolFlags(aUri.scheme); 160 if (!(protocolFlags & Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE)) { 161 throw new Error( 162 `Scheme "${aUri.scheme}" is not supported for creating a Taskbar Tab icon, URI should be local` 163 ); 164 } 165 166 const channel = Services.io.newChannelFromURI( 167 aUri, 168 null, 169 Services.scriptSecurityManager.getSystemPrincipal(), 170 null, 171 Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 172 Ci.nsIContentPolicy.TYPE_IMAGE 173 ); 174 175 return ChromeUtils.fetchDecodedImage(aUri, channel); 176 }