Bug 1343442 - Handle decode-error/warning in browser-media.js - r?gijs draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 11 Apr 2017 13:37:52 +1200
changeset 560839 a74e566ff286a51725d84af89cfadb41ef4be9b7
parent 560838 dc902fcb84d60d3c882c0ab9a43d5d2158902c44
child 560840 a4dded63672b7c3a1b4887831f9d4080d8d6b1fc
push id53551
push usergsquelart@mozilla.com
push dateTue, 11 Apr 2017 23:47:44 +0000
reviewersgijs
bugs1343442
milestone55.0a1
Bug 1343442 - Handle decode-error/warning in browser-media.js - r?gijs Open webcompat.com in new tab, similar to what "Report Site Issue" button does. MozReview-Commit-ID: 1ESOY3upHgc
browser/base/content/browser-media.js
browser/locales/en-US/chrome/browser/browser.properties
modules/libpref/init/all.js
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -202,29 +202,43 @@ let gDecoderDoctorHandler = {
     }
     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 (AppConstants.platform == "win") {
+    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;
@@ -233,89 +247,119 @@ let gDecoderDoctorHandler = {
     } 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.
-    // - 'isSolved' is true when the notification actually indicates the
-    //   resolution of that issue, to be reported as telemetry.
-    let {type, isSolved, decoderDoctorReportId, formats} = parsedData;
+    // - '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 now allowed.)
-    let formatsPref = "media.decoder-doctor." + decoderDoctorReportId + ".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 = Services.prefs.getPrefType(formatsPref) &&
-                        Services.prefs.getCharPref(formatsPref);
+    let formatsInPref = formats &&
+                        Services.prefs.getCharPref(formatsPref, "");
 
     if (!isSolved) {
-      if (!formats) {
-        Cu.reportError("Malformed Decoder Doctor unsolved message with no formats");
+      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;
       }
-      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);
+            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");
             openUILinkIn(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("problem_type", "video_bug");
+            params.append("src", "media-decode-error");
+            params.append("details",
+                          "Technical Information:\n" + decodeIssue +
+                          (resourceURL ? ("\nResource: " + resourceURL) : ""));
+            openUILinkIn(endpoint + "?" + params.toString(), "tab");
+          }
+        });
+      }
 
       box.appendNotification(
           title,
           notificationId,
           "", // This uses the info icon as specified below.
           box.PRIORITY_INFO_LOW,
           buttons
       );
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -840,16 +840,21 @@ pendingCrashReports.alwaysSend = Always 
 decoder.noCodecs.button = Learn how
 decoder.noCodecs.accesskey = L
 decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
 decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
 decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
 decoder.noPulseAudio.message = To play audio, you may need to install the required PulseAudio software.
 decoder.unsupportedLibavcodec.message = libavcodec may be vulnerable or is not supported, and should be updated to play video.
 
+decoder.decodeError.message = An error occurred while decoding a media resource.
+decoder.decodeError.button = Report Site Issue
+decoder.decodeError.accesskey = R
+decoder.decodeWarning.message = A recoverable error occurred while decoding a media resource.
+
 # LOCALIZATION NOTE (captivePortal.infoMessage3):
 # Shown in a notification bar when we detect a captive portal is blocking network access
 # and requires the user to log in before browsing.
 captivePortal.infoMessage3 = You must log in to this network before you can access the Internet.
 # LOCALIZATION NOTE (captivePortal.showLoginPage2):
 # The label for a button shown in the info bar in all tabs except the login page tab.
 # The button shows the portal login page tab when clicked.
 captivePortal.showLoginPage2 = Open Network Login Page
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -389,16 +389,18 @@ pref("media.gmp.storage.version.expected
 pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMF,MediaCannotInitializePulseAudio,MediaCannotPlayNoDecoders,MediaUnsupportedLibavcodec,MediaDecodeError");
 #else
 pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMF,MediaCannotInitializePulseAudio,MediaCannotPlayNoDecoders,MediaUnsupportedLibavcodec");
 #endif
 // Whether we report partial failures.
 pref("media.decoder-doctor.verbose", false);
 // Whether DD should consider WMF-disabled a WMF failure, useful for testing.
 pref("media.decoder-doctor.wmf-disabled-is-failure", false);
+// URL to report decode issues
+pref("media.decoder-doctor.new-issue-endpoint", "https://webcompat.com/issues/new");
 
 // Whether to suspend decoding of videos in background tabs.
 #ifdef NIGHTLY_BUILD
 pref("media.suspend-bkgnd-video.enabled", true);
 #else
 pref("media.suspend-bkgnd-video.enabled", false);
 #endif
 // Delay, in ms, from time window goes to background to suspending