288 lines
9.9 KiB
JavaScript
288 lines
9.9 KiB
JavaScript
/*
|
|
Copyright (C) 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 3 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
class TabUtils {
|
|
// Gets the currently viewed tab
|
|
static getCurrentTab() {
|
|
return new Promise((resolve, reject) => {
|
|
chrome.tabs.query({
|
|
active: true,
|
|
currentWindow: true
|
|
}, (tabs) => {
|
|
const error = chrome.runtime.lastError;
|
|
if (error) {
|
|
return reject(error.message);
|
|
}
|
|
|
|
const tab = tabs[0];
|
|
if (!tab) {
|
|
return reject("NO_TAB");
|
|
}
|
|
|
|
resolve(tab);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Gets the URLs of the currently viewed tab including all of its iframes
|
|
static getCurrentTabFramesUrls() {
|
|
return new Promise((resolve, reject) => {
|
|
TabUtils.getCurrentTab().then((tab) => {
|
|
chrome.scripting.executeScript({
|
|
target: {
|
|
tabId: tab.id,
|
|
allFrames: true
|
|
},
|
|
func: () => {
|
|
return window.location.href;
|
|
}
|
|
}, (result) => {
|
|
const error = chrome.runtime.lastError;
|
|
if (error) {
|
|
return reject(error.message);
|
|
}
|
|
|
|
const urls = result.map(result => result.result);
|
|
resolve(urls);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
class MPrisBlocker {
|
|
getAllowed() {
|
|
return new Promise((resolve, reject) => {
|
|
Promise.all([
|
|
SettingsUtils.get(),
|
|
TabUtils.getCurrentTabFramesUrls()
|
|
]).then((result) => {
|
|
|
|
const settings = result[0];
|
|
const currentUrls = result[1];
|
|
|
|
const mprisSettings = settings.mpris;
|
|
if (!mprisSettings.enabled) {
|
|
return reject("MPRIS_DISABLED");
|
|
}
|
|
|
|
if (!currentUrls) { // can this happen?
|
|
return reject("NO_URLS");
|
|
}
|
|
|
|
const origins = currentUrls.map((url) => {
|
|
try {
|
|
return new URL(url).origin;
|
|
} catch (e) {
|
|
console.warn("Invalid url", url);
|
|
return "";
|
|
}
|
|
}).filter((origin) => {
|
|
// for URLs like about:blank origin is "null" (as string!).
|
|
return !!origin && origin !== "null";
|
|
});
|
|
|
|
if (origins.length === 0) {
|
|
return reject("NO_ORIGINS");
|
|
}
|
|
|
|
const uniqueOrigins = [...new Set(origins)];
|
|
|
|
const websiteSettings = mprisSettings.websiteSettings || {};
|
|
|
|
let response = {
|
|
origins: {},
|
|
mprisSettings
|
|
};
|
|
|
|
for (const origin of uniqueOrigins) {
|
|
let allowed = true;
|
|
if (typeof MPRIS_WEBSITE_SETTINGS[origin] === "boolean") {
|
|
allowed = MPRIS_WEBSITE_SETTINGS[origin];
|
|
}
|
|
if (typeof websiteSettings[origin] === "boolean") {
|
|
allowed = websiteSettings[origin];
|
|
}
|
|
|
|
response.origins[origin] = allowed;
|
|
}
|
|
|
|
resolve(response);
|
|
|
|
}, reject);
|
|
});
|
|
}
|
|
|
|
setAllowed(origin, allowed) {
|
|
return SettingsUtils.get().then((settings) => {
|
|
const mprisSettings = settings.mpris;
|
|
if (!mprisSettings.enabled) {
|
|
return reject("MPRIS_DISABLED");
|
|
}
|
|
|
|
let websiteSettings = mprisSettings.websiteSettings || {};
|
|
|
|
let implicitAllowed = true;
|
|
if (typeof MPRIS_WEBSITE_SETTINGS[origin] === "boolean") {
|
|
implicitAllowed = MPRIS_WEBSITE_SETTINGS[origin];
|
|
}
|
|
|
|
if (allowed !== implicitAllowed) {
|
|
websiteSettings[origin] = allowed;
|
|
} else {
|
|
delete websiteSettings[origin];
|
|
}
|
|
|
|
mprisSettings.websiteSettings = websiteSettings;
|
|
|
|
return SettingsUtils.set({
|
|
mpris: mprisSettings
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
|
|
sendMessage("browserAction", "getStatus").then((status) => {
|
|
|
|
switch (status.portStatus) {
|
|
case "UNSUPPORTED_OS":
|
|
document.getElementById("unsupported_os_error").classList.remove("hidden");
|
|
break;
|
|
|
|
case "STARTUP_FAILED": {
|
|
document.getElementById("startup_error").classList.remove("hidden");
|
|
|
|
const errorText = status.portLastErrorMessage;
|
|
// Don't show generic error on startup failure. There's already an explanation.
|
|
if (errorText && errorText !== "UNKNOWN") {
|
|
const errorTextItem = document.getElementById("startup_error_text");
|
|
errorTextItem.innerText = errorText;
|
|
errorTextItem.classList.remove("hidden");
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
document.getElementById("main").classList.remove("hidden");
|
|
|
|
let errorText = status.portLastErrorMessage;
|
|
if (errorText === "UNKNOWN") {
|
|
errorText = chrome.i18n.getMessage("general_error_unknown");
|
|
}
|
|
|
|
if (errorText) {
|
|
document.getElementById("runtime_error_text").innerText = errorText;
|
|
document.getElementById("runtime_error").classList.remove("hidden");
|
|
|
|
// There's some content, hide dummy placeholder
|
|
document.getElementById("dummy-main").classList.add("hidden");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// HACK so the extension can tell we closed, see "browserAction" "ready" callback in extension.js
|
|
chrome.runtime.onConnect.addListener((port) => {
|
|
if (port.name !== "browserActionPort") {
|
|
return;
|
|
}
|
|
|
|
// do we need to do something with the port here?
|
|
});
|
|
sendMessage("browserAction", "ready");
|
|
});
|
|
|
|
// MPris blocker checkboxes
|
|
const blocker = new MPrisBlocker();
|
|
blocker.getAllowed().then((result) => {
|
|
const origins = result.origins;
|
|
|
|
if (Object.entries(origins).length === 0) { // "isEmpty"
|
|
return;
|
|
}
|
|
|
|
// To keep media controls setting from always showing up, only show them, if:
|
|
// - There is actually a player anywhere on this tab
|
|
// or, since when mpris is disabled, there are never any players
|
|
// - when media controls are disabled for any origin on this tab
|
|
new Promise((resolve, reject) => {
|
|
for (let origin in origins) {
|
|
if (origins[origin] === false) {
|
|
return resolve("HAS_BLOCKED");
|
|
}
|
|
}
|
|
|
|
TabUtils.getCurrentTab().then((tab) => {
|
|
return sendMessage("mpris", "hasTabPlayer", {
|
|
tabId: tab.id
|
|
});
|
|
}).then((playerIds) => {
|
|
if (playerIds.length > 0) {
|
|
return resolve("HAS_PLAYER");
|
|
}
|
|
|
|
reject("NO_PLAYER_NO_BLOCKED");
|
|
});
|
|
}).then(() => {
|
|
// There's some content, hide dummy placeholder
|
|
document.getElementById("dummy-main").classList.add("hidden");
|
|
|
|
let blacklistInfoElement = document.querySelector(".mpris-blacklist-info");
|
|
blacklistInfoElement.classList.remove("hidden");
|
|
|
|
let originsListElement = blacklistInfoElement.querySelector("ul.mpris-blacklist-origins");
|
|
|
|
for (const origin in origins) {
|
|
const originAllowed = origins[origin];
|
|
|
|
let blockListElement = document.createElement("li");
|
|
|
|
let labelElement = document.createElement("label");
|
|
labelElement.innerText = origin;
|
|
|
|
let checkboxElement = document.createElement("input");
|
|
checkboxElement.type = "checkbox";
|
|
checkboxElement.checked = (originAllowed === true);
|
|
checkboxElement.addEventListener("click", (e) => {
|
|
// Let us handle (un)checking the checkbox when setAllowed succeeds
|
|
e.preventDefault();
|
|
|
|
const allowed = checkboxElement.checked;
|
|
blocker.setAllowed(origin, allowed).then(() => {
|
|
checkboxElement.checked = allowed;
|
|
}, (err) => {
|
|
console.warn("Failed to change media controls settings:", err);
|
|
});
|
|
});
|
|
|
|
labelElement.insertBefore(checkboxElement, labelElement.firstChild);
|
|
|
|
blockListElement.appendChild(labelElement);
|
|
|
|
originsListElement.appendChild(blockListElement);
|
|
}
|
|
}, (err) => {
|
|
console.log("Not showing media controls settings because", err);
|
|
});
|
|
}, (err) => {
|
|
console.warn("Failed to check for whether media controls are blocked", err);
|
|
});
|
|
});
|