Bug 1687263: part 4) Defer and in some cases avoid removing spellchecking-ranges...
[gecko.git] / browser / actors / EncryptedMediaParent.jsm
blobe05f03b54e11d0acb96de4e8eb684a5c23b3efe7
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 "use strict";
8 var EXPORTED_SYMBOLS = ["EncryptedMediaParent"];
10 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
11 const { XPCOMUtils } = ChromeUtils.import(
12   "resource://gre/modules/XPCOMUtils.jsm"
15 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
16   return Services.strings.createBundle(
17     "chrome://branding/locale/brand.properties"
18   );
19 });
21 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
22   return Services.strings.createBundle(
23     "chrome://browser/locale/browser.properties"
24   );
25 });
27 XPCOMUtils.defineLazyGetter(this, "gFluentStrings", function() {
28   return new Localization(["branding/brand.ftl", "browser/browser.ftl"], true);
29 });
31 XPCOMUtils.defineLazyPreferenceGetter(
32   this,
33   "gProtonDoorhangersEnabled",
34   "browser.proton.doorhangers.enabled",
35   false
38 class EncryptedMediaParent extends JSWindowActorParent {
39   isUiEnabled() {
40     return Services.prefs.getBoolPref("browser.eme.ui.enabled");
41   }
43   ensureEMEEnabled(aBrowser, aKeySystem) {
44     Services.prefs.setBoolPref("media.eme.enabled", true);
45     if (
46       aKeySystem &&
47       aKeySystem == "com.widevine.alpha" &&
48       Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
49       !Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")
50     ) {
51       Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
52     }
53     aBrowser.reload();
54   }
56   isKeySystemVisible(aKeySystem) {
57     if (!aKeySystem) {
58       return false;
59     }
60     if (
61       aKeySystem == "com.widevine.alpha" &&
62       Services.prefs.getPrefType("media.gmp-widevinecdm.visible")
63     ) {
64       return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
65     }
66     return true;
67   }
69   getMessageWithBrandName(aNotificationId) {
70     let msgId = "emeNotifications." + aNotificationId + ".message";
71     return gNavigatorBundle.formatStringFromName(msgId, [
72       gBrandBundle.GetStringFromName("brandShortName"),
73     ]);
74   }
76   receiveMessage(aMessage) {
77     // The top level browsing context's embedding element should be a xul browser element.
78     let browser = this.browsingContext.top.embedderElement;
80     if (!browser) {
81       // We don't have a browser so bail!
82       return;
83     }
85     let parsedData;
86     try {
87       parsedData = JSON.parse(aMessage.data);
88     } catch (ex) {
89       Cu.reportError("Malformed EME video message with data: " + aMessage.data);
90       return;
91     }
92     let { status, keySystem } = parsedData;
94     // First, see if we need to do updates. We don't need to do anything for
95     // hidden keysystems:
96     if (!this.isKeySystemVisible(keySystem)) {
97       return;
98     }
99     if (status == "cdm-not-installed") {
100       Services.obs.notifyObservers(browser, "EMEVideo:CDMMissing");
101     }
103     // Don't need to show UI if disabled.
104     if (!this.isUiEnabled()) {
105       return;
106     }
108     let notificationId;
109     let buttonCallback;
110     let supportPage;
111     // Notification message can be either a string or a DOM fragment.
112     let notificationMessage;
113     switch (status) {
114       case "available":
115       case "cdm-created":
116         // Only show the chain icon for proprietary CDMs. Clearkey is not one.
117         if (keySystem != "org.w3.clearkey") {
118           this.showPopupNotificationForSuccess(browser, keySystem);
119         }
120         // ... and bail!
121         return;
123       case "api-disabled":
124       case "cdm-disabled":
125         notificationId = "drmContentDisabled";
126         buttonCallback = () => {
127           this.ensureEMEEnabled(browser, keySystem);
128         };
129         notificationMessage = gNavigatorBundle.GetStringFromName(
130           "emeNotifications.drmContentDisabled.message2"
131         );
132         supportPage = "drm-content";
133         break;
135       case "cdm-not-installed":
136         notificationId = "drmContentCDMInstalling";
137         notificationMessage = this.getMessageWithBrandName(notificationId);
138         break;
140       case "cdm-not-supported":
141         // Not to pop up user-level notification because they cannot do anything
142         // about it.
143         return;
144       default:
145         Cu.reportError(
146           new Error(
147             "Unknown message ('" +
148               status +
149               "') dealing with EME key request: " +
150               aMessage.data
151           )
152         );
153         return;
154     }
156     // Now actually create the notification
158     let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
159     if (notificationBox.getNotificationWithValue(notificationId)) {
160       return;
161     }
163     let buttons = [];
164     if (supportPage) {
165       buttons.push({ supportPage });
166     }
167     if (buttonCallback) {
168       let msgPrefix = "emeNotifications." + notificationId + ".";
169       let manageLabelId = msgPrefix + "button.label";
170       let manageAccessKeyId = msgPrefix + "button.accesskey";
171       buttons.push({
172         label: gNavigatorBundle.GetStringFromName(manageLabelId),
173         accessKey: gNavigatorBundle.GetStringFromName(manageAccessKeyId),
174         callback: buttonCallback,
175       });
176     }
178     let iconURL = "chrome://browser/skin/drm-icon.svg";
179     notificationBox.appendNotification(
180       notificationMessage,
181       notificationId,
182       iconURL,
183       notificationBox.PRIORITY_INFO_HIGH,
184       buttons
185     );
186   }
188   async showPopupNotificationForSuccess(aBrowser) {
189     // We're playing EME content! Remove any "we can't play because..." messages.
190     let notificationBox = aBrowser.getTabBrowser().getNotificationBox(aBrowser);
191     ["drmContentDisabled", "drmContentCDMInstalling"].forEach(function(value) {
192       let notification = notificationBox.getNotificationWithValue(value);
193       if (notification) {
194         notificationBox.removeNotification(notification);
195       }
196     });
198     // Don't bother creating it if it's already there:
199     if (
200       aBrowser.ownerGlobal.PopupNotifications.getNotification(
201         "drmContentPlaying",
202         aBrowser
203       )
204     ) {
205       return;
206     }
208     let msgPrefix = "eme-notifications-drm-content-playing";
209     let msgId = msgPrefix;
210     let manageLabelId = msgPrefix + "-manage";
211     let manageAccessKeyId = msgPrefix + "-manage-accesskey";
212     let dismissLabelId = msgPrefix + "-dismiss";
213     let dismissAccessKeyId = msgPrefix + "-dismiss-accesskey";
215     let [
216       message,
217       manageLabel,
218       manageAccessKey,
219       dismissLabel,
220       dismissAccessKey,
221     ] = await gFluentStrings.formatValues([
222       msgId,
223       manageLabelId,
224       manageAccessKeyId,
225       dismissLabelId,
226       dismissAccessKeyId,
227     ]);
229     let anchorId = "eme-notification-icon";
230     let firstPlayPref = "browser.eme.ui.firstContentShown";
231     let document = aBrowser.ownerDocument;
232     if (
233       !Services.prefs.getPrefType(firstPlayPref) ||
234       !Services.prefs.getBoolPref(firstPlayPref)
235     ) {
236       document.getElementById(anchorId).setAttribute("firstplay", "true");
237       Services.prefs.setBoolPref(firstPlayPref, true);
238     } else {
239       document.getElementById(anchorId).removeAttribute("firstplay");
240     }
242     let mainAction = {
243       label: manageLabel,
244       accessKey: manageAccessKey,
245       callback() {
246         aBrowser.ownerGlobal.openPreferences("general-drm");
247       },
248       dismiss: true,
249       disableHighlight: gProtonDoorhangersEnabled,
250     };
252     let secondaryActions = [
253       {
254         label: dismissLabel,
255         accessKey: dismissAccessKey,
256         callback: () => {},
257         dismiss: true,
258       },
259     ];
261     let options = {
262       dismissed: true,
263       eventCallback: aTopic => aTopic == "swapping",
264       learnMoreURL:
265         Services.urlFormatter.formatURLPref("app.support.baseURL") +
266         "drm-content",
267       hideClose: true,
268     };
269     aBrowser.ownerGlobal.PopupNotifications.show(
270       aBrowser,
271       "drmContentPlaying",
272       message,
273       anchorId,
274       mainAction,
275       secondaryActions,
276       options
277     );
278   }