summaryrefslogtreecommitdiffstats
path: root/browser/actors/EncryptedMediaParent.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/actors/EncryptedMediaParent.sys.mjs')
-rw-r--r--browser/actors/EncryptedMediaParent.sys.mjs267
1 files changed, 267 insertions, 0 deletions
diff --git a/browser/actors/EncryptedMediaParent.sys.mjs b/browser/actors/EncryptedMediaParent.sys.mjs
new file mode 100644
index 0000000000..46a76ccb28
--- /dev/null
+++ b/browser/actors/EncryptedMediaParent.sys.mjs
@@ -0,0 +1,267 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+XPCOMUtils.defineLazyGetter(lazy, "gBrandBundle", function() {
+ return Services.strings.createBundle(
+ "chrome://branding/locale/brand.properties"
+ );
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "gNavigatorBundle", function() {
+ return Services.strings.createBundle(
+ "chrome://browser/locale/browser.properties"
+ );
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "gFluentStrings", function() {
+ return new Localization(["branding/brand.ftl", "browser/browser.ftl"], true);
+});
+
+export class EncryptedMediaParent extends JSWindowActorParent {
+ isUiEnabled() {
+ return Services.prefs.getBoolPref("browser.eme.ui.enabled");
+ }
+
+ ensureEMEEnabled(aBrowser, aKeySystem) {
+ Services.prefs.setBoolPref("media.eme.enabled", true);
+ if (
+ aKeySystem &&
+ aKeySystem == "com.widevine.alpha" &&
+ Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
+ !Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")
+ ) {
+ Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
+ }
+ aBrowser.reload();
+ }
+
+ isKeySystemVisible(aKeySystem) {
+ if (!aKeySystem) {
+ return false;
+ }
+ if (
+ aKeySystem == "com.widevine.alpha" &&
+ Services.prefs.getPrefType("media.gmp-widevinecdm.visible")
+ ) {
+ return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
+ }
+ return true;
+ }
+
+ getMessageWithBrandName(aNotificationId) {
+ let msgId = "emeNotifications." + aNotificationId + ".message";
+ return lazy.gNavigatorBundle.formatStringFromName(msgId, [
+ lazy.gBrandBundle.GetStringFromName("brandShortName"),
+ ]);
+ }
+
+ receiveMessage(aMessage) {
+ // The top level browsing context's embedding element should be a xul browser element.
+ let browser = this.browsingContext.top.embedderElement;
+
+ if (!browser) {
+ // We don't have a browser so bail!
+ return;
+ }
+
+ let parsedData;
+ try {
+ parsedData = JSON.parse(aMessage.data);
+ } catch (ex) {
+ console.error("Malformed EME video message with data: ", aMessage.data);
+ return;
+ }
+ let { status, keySystem } = parsedData;
+
+ // First, see if we need to do updates. We don't need to do anything for
+ // hidden keysystems:
+ if (!this.isKeySystemVisible(keySystem)) {
+ return;
+ }
+ if (status == "cdm-not-installed") {
+ Services.obs.notifyObservers(browser, "EMEVideo:CDMMissing");
+ }
+
+ // Don't need to show UI if disabled.
+ if (!this.isUiEnabled()) {
+ return;
+ }
+
+ let notificationId;
+ let buttonCallback;
+ let supportPage;
+ // Notification message can be either a string or a DOM fragment.
+ let notificationMessage;
+ switch (status) {
+ case "available":
+ case "cdm-created":
+ // Only show the chain icon for proprietary CDMs. Clearkey is not one.
+ if (keySystem != "org.w3.clearkey") {
+ this.showPopupNotificationForSuccess(browser, keySystem);
+ }
+ // ... and bail!
+ return;
+
+ case "api-disabled":
+ case "cdm-disabled":
+ notificationId = "drmContentDisabled";
+ buttonCallback = () => {
+ this.ensureEMEEnabled(browser, keySystem);
+ };
+ notificationMessage = lazy.gNavigatorBundle.GetStringFromName(
+ "emeNotifications.drmContentDisabled.message2"
+ );
+ supportPage = "drm-content";
+ break;
+
+ case "cdm-not-installed":
+ notificationId = "drmContentCDMInstalling";
+ notificationMessage = this.getMessageWithBrandName(notificationId);
+ break;
+
+ case "cdm-not-supported":
+ // Not to pop up user-level notification because they cannot do anything
+ // about it.
+ return;
+ default:
+ console.error(
+ new Error(
+ "Unknown message ('" +
+ status +
+ "') dealing with EME key request: " +
+ aMessage.data
+ )
+ );
+ return;
+ }
+
+ // Now actually create the notification
+
+ let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
+ if (notificationBox.getNotificationWithValue(notificationId)) {
+ return;
+ }
+
+ let buttons = [];
+ if (supportPage) {
+ buttons.push({ supportPage });
+ }
+ if (buttonCallback) {
+ let msgPrefix = "emeNotifications." + notificationId + ".";
+ let manageLabelId = msgPrefix + "button.label";
+ let manageAccessKeyId = msgPrefix + "button.accesskey";
+ buttons.push({
+ label: lazy.gNavigatorBundle.GetStringFromName(manageLabelId),
+ accessKey: lazy.gNavigatorBundle.GetStringFromName(manageAccessKeyId),
+ callback: buttonCallback,
+ });
+ }
+
+ let iconURL = "chrome://browser/skin/drm-icon.svg";
+ notificationBox.appendNotification(
+ notificationId,
+ {
+ label: notificationMessage,
+ image: iconURL,
+ priority: notificationBox.PRIORITY_INFO_HIGH,
+ },
+ buttons
+ );
+ }
+
+ async showPopupNotificationForSuccess(aBrowser) {
+ // We're playing EME content! Remove any "we can't play because..." messages.
+ let notificationBox = aBrowser.getTabBrowser().getNotificationBox(aBrowser);
+ ["drmContentDisabled", "drmContentCDMInstalling"].forEach(function(value) {
+ let notification = notificationBox.getNotificationWithValue(value);
+ if (notification) {
+ notificationBox.removeNotification(notification);
+ }
+ });
+
+ // Don't bother creating it if it's already there:
+ if (
+ aBrowser.ownerGlobal.PopupNotifications.getNotification(
+ "drmContentPlaying",
+ aBrowser
+ )
+ ) {
+ return;
+ }
+
+ let msgPrefix = "eme-notifications-drm-content-playing";
+ let msgId = msgPrefix;
+ let manageLabelId = msgPrefix + "-manage";
+ let manageAccessKeyId = msgPrefix + "-manage-accesskey";
+ let dismissLabelId = msgPrefix + "-dismiss";
+ let dismissAccessKeyId = msgPrefix + "-dismiss-accesskey";
+
+ let [
+ message,
+ manageLabel,
+ manageAccessKey,
+ dismissLabel,
+ dismissAccessKey,
+ ] = await lazy.gFluentStrings.formatValues([
+ msgId,
+ manageLabelId,
+ manageAccessKeyId,
+ dismissLabelId,
+ dismissAccessKeyId,
+ ]);
+
+ let anchorId = "eme-notification-icon";
+ let firstPlayPref = "browser.eme.ui.firstContentShown";
+ let document = aBrowser.ownerDocument;
+ if (
+ !Services.prefs.getPrefType(firstPlayPref) ||
+ !Services.prefs.getBoolPref(firstPlayPref)
+ ) {
+ document.getElementById(anchorId).setAttribute("firstplay", "true");
+ Services.prefs.setBoolPref(firstPlayPref, true);
+ } else {
+ document.getElementById(anchorId).removeAttribute("firstplay");
+ }
+
+ let mainAction = {
+ label: manageLabel,
+ accessKey: manageAccessKey,
+ callback() {
+ aBrowser.ownerGlobal.openPreferences("general-drm");
+ },
+ dismiss: true,
+ };
+
+ let secondaryActions = [
+ {
+ label: dismissLabel,
+ accessKey: dismissAccessKey,
+ callback: () => {},
+ dismiss: true,
+ },
+ ];
+
+ let options = {
+ dismissed: true,
+ eventCallback: aTopic => aTopic == "swapping",
+ learnMoreURL:
+ Services.urlFormatter.formatURLPref("app.support.baseURL") +
+ "drm-content",
+ hideClose: true,
+ };
+ aBrowser.ownerGlobal.PopupNotifications.show(
+ aBrowser,
+ "drmContentPlaying",
+ message,
+ anchorId,
+ mainAction,
+ secondaryActions,
+ options
+ );
+ }
+}