File indexing completed on 2024-04-28 05:32:50

0001 /*
0002     Copyright (C) 2017 Kai Uwe Broulik <kde@privat.broulik.de>
0003 
0004     This program is free software; you can redistribute it and/or
0005     modify it under the terms of the GNU General Public License as
0006     published by the Free Software Foundation; either version 3 of
0007     the License, or (at your option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012     GNU General Public License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 // URL - data URL
0019 let favIconDataForUrl = {};
0020 let clearFavIconDataTimeoutId = 0;
0021 let runningGetTabsQueries = 0;
0022 
0023 addCallback("tabsrunner", "activate", function (message) {
0024     var tabId = message.tabId;
0025 
0026     console.log("Tabs Runner requested to activate tab with id", tabId);
0027 
0028     raiseTab(tabId);
0029 });
0030 
0031 addCallback("tabsrunner", "setMuted", function (message) {
0032 
0033     var tabId = message.tabId;
0034     var muted = message.muted;
0035 
0036     chrome.tabs.update(tabId, {muted: muted}, function (tab) {
0037 
0038         if (chrome.runtime.lastError || !tab) { // this "lastError" stuff feels so archaic
0039             // failed to mute/unmute
0040             return;
0041         }
0042     });
0043 
0044 });
0045 
0046 // only forward certain tab properties back to our host
0047 var whitelistedTabProperties = [
0048     "id", "active", "audible", "favIconUrl", "incognito", "title", "url", "mutedInfo"
0049 ];
0050 
0051 // FIXME We really should enforce some kind of security policy, so only e.g. plasmashell and krunner
0052 // may access your tabs
0053 addCallback("tabsrunner", "getTabs", function (message) {
0054     ++runningGetTabsQueries;
0055 
0056     chrome.tabs.query({
0057         windowType: "normal"
0058     }, (tabs) => {
0059         if (clearFavIconDataTimeoutId) {
0060             clearTimeout(clearFavIconDataTimeoutId);
0061             clearFavIconDataTimeoutId = 0;
0062         }
0063 
0064         // remove incognito tabs and properties not in whitelist
0065         let filteredTabs = tabs;
0066 
0067         // Firefox before 67 runs extensions in incognito by default
0068         // but we keep running after an update, so exclude those tabs for it
0069         if (IS_FIREFOX) {
0070             filteredTabs = filteredTabs.filter(function (tab) {
0071                 return !tab.incognito;
0072             });
0073         }
0074 
0075         filteredTabs = filterArrayObjects(filteredTabs, whitelistedTabProperties);
0076 
0077         let favIconUrlsToFetch = new Set();
0078 
0079         // Collect a set of fav icons to be requested
0080         filteredTabs.forEach((tab) => {
0081             const url = tab.favIconUrl;
0082             if (!url) {
0083                 return;
0084             }
0085 
0086             // Already a data URL
0087             if (url.match(/^data:image/)) {
0088                 return;
0089             }
0090 
0091             // Already in cache
0092             if (favIconDataForUrl[url]) {
0093                 return;
0094             }
0095 
0096             favIconUrlsToFetch.add(url);
0097         });
0098 
0099         // Prepare the download requests for all fav icons
0100         let requests = [];
0101         favIconUrlsToFetch.forEach((url) => {
0102             requests.push(new Promise((resolve) => {
0103                 fetch(url, {
0104                     cache: "force-cache"
0105                 }).then((response) => {
0106                     if (!response.ok) {
0107                         return resolve();
0108                     }
0109 
0110                     response.blob().then((blob) => {
0111                         let reader = new FileReader();
0112                         reader.onloadend = function() {
0113                             favIconDataForUrl[url] = reader.result;
0114                             return resolve();
0115                         }
0116                         reader.readAsDataURL(blob);
0117                     }, (err) => {
0118                         console.warn("Failed to read response of", url, "as blob", err);
0119                         resolve();
0120                     });
0121                 }, (err) => {
0122                     console.warn("Failed to get favicon from", url, err);
0123                     resolve();
0124                 });
0125             }));
0126         });
0127 
0128         // Download all favicons and send them out
0129         Promise.all(requests).then(() => {
0130             filteredTabs = filteredTabs.map((tab) => {
0131                 const favIconUrl = tab.favIconUrl;
0132                 if (!favIconUrl) {
0133                     return tab;
0134                 }
0135 
0136                 if (favIconUrl.match(/^data:image/)) {
0137                     tab.favIconData = favIconUrl;
0138                     return tab
0139                 }
0140 
0141                 const data = favIconDataForUrl[favIconUrl];
0142                 if (data) {
0143                     tab.favIconData = data;
0144                 }
0145                 return tab;
0146             });
0147 
0148             --runningGetTabsQueries;
0149             if (runningGetTabsQueries === 0) {
0150                 clearFavIconDataTimeoutId = setTimeout(() => {
0151                     favIconDataForUrl = {};
0152                     clearFavIconDataTimeoutId = 0;
0153                 }, 60000);
0154             }
0155 
0156             port.postMessage({
0157                 subsystem: "tabsrunner",
0158                 event: "gotTabs",
0159                 tabs: filteredTabs
0160             });
0161         });
0162     });
0163 });