Bug 1642465 - Add EncryptedMedia actors to handle EME messages. r=Gijs

This patch also removes the code that is replaced by the new actors.

Differential Revision: https://phabricator.services.mozilla.com/D77861
This commit is contained in:
Bryce Seager van Dyk 2020-06-29 21:56:25 +00:00
Родитель 897e56554c
Коммит 12c7838399
6 изменённых файлов: 284 добавлений и 220 удалений

Просмотреть файл

@ -0,0 +1,17 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["EncryptedMediaChild"];
class EncryptedMediaChild extends JSWindowActorChild {
// Expected to observe 'mediakeys-request' as notified from MediaKeySystemAccess.
// @param aSubject the nsPIDOMWindowInner associated with the notifying MediaKeySystemAccess.
// @param aTopic should be "mediakeys-request".
// @param aData json containing a `status` and a `keysystem`.
observe(aSubject, aTopic, aData) {
this.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", aData);
}
}

Просмотреть файл

@ -0,0 +1,252 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["EncryptedMediaParent"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm"
);
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
});
XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
return Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
});
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;
}
getEMEDisabledFragment(aBrowser) {
let mainMessage = gNavigatorBundle.GetStringFromName(
"emeNotifications.drmContentDisabled.message"
);
let text = gNavigatorBundle.GetStringFromName(
"emeNotifications.drmContentDisabled.learnMoreLabel"
);
let document = aBrowser.ownerDocument;
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
let link = document.createXULElement("label", { is: "text-link" });
link.setAttribute("href", baseURL + "drm-content");
link.textContent = text;
return BrowserUtils.getLocalizedFragment(document, mainMessage, link);
}
getMessageWithBrandName(aNotificationId) {
let msgId = "emeNotifications." + aNotificationId + ".message";
return gNavigatorBundle.formatStringFromName(msgId, [
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;
}
if (browser.outerBrowser) {
// Responsive design mode check
browser = browser.outerBrowser;
}
let parsedData;
try {
parsedData = JSON.parse(aMessage.data);
} catch (ex) {
Cu.reportError("Malformed EME video message with data: " + aMessage.data);
return;
}
let { status, keySystem } = parsedData;
// Don't need to show if disabled or keysystem not visible.
if (!this.isUiEnabled() || !this.isKeySystemVisible(keySystem)) {
return;
}
let notificationId;
let buttonCallback;
// 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 = this.getEMEDisabledFragment(browser);
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:
Cu.reportError(
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 (buttonCallback) {
let msgPrefix = "emeNotifications." + notificationId + ".";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
buttons.push({
label: gNavigatorBundle.GetStringFromName(btnLabelId),
accessKey: gNavigatorBundle.GetStringFromName(btnAccessKeyId),
callback: buttonCallback,
});
}
let iconURL = "chrome://browser/skin/drm-icon.svg";
notificationBox.appendNotification(
notificationMessage,
notificationId,
iconURL,
notificationBox.PRIORITY_WARNING_MEDIUM,
buttons
);
}
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 = "emeNotifications.drmContentPlaying.";
let msgId = msgPrefix + "message2";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
let message = gNavigatorBundle.formatStringFromName(msgId, [
gBrandBundle.GetStringFromName("brandShortName"),
]);
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: gNavigatorBundle.GetStringFromName(btnLabelId),
accessKey: gNavigatorBundle.GetStringFromName(btnAccessKeyId),
callback() {
aBrowser.ownerGlobal.openPreferences("general-drm");
},
dismiss: true,
};
let options = {
dismissed: true,
eventCallback: aTopic => aTopic == "swapping",
learnMoreURL:
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"drm-content",
};
aBrowser.ownerGlobal.PopupNotifications.show(
aBrowser,
"drmContentPlaying",
message,
anchorId,
mainAction,
null,
options
);
}
}

Просмотреть файл

@ -56,6 +56,8 @@ FINAL_TARGET_FILES.actors += [
'ContextMenuParent.jsm',
'DOMFullscreenChild.jsm',
'DOMFullscreenParent.jsm',
'EncryptedMediaChild.jsm',
'EncryptedMediaParent.jsm',
'FormValidationChild.jsm',
'FormValidationParent.jsm',
'LightweightThemeChild.jsm',

Просмотреть файл

@ -5,211 +5,6 @@
"use strict";
var gEMEHandler = {
get uiEnabled() {
let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
// Force-disable on WinXP:
if (navigator.platform.toLowerCase().startsWith("win")) {
emeUIEnabled =
emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
}
return emeUIEnabled;
},
ensureEMEEnabled(browser, keySystem) {
Services.prefs.setBoolPref("media.eme.enabled", true);
if (
keySystem &&
keySystem == "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);
}
browser.reload();
},
isKeySystemVisible(keySystem) {
if (!keySystem) {
return false;
}
if (
keySystem == "com.widevine.alpha" &&
Services.prefs.getPrefType("media.gmp-widevinecdm.visible")
) {
return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
}
return true;
},
getEMEDisabledFragment(msgId) {
let mainMessage = gNavigatorBundle.getString(
"emeNotifications.drmContentDisabled.message"
);
let text = gNavigatorBundle.getString(
"emeNotifications.drmContentDisabled.learnMoreLabel"
);
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
let link = document.createXULElement("label", { is: "text-link" });
link.setAttribute("href", baseURL + "drm-content");
link.textContent = text;
return BrowserUtils.getLocalizedFragment(document, mainMessage, link);
},
getMessageWithBrandName(notificationId) {
let msgId = "emeNotifications." + notificationId + ".message";
return gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]);
},
receiveMessage({ target: browser, data: data }) {
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (ex) {
Cu.reportError("Malformed EME video message with data: " + data);
return;
}
let { status: status, keySystem: keySystem } = parsedData;
// Don't need to show if disabled or keysystem not visible.
if (!this.uiEnabled || !this.isKeySystemVisible(keySystem)) {
return;
}
let notificationId;
let buttonCallback;
// 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 = gEMEHandler.ensureEMEEnabled.bind(
gEMEHandler,
browser,
keySystem
);
notificationMessage = this.getEMEDisabledFragment();
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:
Cu.reportError(
new Error(
"Unknown message ('" +
status +
"') dealing with EME key request: " +
data
)
);
return;
}
// Now actually create the notification
let box = gBrowser.getNotificationBox(browser);
if (box.getNotificationWithValue(notificationId)) {
return;
}
let buttons = [];
if (buttonCallback) {
let msgPrefix = "emeNotifications." + notificationId + ".";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
buttons.push({
label: gNavigatorBundle.getString(btnLabelId),
accessKey: gNavigatorBundle.getString(btnAccessKeyId),
callback: buttonCallback,
});
}
let iconURL = "chrome://browser/skin/drm-icon.svg";
box.appendNotification(
notificationMessage,
notificationId,
iconURL,
box.PRIORITY_WARNING_MEDIUM,
buttons
);
},
showPopupNotificationForSuccess(browser, keySystem) {
// We're playing EME content! Remove any "we can't play because..." messages.
var box = gBrowser.getNotificationBox(browser);
["drmContentDisabled", "drmContentCDMInstalling"].forEach(function(value) {
var notification = box.getNotificationWithValue(value);
if (notification) {
box.removeNotification(notification);
}
});
// Don't bother creating it if it's already there:
if (PopupNotifications.getNotification("drmContentPlaying", browser)) {
return;
}
let msgPrefix = "emeNotifications.drmContentPlaying.";
let msgId = msgPrefix + "message2";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
let message = gNavigatorBundle.getFormattedString(msgId, [
this._brandShortName,
]);
let anchorId = "eme-notification-icon";
let firstPlayPref = "browser.eme.ui.firstContentShown";
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: gNavigatorBundle.getString(btnLabelId),
accessKey: gNavigatorBundle.getString(btnAccessKeyId),
callback() {
openPreferences("general-drm");
},
dismiss: true,
};
let options = {
dismissed: true,
eventCallback: aTopic => aTopic == "swapping",
learnMoreURL:
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"drm-content",
};
PopupNotifications.show(
browser,
"drmContentPlaying",
message,
anchorId,
mainAction,
null,
options
);
},
};
XPCOMUtils.defineLazyGetter(gEMEHandler, "_brandShortName", function() {
return document.getElementById("bundle_brand").getString("brandShortName");
});
const TELEMETRY_DDSTAT_SHOWN = 0;
const TELEMETRY_DDSTAT_SHOWN_FIRST = 1;
const TELEMETRY_DDSTAT_CLICKED = 2;
@ -426,13 +221,7 @@ let gDecoderDoctorHandler = {
window
.getGroupMessageManager("browsers")
.addMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
window
.getGroupMessageManager("browsers")
.addMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
window.addEventListener("unload", function() {
window
.getGroupMessageManager("browsers")
.removeMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
window
.getGroupMessageManager("browsers")
.removeMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);

Просмотреть файл

@ -352,6 +352,19 @@ let JSWINDOWACTORS = {
allFrames: true,
},
EncryptedMedia: {
parent: {
moduleURI: "resource:///actors/EncryptedMediaParent.jsm",
},
child: {
moduleURI: "resource:///actors/EncryptedMediaChild.jsm",
observers: ["mediakeys-request"],
},
allFrames: true,
},
FormValidation: {
parent: {
moduleURI: "resource:///actors/FormValidationParent.jsm",

Просмотреть файл

@ -28,14 +28,6 @@ ChromeUtils.defineModuleGetter(
"resource:///modules/AboutNewTabService.jsm"
);
var gEMEUIObserver = function(subject, topic, data) {
let win = subject.top;
let mm = getMessageManagerForWindow(win);
if (mm) {
mm.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", data);
}
};
var gDecoderDoctorObserver = function(subject, topic, data) {
let win = subject.top;
let mm = getMessageManagerForWindow(win);
@ -48,7 +40,6 @@ function getMessageManagerForWindow(aContentWindow) {
return aContentWindow.docShell.messageManager;
}
Services.obs.addObserver(gEMEUIObserver, "mediakeys-request");
Services.obs.addObserver(gDecoderDoctorObserver, "decoder-doctor-notification");
// WebRTCChild observer registration. Actor observers require the subject