no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / manifest / ManifestIcons.sys.mjs
blob62e07e07ae1e71afc587d3c6098d5ea990a6d4c1
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/. */
5 export var ManifestIcons = {
6   async browserFetchIcon(aBrowser, manifest, iconSize) {
7     const msgKey = "DOM:WebManifest:fetchIcon";
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   },
18   async contentFetchIcon(aWindow, manifest, iconSize) {
19     return getIcon(aWindow, toIconArray(manifest.icons), iconSize);
20   },
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);
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);
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   }
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   });
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   }
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   });