ManifestIcons.sys.mjs (2729B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 export var ManifestIcons = { 6 async browserFetchIcon(aBrowser, manifest, iconSize) { 7 const msgKey = "DOM:WebManifest:fetchIcon"; 8 9 const actor = 10 aBrowser.browsingContext.currentWindowGlobal.getActor("ManifestMessages"); 11 const reply = await actor.sendQuery(msgKey, { manifest, iconSize }); 12 if (!reply.success) { 13 throw reply.result; 14 } 15 return reply.result; 16 }, 17 18 async contentFetchIcon(aWindow, manifest, iconSize) { 19 return getIcon(aWindow, toIconArray(manifest.icons), iconSize); 20 }, 21 }; 22 23 function parseIconSize(size) { 24 if (size === "any" || size === "") { 25 // We want icons without size specified to sorted 26 // as the largest available icons 27 return Number.MAX_SAFE_INTEGER; 28 } 29 // 100x100 will parse as 100 30 return parseInt(size, 10); 31 } 32 33 // Create an array of icons sorted by their size 34 function toIconArray(icons) { 35 const iconBySize = []; 36 icons.forEach(icon => { 37 const sizes = "sizes" in icon ? icon.sizes : ""; 38 sizes.forEach(size => { 39 iconBySize.push({ src: icon.src, size: parseIconSize(size) }); 40 }); 41 }); 42 return iconBySize.sort((a, b) => a.size - b.size); 43 } 44 45 async function getIcon(aWindow, icons, expectedSize) { 46 if (!icons.length) { 47 throw new Error("Could not find valid icon"); 48 } 49 // We start trying the smallest icon that is larger than the requested 50 // size and go up to the largest icon if they fail, if all those fail 51 // go back down to the smallest 52 let index = icons.findIndex(icon => icon.size >= expectedSize); 53 if (index === -1) { 54 index = icons.length - 1; 55 } 56 57 return fetchIcon(aWindow, icons[index].src).catch(() => { 58 // Remove all icons with the failed source, the same source 59 // may have been used for multiple sizes 60 icons = icons.filter(x => x.src !== icons[index].src); 61 return getIcon(aWindow, icons, expectedSize); 62 }); 63 } 64 65 async function fetchIcon(aWindow, src) { 66 const iconURL = new aWindow.URL(src, aWindow.location); 67 // If this is already a data URL then no need to load it again. 68 if (iconURL.protocol === "data:") { 69 return iconURL.href; 70 } 71 72 const request = new aWindow.Request(iconURL, { mode: "cors" }); 73 request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_IMAGE); 74 const response = await aWindow.fetch(request); 75 const blob = await response.blob(); 76 return new Promise((resolve, reject) => { 77 const reader = new FileReader(); 78 reader.onloadend = () => resolve(reader.result); 79 reader.onerror = reject; 80 reader.readAsDataURL(blob); 81 }); 82 }