fune/browser/base/content/browser-media.js
Bryce Van Dyk 9951a3cb6f Bug 1431153 - Remove unused CDM version check path from browser-media.js r=Gijs
Bug 1314445 removed GMPService::GetPluginVersionForAPI and cleaned up code paths
around this that would notify users if the content decryption module (CDM) did
not satisfy version requirements. Some dead code appears to have been overlooked
and is removed by this patch.

Differential Revision: https://phabricator.services.mozilla.com/D3972

--HG--
extra : moz-landing-system : lando
2018-08-22 14:11:15 +00:00

375 lines
14 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* 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 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");
link.className = "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", {origin: "browserMedia"});
},
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;
const TELEMETRY_DDSTAT_CLICKED_FIRST = 3;
const TELEMETRY_DDSTAT_SOLVED = 4;
let gDecoderDoctorHandler = {
getLabelForNotificationBox(type) {
if (type == "platform-decoder-not-found") {
if (AppConstants.platform == "win") {
return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
}
if (AppConstants.platform == "linux") {
return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
}
}
if (type == "cannot-initialize-pulseaudio") {
return gNavigatorBundle.getString("decoder.noPulseAudio.message");
}
if (type == "unsupported-libavcodec" &&
AppConstants.platform == "linux") {
return gNavigatorBundle.getString("decoder.unsupportedLibavcodec.message");
}
if (type == "decode-error") {
return gNavigatorBundle.getString("decoder.decodeError.message");
}
if (type == "decode-warning") {
return gNavigatorBundle.getString("decoder.decodeWarning.message");
}
return "";
},
getSumoForLearnHowButton(type) {
if (type == "platform-decoder-not-found" &&
AppConstants.platform == "win") {
return "fix-video-audio-problems-firefox-windows";
}
if (type == "cannot-initialize-pulseaudio") {
return "fix-common-audio-and-video-issues";
}
return "";
},
getEndpointForReportIssueButton(type) {
if (type == "decode-error" || type == "decode-warning") {
return Services.prefs.getStringPref("media.decoder-doctor.new-issue-endpoint", "");
}
return "";
},
receiveMessage({target: browser, data: data}) {
let box = gBrowser.getNotificationBox(browser);
let notificationId = "decoder-doctor-notification";
if (box.getNotificationWithValue(notificationId)) {
return;
}
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (ex) {
Cu.reportError("Malformed Decoder Doctor message with data: " + data);
return;
}
// parsedData (the result of parsing the incoming 'data' json string)
// contains analysis information from Decoder Doctor:
// - 'type' is the type of issue, it determines which text to show in the
// infobar.
// - 'isSolved' is true when the notification actually indicates the
// resolution of that issue, to be reported as telemetry.
// - 'decoderDoctorReportId' is the Decoder Doctor issue identifier, to be
// used here as key for the telemetry (counting infobar displays,
// "Learn how" buttons clicks, and resolutions) and for the prefs used
// to store at-issue formats.
// - 'formats' contains a comma-separated list of formats (or key systems)
// that suffer the issue. These are kept in a pref, which the backend
// uses to later find when an issue is resolved.
// - 'decodeIssue' is a description of the decode error/warning.
// - 'resourceURL' is the resource with the issue.
let {type, isSolved, decoderDoctorReportId,
formats, decodeIssue, docURL, resourceURL} = parsedData;
type = type.toLowerCase();
// Error out early on invalid ReportId
if (!(/^\w+$/mi).test(decoderDoctorReportId)) {
return;
}
let title = gDecoderDoctorHandler.getLabelForNotificationBox(type);
if (!title) {
return;
}
// We keep the list of formats in prefs for the sake of the decoder itself,
// which reads it to determine when issues get solved for these formats.
// (Writing prefs from e10s content is not allowed.)
let formatsPref = formats &&
"media.decoder-doctor." + decoderDoctorReportId + ".formats";
let buttonClickedPref = "media.decoder-doctor." + decoderDoctorReportId + ".button-clicked";
let histogram =
Services.telemetry.getKeyedHistogramById("DECODER_DOCTOR_INFOBAR_STATS");
let formatsInPref = formats &&
Services.prefs.getCharPref(formatsPref, "");
if (!isSolved) {
if (formats) {
if (!formatsInPref) {
Services.prefs.setCharPref(formatsPref, formats);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN_FIRST);
} else {
// Split existing formats into an array of strings.
let existing = formatsInPref.split(",").map(x => x.trim());
// Keep given formats that were not already recorded.
let newbies = formats.split(",").map(x => x.trim())
.filter(x => !existing.includes(x));
// And rewrite pref with the added new formats (if any).
if (newbies.length) {
Services.prefs.setCharPref(formatsPref,
existing.concat(newbies).join(", "));
}
}
} else if (!decodeIssue) {
Cu.reportError("Malformed Decoder Doctor unsolved message with no formats nor decode issue");
return;
}
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN);
let buttons = [];
let sumo = gDecoderDoctorHandler.getSumoForLearnHowButton(type);
if (sumo) {
buttons.push({
label: gNavigatorBundle.getString("decoder.noCodecs.button"),
accessKey: gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
callback() {
let clickedInPref =
Services.prefs.getBoolPref(buttonClickedPref, false);
if (!clickedInPref) {
Services.prefs.setBoolPref(buttonClickedPref, true);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED_FIRST);
}
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED);
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
openTrustedLinkIn(baseURL + sumo, "tab");
}
});
}
let endpoint = gDecoderDoctorHandler.getEndpointForReportIssueButton(type);
if (endpoint) {
buttons.push({
label: gNavigatorBundle.getString("decoder.decodeError.button"),
accessKey: gNavigatorBundle.getString("decoder.decodeError.accesskey"),
callback() {
let clickedInPref =
Services.prefs.getBoolPref(buttonClickedPref, false);
if (!clickedInPref) {
Services.prefs.setBoolPref(buttonClickedPref, true);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED_FIRST);
}
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED);
let params = new URLSearchParams;
params.append("url", docURL);
params.append("label", "type-media");
params.append("problem_type", "video_bug");
params.append("src", "media-decode-error");
let details = {"Technical Information:": decodeIssue};
if (resourceURL) {
details["Resource:"] = resourceURL;
}
params.append("details", JSON.stringify(details));
openTrustedLinkIn(endpoint + "?" + params.toString(), "tab");
}
});
}
box.appendNotification(
title,
notificationId,
"", // This uses the info icon as specified below.
box.PRIORITY_INFO_LOW,
buttons
);
} else if (formatsInPref) {
// Issue is solved, and prefs haven't been cleared yet, meaning it's the
// first time we get this resolution -> Clear prefs and report telemetry.
Services.prefs.clearUserPref(formatsPref);
Services.prefs.clearUserPref(buttonClickedPref);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SOLVED);
}
},
};
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);
});