diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 13:17:38 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 13:17:38 +0200 |
commit | a1be17c1cea81ebb1e8b131a662c698d78f3f7f2 (patch) | |
tree | a92f7de513be600cc07bac458183e9af40e00c06 /application/basilisk/base/content/browser-media.js | |
parent | bf11fdd304898ac675e39b01b280d39550e419d0 (diff) | |
download | UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.gz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.lz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.xz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.zip |
Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk
Diffstat (limited to 'application/basilisk/base/content/browser-media.js')
-rw-r--r-- | application/basilisk/base/content/browser-media.js | 354 |
1 files changed, 354 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..bd5c5b227 --- /dev/null +++ b/application/basilisk/base/content/browser-media.js @@ -0,0 +1,354 @@ +/* -*- 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/. */ + +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: function(browser, keySystem) { + Services.prefs.setBoolPref("media.eme.enabled", true); + if (keySystem) { + if (keySystem.startsWith("com.adobe") && + Services.prefs.getPrefType("media.gmp-eme-adobe.enabled") && + !Services.prefs.getBoolPref("media.gmp-eme-adobe.enabled")) { + Services.prefs.setBoolPref("media.gmp-eme-adobe.enabled", true); + } else if (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: function(keySystem) { + if (!keySystem) { + return false; + } + if (keySystem.startsWith("com.adobe") && + Services.prefs.getPrefType("media.gmp-eme-adobe.visible")) { + return Services.prefs.getBoolPref("media.gmp-eme-adobe.visible"); + } + if (keySystem == "com.widevine.alpha" && + Services.prefs.getPrefType("media.gmp-widevinecdm.visible")) { + return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible"); + } + return true; + }, + getEMEDisabledFragment: function(msgId) { + let mainMessage = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.message"); + let [prefix, suffix] = mainMessage.split(/%(?:\$\d)?S/).map(s => document.createTextNode(s)); + let text = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.learnMoreLabel"); + let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); + let link = document.createElement("label"); + link.className = "text-link"; + link.setAttribute("href", baseURL + "drm-content"); + link.textContent = text; + + let fragment = document.createDocumentFragment(); + [prefix, link, suffix].forEach(n => fragment.appendChild(n)); + return fragment; + }, + getMessageWithBrandName: function(notificationId) { + let msgId = "emeNotifications." + notificationId + ".message"; + return gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]); + }, + receiveMessage: function({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-insufficient-version": + notificationId = "drmContentCDMInsufficientVersion"; + notificationMessage = this.getMessageWithBrandName(notificationId); + 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#chains-black"; + + box.appendNotification(notificationMessage, notificationId, iconURL, + box.PRIORITY_WARNING_MEDIUM, buttons); + }, + showPopupNotificationForSuccess: function(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: function() { 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) { + if (type == "adobe-cdm-not-found" && + AppConstants.platform == "win") { + return gNavigatorBundle.getString("decoder.noCodecs.message"); + } + if (type == "adobe-cdm-not-activated" && + AppConstants.platform == "win") { + return gNavigatorBundle.getString("decoder.noCodecs.message"); + } + 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"); + } + return ""; + }, + + getSumoForLearnHowButton(type) { + if (AppConstants.platform == "win") { + return "fix-video-audio-problems-firefox-windows"; + } + if (type == "cannot-initialize-pulseaudio") { + return "fix-common-audio-and-video-issues"; + } + 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. + // - '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(String.trim); + // Keep given formats that were not already recorded. + let newbies = formats.split(",").map(String.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); +}, false); |