Bug 1857998 [wpt PR 42432] - [css-nesting-ident] Enable relaxed syntax, a=testonly
[gecko.git] / browser / actors / EncryptedMediaParent.sys.mjs
blobad8f906407452c7b3e047e580792135e3ff0cc19
1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 const lazy = {};
8 ChromeUtils.defineLazyGetter(lazy, "gBrandBundle", function () {
9   return Services.strings.createBundle(
10     "chrome://branding/locale/brand.properties"
11   );
12 });
14 ChromeUtils.defineLazyGetter(lazy, "gNavigatorBundle", function () {
15   return Services.strings.createBundle(
16     "chrome://browser/locale/browser.properties"
17   );
18 });
20 ChromeUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
21   return new Localization(["branding/brand.ftl", "browser/browser.ftl"], true);
22 });
24 export class EncryptedMediaParent extends JSWindowActorParent {
25   isUiEnabled() {
26     return Services.prefs.getBoolPref("browser.eme.ui.enabled");
27   }
29   ensureEMEEnabled(aBrowser, aKeySystem) {
30     Services.prefs.setBoolPref("media.eme.enabled", true);
31     if (
32       aKeySystem &&
33       aKeySystem == "com.widevine.alpha" &&
34       Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
35       !Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")
36     ) {
37       Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
38     }
39     aBrowser.reload();
40   }
42   isKeySystemVisible(aKeySystem) {
43     if (!aKeySystem) {
44       return false;
45     }
46     if (
47       aKeySystem == "com.widevine.alpha" &&
48       Services.prefs.getPrefType("media.gmp-widevinecdm.visible")
49     ) {
50       return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
51     }
52     return true;
53   }
55   getMessageWithBrandName(aNotificationId) {
56     let msgId = "emeNotifications." + aNotificationId + ".message";
57     return lazy.gNavigatorBundle.formatStringFromName(msgId, [
58       lazy.gBrandBundle.GetStringFromName("brandShortName"),
59     ]);
60   }
62   receiveMessage(aMessage) {
63     // The top level browsing context's embedding element should be a xul browser element.
64     let browser = this.browsingContext.top.embedderElement;
66     if (!browser) {
67       // We don't have a browser so bail!
68       return;
69     }
71     let parsedData;
72     try {
73       parsedData = JSON.parse(aMessage.data);
74     } catch (ex) {
75       console.error("Malformed EME video message with data: ", aMessage.data);
76       return;
77     }
78     let { status, keySystem } = parsedData;
80     // First, see if we need to do updates. We don't need to do anything for
81     // hidden keysystems:
82     if (!this.isKeySystemVisible(keySystem)) {
83       return;
84     }
85     if (status == "cdm-not-installed") {
86       Services.obs.notifyObservers(browser, "EMEVideo:CDMMissing");
87     }
89     // Don't need to show UI if disabled.
90     if (!this.isUiEnabled()) {
91       return;
92     }
94     let notificationId;
95     let buttonCallback;
96     let supportPage;
97     // Notification message can be either a string or a DOM fragment.
98     let notificationMessage;
99     switch (status) {
100       case "available":
101       case "cdm-created":
102         // Only show the chain icon for proprietary CDMs. Clearkey is not one.
103         if (keySystem != "org.w3.clearkey") {
104           this.showPopupNotificationForSuccess(browser, keySystem);
105         }
106         // ... and bail!
107         return;
109       case "api-disabled":
110       case "cdm-disabled":
111         notificationId = "drmContentDisabled";
112         buttonCallback = () => {
113           this.ensureEMEEnabled(browser, keySystem);
114         };
115         notificationMessage = lazy.gNavigatorBundle.GetStringFromName(
116           "emeNotifications.drmContentDisabled.message2"
117         );
118         supportPage = "drm-content";
119         break;
121       case "cdm-not-installed":
122         notificationId = "drmContentCDMInstalling";
123         notificationMessage = this.getMessageWithBrandName(notificationId);
124         break;
126       case "cdm-not-supported":
127         // Not to pop up user-level notification because they cannot do anything
128         // about it.
129         return;
130       default:
131         console.error(
132           new Error(
133             "Unknown message ('" +
134               status +
135               "') dealing with EME key request: " +
136               aMessage.data
137           )
138         );
139         return;
140     }
142     // Now actually create the notification
144     let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
145     if (notificationBox.getNotificationWithValue(notificationId)) {
146       return;
147     }
149     let buttons = [];
150     if (supportPage) {
151       buttons.push({ supportPage });
152     }
153     if (buttonCallback) {
154       let msgPrefix = "emeNotifications." + notificationId + ".";
155       let manageLabelId = msgPrefix + "button.label";
156       let manageAccessKeyId = msgPrefix + "button.accesskey";
157       buttons.push({
158         label: lazy.gNavigatorBundle.GetStringFromName(manageLabelId),
159         accessKey: lazy.gNavigatorBundle.GetStringFromName(manageAccessKeyId),
160         callback: buttonCallback,
161       });
162     }
164     let iconURL = "chrome://browser/skin/drm-icon.svg";
165     notificationBox.appendNotification(
166       notificationId,
167       {
168         label: notificationMessage,
169         image: iconURL,
170         priority: notificationBox.PRIORITY_INFO_HIGH,
171       },
172       buttons
173     );
174   }
176   async showPopupNotificationForSuccess(aBrowser) {
177     // We're playing EME content! Remove any "we can't play because..." messages.
178     let notificationBox = aBrowser.getTabBrowser().getNotificationBox(aBrowser);
179     ["drmContentDisabled", "drmContentCDMInstalling"].forEach(function (value) {
180       let notification = notificationBox.getNotificationWithValue(value);
181       if (notification) {
182         notificationBox.removeNotification(notification);
183       }
184     });
186     // Don't bother creating it if it's already there:
187     if (
188       aBrowser.ownerGlobal.PopupNotifications.getNotification(
189         "drmContentPlaying",
190         aBrowser
191       )
192     ) {
193       return;
194     }
196     let msgPrefix = "eme-notifications-drm-content-playing";
197     let msgId = msgPrefix;
198     let manageLabelId = msgPrefix + "-manage";
199     let manageAccessKeyId = msgPrefix + "-manage-accesskey";
200     let dismissLabelId = msgPrefix + "-dismiss";
201     let dismissAccessKeyId = msgPrefix + "-dismiss-accesskey";
203     let [
204       message,
205       manageLabel,
206       manageAccessKey,
207       dismissLabel,
208       dismissAccessKey,
209     ] = await lazy.gFluentStrings.formatValues([
210       msgId,
211       manageLabelId,
212       manageAccessKeyId,
213       dismissLabelId,
214       dismissAccessKeyId,
215     ]);
217     let anchorId = "eme-notification-icon";
218     let firstPlayPref = "browser.eme.ui.firstContentShown";
219     let document = aBrowser.ownerDocument;
220     if (
221       !Services.prefs.getPrefType(firstPlayPref) ||
222       !Services.prefs.getBoolPref(firstPlayPref)
223     ) {
224       document.getElementById(anchorId).setAttribute("firstplay", "true");
225       Services.prefs.setBoolPref(firstPlayPref, true);
226     } else {
227       document.getElementById(anchorId).removeAttribute("firstplay");
228     }
230     let mainAction = {
231       label: manageLabel,
232       accessKey: manageAccessKey,
233       callback() {
234         aBrowser.ownerGlobal.openPreferences("general-drm");
235       },
236       dismiss: true,
237     };
239     let secondaryActions = [
240       {
241         label: dismissLabel,
242         accessKey: dismissAccessKey,
243         callback: () => {},
244         dismiss: true,
245       },
246     ];
248     let options = {
249       dismissed: true,
250       eventCallback: aTopic => aTopic == "swapping",
251       learnMoreURL:
252         Services.urlFormatter.formatURLPref("app.support.baseURL") +
253         "drm-content",
254       hideClose: true,
255     };
256     aBrowser.ownerGlobal.PopupNotifications.show(
257       aBrowser,
258       "drmContentPlaying",
259       message,
260       anchorId,
261       mainAction,
262       secondaryActions,
263       options
264     );
265   }