diff --git a/extension/_locales/en/messages.json b/extension/_locales/en/messages.json
--- a/extension/_locales/en/messages.json
+++ b/extension/_locales/en/messages.json
@@ -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"
diff --git a/extension/action_popup.html b/extension/action_popup.html
--- a/extension/action_popup.html
+++ b/extension/action_popup.html
@@ -7,6 +7,8 @@
+
+
@@ -41,7 +43,15 @@
diff --git a/extension/action_popup.css b/extension/action_popup.css
--- a/extension/action_popup.css
+++ b/extension/action_popup.css
@@ -16,10 +16,14 @@
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 @@
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 @@
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;
+}
diff --git a/extension/action_popup.js b/extension/action_popup.js
--- a/extension/action_popup.js
+++ b/extension/action_popup.js
@@ -15,6 +15,141 @@
along with this program. If not, see .
*/
+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 @@
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 @@
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);
+ });
});
diff --git a/extension/constants.js b/extension/constants.js
--- a/extension/constants.js
+++ b/extension/constants.js
@@ -18,7 +18,8 @@
DEFAULT_EXTENSION_SETTINGS = {
mpris: {
- enabled: true
+ enabled: true,
+ websiteSettings: {}
},
mprisMediaSessions: {
enabled: true
@@ -46,3 +47,8 @@
// 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
+};
diff --git a/extension/content-script.js b/extension/content-script.js
--- a/extension/content-script.js
+++ b/extension/content-script.js
@@ -65,9 +65,23 @@
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();
+ }
}
}
diff --git a/extension/extension-mpris.js b/extension/extension-mpris.js
--- a/extension/extension-mpris.js
+++ b/extension/extension-mpris.js
@@ -195,3 +195,11 @@
sendPortMessage("mpris", action, payload);
}
});
+
+addRuntimeCallback("mpris", "hasTabPlayer", (message) => {
+ const playersOnTab = playerIds.filter((playerId) => {
+ return playerId.startsWith(message.tabId + "-");
+ });
+
+ return Promise.resolve(playersOnTab);
+});
diff --git a/extension/extension.js b/extension/extension.js
--- a/extension/extension.js
+++ b/extension/extension.js
@@ -86,7 +86,6 @@
var portLastErrorMessage = undefined;
function updateBrowserAction() {
- let enableAction = false;
if (portStatus === "UNSUPPORTED_OS" || portStatus === "STARTUP_FAILED") {
chrome.browserAction.setIcon({
path: {
@@ -96,22 +95,14 @@
"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();