forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			272 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 | |
| /* 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/. */
 | |
| 
 | |
| import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
 | |
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineLazyGetter(lazy, "gNavigatorBundle", function () {
 | |
|   return Services.strings.createBundle(
 | |
|     "chrome://browser/locale/browser.properties"
 | |
|   );
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(
 | |
|   lazy,
 | |
|   "DEBUG_LOG",
 | |
|   "media.decoder-doctor.testing",
 | |
|   false
 | |
| );
 | |
| 
 | |
| function LOG_DD(message) {
 | |
|   if (lazy.DEBUG_LOG) {
 | |
|     dump("[DecoderDoctorParent] " + message + "\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| export class DecoderDoctorParent extends JSWindowActorParent {
 | |
|   getLabelForNotificationBox({ type, decoderDoctorReportId }) {
 | |
|     if (type == "platform-decoder-not-found") {
 | |
|       if (decoderDoctorReportId == "MediaWMFNeeded") {
 | |
|         return lazy.gNavigatorBundle.GetStringFromName(
 | |
|           "decoder.noHWAcceleration.message"
 | |
|         );
 | |
|       }
 | |
|       // Although this name seems generic, this is actually for not being able
 | |
|       // to find libavcodec on Linux.
 | |
|       if (decoderDoctorReportId == "MediaPlatformDecoderNotFound") {
 | |
|         return lazy.gNavigatorBundle.GetStringFromName(
 | |
|           "decoder.noCodecsLinux.message"
 | |
|         );
 | |
|       }
 | |
|     }
 | |
|     if (type == "cannot-initialize-pulseaudio") {
 | |
|       return lazy.gNavigatorBundle.GetStringFromName(
 | |
|         "decoder.noPulseAudio.message"
 | |
|       );
 | |
|     }
 | |
|     if (type == "unsupported-libavcodec" && AppConstants.platform == "linux") {
 | |
|       return lazy.gNavigatorBundle.GetStringFromName(
 | |
|         "decoder.unsupportedLibavcodec.message"
 | |
|       );
 | |
|     }
 | |
|     if (type == "decode-error") {
 | |
|       return lazy.gNavigatorBundle.GetStringFromName(
 | |
|         "decoder.decodeError.message"
 | |
|       );
 | |
|     }
 | |
|     if (type == "decode-warning") {
 | |
|       return lazy.gNavigatorBundle.GetStringFromName(
 | |
|         "decoder.decodeWarning.message"
 | |
|       );
 | |
|     }
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   getSumoForLearnHowButton({ type, decoderDoctorReportId }) {
 | |
|     if (
 | |
|       type == "platform-decoder-not-found" &&
 | |
|       decoderDoctorReportId == "MediaWMFNeeded"
 | |
|     ) {
 | |
|       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(aMessage) {
 | |
|     // The top level browsing context's embedding element should be a xul browser element.
 | |
|     let browser = this.browsingContext.top.embedderElement;
 | |
|     // The xul browser is owned by a window.
 | |
|     let window = browser?.ownerGlobal;
 | |
| 
 | |
|     if (!browser || !window) {
 | |
|       // We don't have a browser or window so bail!
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let box = browser.getTabBrowser().getNotificationBox(browser);
 | |
|     let notificationId = "decoder-doctor-notification";
 | |
|     if (box.getNotificationWithValue(notificationId)) {
 | |
|       // We already have a notification showing, bail.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let parsedData;
 | |
|     try {
 | |
|       parsedData = JSON.parse(aMessage.data);
 | |
|     } catch (ex) {
 | |
|       console.error(
 | |
|         "Malformed Decoder Doctor message with data: ",
 | |
|         aMessage.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+$/im.test(decoderDoctorReportId)) {
 | |
|       return;
 | |
|     }
 | |
|     LOG_DD(
 | |
|       `type=${type}, isSolved=${isSolved}, ` +
 | |
|         `decoderDoctorReportId=${decoderDoctorReportId}, formats=${formats}, ` +
 | |
|         `decodeIssue=${decodeIssue}, docURL=${docURL}, ` +
 | |
|         `resourceURL=${resourceURL}`
 | |
|     );
 | |
|     let title = this.getLabelForNotificationBox({
 | |
|       type,
 | |
|       decoderDoctorReportId,
 | |
|     });
 | |
|     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 formatsInPref = formats && Services.prefs.getCharPref(formatsPref, "");
 | |
| 
 | |
|     if (!isSolved) {
 | |
|       if (formats) {
 | |
|         if (!formatsInPref) {
 | |
|           Services.prefs.setCharPref(formatsPref, formats);
 | |
|         } 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) {
 | |
|         console.error(
 | |
|           "Malformed Decoder Doctor unsolved message with no formats nor decode issue"
 | |
|         );
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       let buttons = [];
 | |
|       let sumo = this.getSumoForLearnHowButton({ type, decoderDoctorReportId });
 | |
|       if (sumo) {
 | |
|         LOG_DD(`sumo=${sumo}`);
 | |
|         buttons.push({
 | |
|           label: lazy.gNavigatorBundle.GetStringFromName(
 | |
|             "decoder.noCodecs.button"
 | |
|           ),
 | |
|           supportPage: sumo,
 | |
|           callback() {
 | |
|             let clickedInPref = Services.prefs.getBoolPref(
 | |
|               buttonClickedPref,
 | |
|               false
 | |
|             );
 | |
|             if (!clickedInPref) {
 | |
|               Services.prefs.setBoolPref(buttonClickedPref, true);
 | |
|             }
 | |
|           },
 | |
|         });
 | |
|       }
 | |
|       let endpoint = this.getEndpointForReportIssueButton(type);
 | |
|       if (endpoint) {
 | |
|         LOG_DD(`endpoint=${endpoint}`);
 | |
|         buttons.push({
 | |
|           label: lazy.gNavigatorBundle.GetStringFromName(
 | |
|             "decoder.decodeError.button"
 | |
|           ),
 | |
|           accessKey: lazy.gNavigatorBundle.GetStringFromName(
 | |
|             "decoder.decodeError.accesskey"
 | |
|           ),
 | |
|           callback() {
 | |
|             let clickedInPref = Services.prefs.getBoolPref(
 | |
|               buttonClickedPref,
 | |
|               false
 | |
|             );
 | |
|             if (!clickedInPref) {
 | |
|               Services.prefs.setBoolPref(buttonClickedPref, true);
 | |
|             }
 | |
| 
 | |
|             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));
 | |
|             window.openTrustedLinkIn(endpoint + "?" + params.toString(), "tab");
 | |
|           },
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       box.appendNotification(
 | |
|         notificationId,
 | |
|         {
 | |
|           label: title,
 | |
|           image: "", // This uses the info icon as specified below.
 | |
|           priority: 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);
 | |
|     }
 | |
|   }
 | |
| }
 | 
