Commit ca085f50 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

Add per-origin media controls blacklist

Allows disabling them for a specific origin in case it causes trouble or unwanted media controls.

BUG: 394126

Differential Revision: https://phabricator.kde.org/D24194
parent f0fbc829
......@@ -9,6 +9,15 @@
"message": "Plasma Browser Integration"
},
"browseraction_mpris_title": {
"description": "Title for Media controls in popup",
"message": "Media Controls"
},
"browseraction_mpris_enable_on": {
"description": "Heading for list of domains to enable media controls on",
"message": "Enable media controls on:"
},
"options_title": {
"description": "Title for settings page",
"message": "Plasma Integration Settings"
......
......@@ -16,10 +16,14 @@ header {
text-align: center;
}
section {
margin: 0 0.5em;
}
section > header {
background: #F0F0F0;
color: #757777;
margin: 0 -1em .5em -1em;
margin: 0 -1em -0.5em -1em;
}
.message {
......@@ -31,6 +35,9 @@ section > header {
display: block;
background: center no-repeat;
}
.message.with-icon.general::before {
background-image: url('icons/plasma.svg');
}
.message.with-icon.error::before {
background-image: url('icons/sad-face.svg');
}
......@@ -44,3 +51,19 @@ section > header {
filter: invert(1);
}
}
/* Media controls blacklist */
.mpris-blacklist-info {
padding: 0.5em 0;
}
.mpris-blacklist-info p {
padding: 0 0.5em;
}
.mpris-blacklist-info ul {
display: block;
padding: 0 0.5em;
}
.mpris-blacklist-info ul > li {
display: block;
margin-bottom: 0.5em;
}
......@@ -7,6 +7,8 @@
<title></title>
<link rel="stylesheet" href="action_popup.css">
<script src="i18n.js"></script>
<script src="constants.js"></script>
<script src="utils.js"></script>
<script src="content-utils.js"></script>
<script src="action_popup.js"></script>
</head>
......@@ -41,7 +43,15 @@
</div>
<div id="main" class="hidden">
<!-- Stuff like media controls settings, share functionalty, etc would eventually go here -->
<div id="dummy-main" class="message general with-icon"></div>
<section class="mpris-blacklist-info hidden">
<header data-i18n="browseraction_mpris_title">I18N</header>
<p data-i18n="browseraction_mpris_enable_on">I18N</p>
<ul class="mpris-blacklist-origins"></ul>
</section>
</div>
</main>
......
......@@ -15,6 +15,141 @@
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.tabs.executeScript({
allFrames: true, // so we also catch iframe videos
code: `window.location.href`,
runAt: "document_start"
}, (result) => {
const error = chrome.runtime.lastError;
if (error) {
return reject(error.message);
}
resolve(result);
});
});
});
}
};
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) => {
return !!origin;
});
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) => {
......@@ -39,6 +174,9 @@ document.addEventListener("DOMContentLoaded", () => {
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;
......@@ -55,4 +193,79 @@ document.addEventListener("DOMContentLoaded", () => {
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);
});
});
......@@ -18,7 +18,8 @@
DEFAULT_EXTENSION_SETTINGS = {
mpris: {
enabled: true
enabled: true,
websiteSettings: {}
},
mprisMediaSessions: {
enabled: true
......@@ -46,3 +47,8 @@ IS_FIREFOX = (typeof InstallTrigger !== "undefined"); // heh.
// NOTE if you change this, make sure to adjust the error message shown in action_popup.html
SUPPORTED_PLATFORMS = ["linux", "openbsd", "freebsd"];
// Default MPRIS settings for websites
const MPRIS_WEBSITE_SETTINGS = {
//"https://www.example.com": false
};
......@@ -65,9 +65,23 @@ SettingsUtils.get().then((items) => {
const mpris = items.mpris;
if (mpris.enabled) {
loadMpris();
if (items.mprisMediaSessions.enabled) {
loadMediaSessionsShim();
const origin = window.location.origin;
const websiteSettings = mpris.websiteSettings || {};
let mprisAllowed = true;
if (typeof MPRIS_WEBSITE_SETTINGS[origin] === "boolean") {
mprisAllowed = MPRIS_WEBSITE_SETTINGS[origin];
}
if (typeof websiteSettings[origin] === "boolean") {
mprisAllowed = websiteSettings[origin];
}
if (mprisAllowed) {
loadMpris();
if (items.mprisMediaSessions.enabled) {
loadMediaSessionsShim();
}
}
}
......
......@@ -195,3 +195,11 @@ addRuntimeCallback("mpris", ["metadata", "callbacks"], function (message, sender
sendPortMessage("mpris", action, payload);
}
});
addRuntimeCallback("mpris", "hasTabPlayer", (message) => {
const playersOnTab = playerIds.filter((playerId) => {
return playerId.startsWith(message.tabId + "-");
});
return Promise.resolve(playersOnTab);
});
......@@ -86,7 +86,6 @@ var portStatus = "";
var portLastErrorMessage = undefined;
function updateBrowserAction() {
let enableAction = false;
if (portStatus === "UNSUPPORTED_OS" || portStatus === "STARTUP_FAILED") {
chrome.browserAction.setIcon({
path: {
......@@ -96,22 +95,14 @@ function updateBrowserAction() {
"128": "icons/plasma-disabled-128.png"
}
});
enableAction = true;
}
if (portLastErrorMessage) {
chrome.browserAction.setBadgeText({ text: "!" });
chrome.browserAction.setBadgeBackgroundColor({ color: "#da4453" }); // breeze "negative" color
enableAction = true;
} else {
chrome.browserAction.setBadgeText({ text: "" });
}
if (enableAction) {
chrome.browserAction.enable();
} else {
chrome.browserAction.disable();
}
}
updateBrowserAction();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment