File indexing completed on 2024-05-05 17:41:30
0001 /* 0002 Copyright (C) 2019 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 class TabUtils { 0019 // Gets the currently viewed tab 0020 static getCurrentTab() { 0021 return new Promise((resolve, reject) => { 0022 chrome.tabs.query({ 0023 active: true, 0024 currentWindow: true 0025 }, (tabs) => { 0026 const error = chrome.runtime.lastError; 0027 if (error) { 0028 return reject(error.message); 0029 } 0030 0031 const tab = tabs[0]; 0032 if (!tab) { 0033 return reject("NO_TAB"); 0034 } 0035 0036 resolve(tab); 0037 }); 0038 }); 0039 } 0040 0041 // Gets the URLs of the currently viewed tab including all of its iframes 0042 static getCurrentTabFramesUrls() { 0043 return new Promise((resolve, reject) => { 0044 TabUtils.getCurrentTab().then((tab) => { 0045 chrome.tabs.executeScript({ 0046 allFrames: true, // so we also catch iframe videos 0047 code: `window.location.href`, 0048 runAt: "document_start" 0049 }, (result) => { 0050 const error = chrome.runtime.lastError; 0051 if (error) { 0052 return reject(error.message); 0053 } 0054 0055 resolve(result); 0056 }); 0057 }); 0058 }); 0059 } 0060 }; 0061 0062 class MPrisBlocker { 0063 getAllowed() { 0064 return new Promise((resolve, reject) => { 0065 Promise.all([ 0066 SettingsUtils.get(), 0067 TabUtils.getCurrentTabFramesUrls() 0068 ]).then((result) => { 0069 0070 const settings = result[0]; 0071 const currentUrls = result[1]; 0072 0073 const mprisSettings = settings.mpris; 0074 if (!mprisSettings.enabled) { 0075 return reject("MPRIS_DISABLED"); 0076 } 0077 0078 if (!currentUrls) { // can this happen? 0079 return reject("NO_URLS"); 0080 } 0081 0082 const origins = currentUrls.map((url) => { 0083 try { 0084 return new URL(url).origin; 0085 } catch (e) { 0086 console.warn("Invalid url", url); 0087 return ""; 0088 } 0089 }).filter((origin) => { 0090 return !!origin; 0091 }); 0092 0093 if (origins.length === 0) { 0094 return reject("NO_ORIGINS"); 0095 } 0096 0097 const uniqueOrigins = [...new Set(origins)]; 0098 0099 const websiteSettings = mprisSettings.websiteSettings || {}; 0100 0101 let response = { 0102 origins: {}, 0103 mprisSettings 0104 }; 0105 0106 for (const origin of uniqueOrigins) { 0107 let allowed = true; 0108 if (typeof MPRIS_WEBSITE_SETTINGS[origin] === "boolean") { 0109 allowed = MPRIS_WEBSITE_SETTINGS[origin]; 0110 } 0111 if (typeof websiteSettings[origin] === "boolean") { 0112 allowed = websiteSettings[origin]; 0113 } 0114 0115 response.origins[origin] = allowed; 0116 } 0117 0118 resolve(response); 0119 0120 }, reject); 0121 }); 0122 } 0123 0124 setAllowed(origin, allowed) { 0125 return SettingsUtils.get().then((settings) => { 0126 const mprisSettings = settings.mpris; 0127 if (!mprisSettings.enabled) { 0128 return reject("MPRIS_DISABLED"); 0129 } 0130 0131 let websiteSettings = mprisSettings.websiteSettings || {}; 0132 0133 let implicitAllowed = true; 0134 if (typeof MPRIS_WEBSITE_SETTINGS[origin] === "boolean") { 0135 implicitAllowed = MPRIS_WEBSITE_SETTINGS[origin]; 0136 } 0137 0138 if (allowed !== implicitAllowed) { 0139 websiteSettings[origin] = allowed; 0140 } else { 0141 delete websiteSettings[origin]; 0142 } 0143 0144 mprisSettings.websiteSettings = websiteSettings; 0145 0146 return SettingsUtils.set({ 0147 mpris: mprisSettings 0148 }); 0149 }); 0150 } 0151 }; 0152 0153 document.addEventListener("DOMContentLoaded", () => { 0154 0155 sendMessage("browserAction", "getStatus").then((status) => { 0156 0157 switch (status.portStatus) { 0158 case "UNSUPPORTED_OS": 0159 document.getElementById("unsupported_os_error").classList.remove("hidden"); 0160 break; 0161 0162 case "STARTUP_FAILED": { 0163 document.getElementById("startup_error").classList.remove("hidden"); 0164 0165 const errorText = status.portLastErrorMessage; 0166 // Don't show generic error on startup failure. There's already an explanation. 0167 if (errorText && errorText !== "UNKNOWN") { 0168 const errorTextItem = document.getElementById("startup_error_text"); 0169 errorTextItem.innerText = errorText; 0170 errorTextItem.classList.remove("hidden"); 0171 } 0172 break; 0173 } 0174 0175 default: { 0176 document.getElementById("main").classList.remove("hidden"); 0177 0178 let errorText = status.portLastErrorMessage; 0179 if (errorText === "UNKNOWN") { 0180 errorText = chrome.i18n.getMessage("general_error_unknown"); 0181 } 0182 0183 if (errorText) { 0184 document.getElementById("runtime_error_text").innerText = errorText; 0185 document.getElementById("runtime_error").classList.remove("hidden"); 0186 0187 // There's some content, hide dummy placeholder 0188 document.getElementById("dummy-main").classList.add("hidden"); 0189 } 0190 0191 break; 0192 } 0193 } 0194 0195 // HACK so the extension can tell we closed, see "browserAction" "ready" callback in extension.js 0196 chrome.runtime.onConnect.addListener((port) => { 0197 if (port.name !== "browserActionPort") { 0198 return; 0199 } 0200 0201 // do we need to do something with the port here? 0202 }); 0203 sendMessage("browserAction", "ready"); 0204 }); 0205 0206 // MPris blocker checkboxes 0207 const blocker = new MPrisBlocker(); 0208 blocker.getAllowed().then((result) => { 0209 const origins = result.origins; 0210 0211 if (Object.entries(origins).length === 0) { // "isEmpty" 0212 return; 0213 } 0214 0215 // To keep media controls setting from always showing up, only show them, if: 0216 // - There is actually a player anywhere on this tab 0217 // or, since when mpris is disabled, there are never any players 0218 // - when media controls are disabled for any origin on this tab 0219 new Promise((resolve, reject) => { 0220 for (let origin in origins) { 0221 if (origins[origin] === false) { 0222 return resolve("HAS_BLOCKED"); 0223 } 0224 } 0225 0226 TabUtils.getCurrentTab().then((tab) => { 0227 return sendMessage("mpris", "hasTabPlayer", { 0228 tabId: tab.id 0229 }); 0230 }).then((playerIds) => { 0231 if (playerIds.length > 0) { 0232 return resolve("HAS_PLAYER"); 0233 } 0234 0235 reject("NO_PLAYER_NO_BLOCKED"); 0236 }); 0237 }).then(() => { 0238 // There's some content, hide dummy placeholder 0239 document.getElementById("dummy-main").classList.add("hidden"); 0240 0241 let blacklistInfoElement = document.querySelector(".mpris-blacklist-info"); 0242 blacklistInfoElement.classList.remove("hidden"); 0243 0244 let originsListElement = blacklistInfoElement.querySelector("ul.mpris-blacklist-origins"); 0245 0246 for (const origin in origins) { 0247 const originAllowed = origins[origin]; 0248 0249 let blockListElement = document.createElement("li"); 0250 0251 let labelElement = document.createElement("label"); 0252 labelElement.innerText = origin; 0253 0254 let checkboxElement = document.createElement("input"); 0255 checkboxElement.type = "checkbox"; 0256 checkboxElement.checked = (originAllowed === true); 0257 checkboxElement.addEventListener("click", (e) => { 0258 // Let us handle (un)checking the checkbox when setAllowed succeeds 0259 e.preventDefault(); 0260 0261 const allowed = checkboxElement.checked; 0262 blocker.setAllowed(origin, allowed).then(() => { 0263 checkboxElement.checked = allowed; 0264 }, (err) => { 0265 console.warn("Failed to change media controls settings:", err); 0266 }); 0267 }); 0268 0269 labelElement.insertBefore(checkboxElement, labelElement.firstChild); 0270 0271 blockListElement.appendChild(labelElement); 0272 0273 originsListElement.appendChild(blockListElement); 0274 } 0275 }, (err) => { 0276 console.log("Not showing media controls settings because", err); 0277 }); 0278 }, (err) => { 0279 console.warn("Failed to check for whether media controls are blocked", err); 0280 }); 0281 });