diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 03:32:58 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-02 03:32:58 -0500 |
commit | e72ef92b5bdc43cd2584198e2e54e951b70299e8 (patch) | |
tree | 01ceb4a897c33eca9e7ccf2bc3aefbe530169fe5 /application/basilisk/base/content/browser-media.js | |
parent | 0d19b77d3eaa5b8d837bf52c19759e68e42a1c4c (diff) | |
download | UXP-e72ef92b5bdc43cd2584198e2e54e951b70299e8.tar UXP-e72ef92b5bdc43cd2584198e2e54e951b70299e8.tar.gz UXP-e72ef92b5bdc43cd2584198e2e54e951b70299e8.tar.lz UXP-e72ef92b5bdc43cd2584198e2e54e951b70299e8.tar.xz UXP-e72ef92b5bdc43cd2584198e2e54e951b70299e8.zip |
Add Basilisk
Diffstat (limited to 'application/basilisk/base/content/browser-media.js')
-rw-r--r-- | application/basilisk/base/content/browser-media.js | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/application/basilisk/base/content/browser-media.js b/application/basilisk/base/content/browser-media.js new file mode 100644 index 000000000..df76df9c0 --- /dev/null +++ b/application/basilisk/base/content/browser-media.js @@ -0,0 +1,351 @@ +/* -*- 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() { +#ifdef MOZ_EME + 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; +#else + return false; +#endif + }, + 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; + }, + getLearnMoreLink(msgId) { + let text = gNavigatorBundle.getString("emeNotifications." + msgId + ".learnMoreLabel"); + let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); + return "<label class='text-link' href='" + baseURL + "drm-content'>" + + text + "</label>"; + }, + 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; + let params = []; + 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) + params = [this.getLearnMoreLink(notificationId)]; + break; + + case "cdm-insufficient-version": + notificationId = "drmContentCDMInsufficientVersion"; + params = [this._brandShortName]; + break; + + case "cdm-not-installed": + notificationId = "drmContentCDMInstalling"; + params = [this._brandShortName]; + 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; + } + + this.showNotificationBar(browser, notificationId, keySystem, params, buttonCallback); + }, + showNotificationBar(browser, notificationId, keySystem, labelParams, callback) { + let box = gBrowser.getNotificationBox(browser); + if (box.getNotificationWithValue(notificationId)) { + return; + } + + let msgPrefix = "emeNotifications." + notificationId + "."; + let msgId = msgPrefix + "message"; + + let message = labelParams.length ? + gNavigatorBundle.getFormattedString(msgId, labelParams) : + gNavigatorBundle.getString(msgId); + + let buttons = []; + if (callback) { + let btnLabelId = msgPrefix + "button.label"; + let btnAccessKeyId = msgPrefix + "button.accesskey"; + buttons.push({ + label: gNavigatorBundle.getString(btnLabelId), + accessKey: gNavigatorBundle.getString(btnAccessKeyId), + callback + }); + } + + let iconURL = "chrome://browser/skin/drm-icon.svg#chains-black"; + + // Do a little dance to get rich content into the notification: + let fragment = document.createDocumentFragment(); + let descriptionContainer = document.createElement("description"); + descriptionContainer.innerHTML = message; + while (descriptionContainer.childNodes.length) { + fragment.appendChild(descriptionContainer.childNodes[0]); + } + + box.appendNotification(fragment, 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("paneContent"); }, + 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); + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener]) +}; + +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) { +#ifdef XP_WIN + if (type == "adobe-cdm-not-found" || type == "adobe-cdm-not-activated") { + return gNavigatorBundle.getString("decoder.noCodecs.message"); + } +#endif + +#ifndef XP_MACOSX + if (type == "platform-decoder-not-found") { +#ifdef XP_WIN + return gNavigatorBundle.getString("decoder.noHWAcceleration.message"); +#elif XP_LINUX + return gNavigatorBundle.getString("decoder.noCodecsLinux.message"); +#endif + } +#endif + if (type == "cannot-initialize-pulseaudio") { + return gNavigatorBundle.getString("decoder.noPulseAudio.message"); + } +#ifdef XP_LINUX + if (type == "unsupported-libavcodec") { + return gNavigatorBundle.getString("decoder.unsupportedLibavcodec.message"); + } +#endif + return ""; + }, + + getSumoForLearnHowButton(type) { +#ifdef XP_WIN + return "fix-video-audio-problems-firefox-windows"; +#else + if (type == "cannot-initialize-pulseaudio") { + return "fix-common-audio-and-video-issues"; + } + + return ""; +#endif + }, + + 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. + // - '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. + // - 'isSolved' is true when the notification actually indicates the + // resolution of that issue, to be reported as telemetry. + let {type, isSolved, decoderDoctorReportId, formats} = 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 now allowed.) + let formatsPref = "media.decoder-doctor." + decoderDoctorReportId + ".formats"; + let buttonClickedPref = "media.decoder-doctor." + decoderDoctorReportId + ".button-clicked"; + let histogram = + Services.telemetry.getKeyedHistogramById("DECODER_DOCTOR_INFOBAR_STATS"); + + let formatsInPref = Services.prefs.getPrefType(formatsPref) && + Services.prefs.getCharPref(formatsPref); + + if (!isSolved) { + if (!formats) { + Cu.reportError("Malformed Decoder Doctor unsolved message with no formats"); + return; + } + 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(", ")); + } + } + 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.getPrefType(buttonClickedPref) && + Services.prefs.getBoolPref(buttonClickedPref); + 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"); + openUILinkIn(baseURL + sumo, "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); +}); |