2 Copyright (C) 2017 Kai Uwe Broulik <kde@privat.broulik.de>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 3 of
7 the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 addCallback("tabsrunner", "activate", function (message) {
19 var tabId = message.tabId;
21 console.log("Tabs Runner requested to activate tab with id", tabId);
26 addCallback("tabsrunner", "setMuted", function (message) {
28 var tabId = message.tabId;
29 var muted = message.muted;
31 chrome.tabs.update(tabId, {muted: muted}, function (tab) {
33 if (chrome.runtime.lastError || !tab) { // this "lastError" stuff feels so archaic
34 // failed to mute/unmute
41 // only forward certain tab properties back to our host
42 var whitelistedTabProperties = [
43 "id", "active", "audible", "favIconUrl", "incognito", "title", "url", "mutedInfo"
46 // FIXME We really should enforce some kind of security policy, so only e.g. plasmashell and krunner
47 // may access your tabs
48 addCallback("tabsrunner", "getTabs", function (message) {
49 chrome.tabs.query({}, function (tabs) {
50 // remove incognito tabs and properties not in whitelist
51 var filteredTabs = tabs;
53 // Firefox before 67 runs extensions in incognito by default
54 // but we keep running after an update, so exclude those tabs for it
56 filteredTabs = filteredTabs.filter(function (tab) {
57 return !tab.incognito;
61 var filteredTabs = filterArrayObjects(filteredTabs, whitelistedTabProperties);
63 // Shared between the callbacks
64 var total = filteredTabs.length;
66 var sendTabsIfComplete = function() {
72 subsystem: "tabsrunner",
78 for (let tabIndex in filteredTabs) {
79 let currentIndex = tabIndex; // Not shared
80 var favIconUrl = filteredTabs[tabIndex].favIconUrl;
84 } else if (favIconUrl.match(/^data:image/)) {
86 filteredTabs[currentIndex].favIconData = favIconUrl;
87 filteredTabs[currentIndex].favIconUrl = "";
90 // Send a request to fill the cache (=no timeout)
91 let xhrForCache = new XMLHttpRequest();
92 xhrForCache.open("GET", favIconUrl);
95 // Try to fetch from (hopefully) the cache (100ms timeout)
96 let xhr = new XMLHttpRequest();
97 xhr.onreadystatechange = function() {
98 if (xhr.readyState != 4) {
103 filteredTabs[currentIndex].favIconData = "";
104 sendTabsIfComplete();
108 var reader = new FileReader();
109 reader.onloadend = function() {
110 filteredTabs[currentIndex].favIconData = reader.result;
111 sendTabsIfComplete();
113 reader.readAsDataURL(xhr.response);
115 xhr.open('GET', favIconUrl);
116 xhr.responseType = 'blob';