Bug 1160424 - [Decoder Doctor] Show notification UI when a requested codec is missing but downloadable. r?gijs draft
authorJared Wein <jwein@mozilla.com>
Tue, 19 Apr 2016 17:48:02 -0400 (2016-04-19)
changeset 353783 de4f5c7357a18f746f8babdba7bd886130205c30
parent 350393 4c690a8ab43db8d8a0b4e65a4f67f79556d6a00b
child 518877 e346079d73a725649b13ce65bd0d17308761fef6
push id15942
push userjwein@mozilla.com
push dateTue, 19 Apr 2016 22:02:05 +0000 (2016-04-19)
reviewersgijs
bugs1160424
milestone48.0a1
Bug 1160424 - [Decoder Doctor] Show notification UI when a requested codec is missing but downloadable. r?gijs MozReview-Commit-ID: BHPpLM96cg3
browser/base/content/browser-eme.js
browser/base/content/browser-media.js
browser/base/content/global-scripts.inc
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_decoderDoctor.js
browser/base/jar.mn
browser/modules/ContentObservers.jsm
testing/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
rename from browser/base/content/browser-eme.js
rename to browser/base/content/browser-media.js
--- a/browser/base/content/browser-eme.js
+++ b/browser/base/content/browser-media.js
@@ -172,12 +172,95 @@ var gEMEHandler = {
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener])
 };
 
 XPCOMUtils.defineLazyGetter(gEMEHandler, "_brandShortName", function() {
   return document.getElementById("bundle_brand").getString("brandShortName");
 });
 
+var gDecoderDoctorHandler = {
+  shouldShowNotification(type) {
+    return type == "adobe-cdm-not-found" ||
+           type == "adobe-cdm-not-activated" ||
+           type == "platform-decoder-not-found";
+  },
+
+  shouldShowLearnMoreButton() {
+    return AppConstants.platform != "linux";
+  },
+
+  getLabelForNotificationBox(type) {
+    if (type == "adobe-cdm-not-found" &&
+        AppConstants.platform == "win") {
+      if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+        // We supply our own Learn More link so we don't need to populate the message here.
+        return gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
+      }
+      return gNavigatorBundle.getString("decoder.noCodecs.message");
+    }
+    if (type == "adobe-cdm-not-activated" &&
+        AppConstants.platform == "win") {
+      if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+        return gNavigatorBundle.getString("decoder.noCodecsXP.message");
+      }
+      return gNavigatorBundle.getString("decoder.noCodecs.message");
+    }
+    if (type == "platform-decoder-not-found") {
+      if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) {
+        return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
+      }
+      if (AppConstants.platform == "linux") {
+        return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
+      }
+    }
+    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;
+    }
+    let {type: type} = parsedData;
+    type = type.toLowerCase();
+    if (!gDecoderDoctorHandler.shouldShowNotification(type)) {
+      return;
+    }
+
+    let title = gDecoderDoctorHandler.getLabelForNotificationBox(type);
+    let buttons = [];
+    if (gDecoderDoctorHandler.shouldShowLearnMoreButton()) {
+      buttons.push({
+        label: gNavigatorBundle.getString("decoder.noCodecs.button"),
+        accessKey: gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
+        callback() {
+          let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+          openUILinkIn(baseURL + "fix-video-audio-problems-firefox-windows", "tab");
+        }
+      });
+    }
+
+    box.appendNotification(
+      title,
+      notificationId,
+      "", // This uses the info icon as specified below.
+      box.PRIORITY_INFO_LOW,
+      buttons
+    );
+  },
+}
+
+window.messageManager.addMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
 window.messageManager.addMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
 window.addEventListener("unload", function() {
   window.messageManager.removeMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
+  window.messageManager.removeMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
 }, false);
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -10,21 +10,21 @@
 <script type="application/javascript" src="chrome://browser/content/customizableui/panelUI.js"/>
 <script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
 <script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
 
 <script type="application/javascript" src="chrome://browser/content/browser-addons.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-ctrlTab.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-customization.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-devedition.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-eme.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullScreen.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser-media.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/>
 #ifdef MOZ_SAFE_BROWSING
 <script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
 #endif
 <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -311,16 +311,18 @@ skip-if = os == 'win'
 [browser_contextmenu.js]
 tags = fullscreen
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_contextmenu_input.js]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_ctrlTab.js]
 [browser_datachoices_notification.js]
 skip-if = !datareporting
+[browser_decoderDoctor.js]
+skip-if = os == "mac" # decoder doctor isn't implemented on osx
 [browser_devedition.js]
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_about_urls.js]
 skip-if = e10s && debug
 [browser_devices_get_user_media_in_frame.js]
 [browser_discovery.js]
 [browser_double_close_tab.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_decoderDoctor.js
@@ -0,0 +1,94 @@
+"use strict";
+
+function* test_decoder_doctor_notification(type, notificationMessage, options) {
+  yield BrowserTestUtils.withNewTab({ gBrowser }, function*(browser) {
+    let awaitNotificationBar =
+      BrowserTestUtils.waitForNotificationBar(gBrowser, browser, "decoder-doctor-notification");
+
+    yield ContentTask.spawn(browser, type, function*(type) {
+      Services.obs.notifyObservers(content.window,
+                                   "decoder-doctor-notification",
+                                   JSON.stringify({type: type}));
+    });
+
+    let notification;
+    try {
+      notification = yield awaitNotificationBar;
+    } catch (ex) {
+      ok(false, ex);
+      return;
+    }
+    ok(notification, "Got decoder-doctor-notification notification");
+
+    is(notification.getAttribute("label"), notificationMessage,
+      "notification message should match expectation");
+    let button = notification.childNodes[0];
+    if (options && options.noLearnMoreButton) {
+      ok(!button, "There should not be a Learn More button");
+      return;
+    }
+
+    is(button.getAttribute("label"), gNavigatorBundle.getString("decoder.noCodecs.button"),
+      "notification button should be 'Learn more'");
+    is(button.getAttribute("accesskey"), gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
+      "notification button should have accesskey");
+
+    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+    let url = baseURL + "fix-video-audio-problems-firefox-windows";
+    let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url);
+    button.click();
+    let sumoTab = yield awaitNewTab;
+    yield BrowserTestUtils.removeTab(sumoTab);
+  });
+}
+
+add_task(function* test_adobe_cdm_not_found() {
+  // This is only sent on Windows.
+  if (AppConstants.platform != "win") {
+    return;
+  }
+
+  let message;
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    message = gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
+  } else {
+    message = gNavigatorBundle.getString("decoder.noCodecs.message");
+  }
+
+  yield test_decoder_doctor_notification("adobe-cdm-not-found", message);
+});
+
+add_task(function* test_adobe_cdm_not_activated() {
+  // This is only sent on Windows.
+  if (AppConstants.platform != "win") {
+    return;
+  }
+
+  let message;
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    message = gNavigatorBundle.getString("decoder.noCodecsXP.message");
+  } else {
+    message = gNavigatorBundle.getString("decoder.noCodecs.message");
+  }
+
+  yield test_decoder_doctor_notification("adobe-cdm-not-activated", message);
+});
+
+add_task(function* test_platform_decoder_not_found() {
+  // Not sent on Windows XP.
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    return;
+  }
+
+  let message;
+  let isLinux = AppConstants.platform == "linux";
+  if (isLinux) {
+    message = gNavigatorBundle.getString("decoder.noCodecsLinux.message");
+  } else {
+    message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
+  }
+
+  yield test_decoder_doctor_notification("platform-decoder-not-found",
+                                         message,
+                                         {noLearnMoreButton: isLinux});
+});
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -74,22 +74,22 @@ browser.jar:
 *       content/browser/browser.css                   (content/browser.css)
         content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
         content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
         content/browser/browser-devedition.js         (content/browser-devedition.js)
-        content/browser/browser-eme.js                (content/browser-eme.js)
         content/browser/browser-feeds.js              (content/browser-feeds.js)
         content/browser/browser-fullScreen.js         (content/browser-fullScreen.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-fxaccounts.js         (content/browser-fxaccounts.js)
         content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
+        content/browser/browser-media.js              (content/browser-media.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
         content/browser/browser-refreshblocker.js     (content/browser-refreshblocker.js)
 #ifdef MOZ_SAFE_BROWSING
         content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
 #endif
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-social.js             (content/browser-social.js)
--- a/browser/modules/ContentObservers.jsm
+++ b/browser/modules/ContentObservers.jsm
@@ -22,16 +22,24 @@ Cu.import("resource://gre/modules/Servic
 var gEMEUIObserver = function(subject, topic, data) {
   let win = subject.top;
   let mm = getMessageManagerForWindow(win);
   if (mm) {
     mm.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", data);
   }
 };
 
+var gDecoderDoctorObserver = function(subject, topic, data) {
+  let win = subject.top;
+  let mm = getMessageManagerForWindow(win);
+  if (mm) {
+    mm.sendAsyncMessage("DecoderDoctor:Notification", data);
+  }
+};
+
 function getMessageManagerForWindow(aContentWindow) {
   let ir = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDocShell)
                          .sameTypeRootTreeItem
                          .QueryInterface(Ci.nsIInterfaceRequestor);
   try {
     // If e10s is disabled, this throws NS_NOINTERFACE for closed tabs.
     return ir.getInterface(Ci.nsIContentFrameMessageManager);
@@ -39,8 +47,9 @@ function getMessageManagerForWindow(aCon
     if (e.result == Cr.NS_NOINTERFACE) {
       return null;
     }
     throw e;
   }
 }
 
 Services.obs.addObserver(gEMEUIObserver, "mediakeys-request", false);
+Services.obs.addObserver(gDecoderDoctorObserver, "decoder-doctor-notification", false);
--- a/testing/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
+++ b/testing/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
@@ -29,21 +29,21 @@ const SCRIPTS = [
   "browser/components/downloads/content/indicator.js",
   "browser/components/customizableui/content/panelUI.js",
   "toolkit/obsolete/content/inlineSpellCheckUI.js",
   "toolkit/components/viewsource/content/viewSourceUtils.js",
   "browser/base/content/browser-addons.js",
   "browser/base/content/browser-ctrlTab.js",
   "browser/base/content/browser-customization.js",
   "browser/base/content/browser-devedition.js",
-  "browser/base/content/browser-eme.js",
   "browser/base/content/browser-feeds.js",
   "browser/base/content/browser-fullScreen.js",
   "browser/base/content/browser-fullZoom.js",
   "browser/base/content/browser-gestureSupport.js",
+  "browser/base/content/browser-media.js",
   "browser/base/content/browser-places.js",
   "browser/base/content/browser-plugins.js",
   "browser/base/content/browser-refreshblocker.js",
   "browser/base/content/browser-safebrowsing.js",
   "browser/base/content/browser-sidebar.js",
   "browser/base/content/browser-social.js",
   "browser/base/content/browser-syncui.js",
   "browser/base/content/browser-tabsintitlebar.js",