зеркало из https://github.com/mozilla/gecko-dev.git
273 строки
8.9 KiB
JavaScript
273 строки
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);
|
|
}
|
|
}
|
|
}
|