Bug 983984 - Default AudioChannel from a pref, r=ehsan, r=roc, r=mchen
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 20 Mar 2014 10:45:55 +0000
changeset 174432 bb274bf64633389f55a64a9ae6e3825e84041f75
parent 174431 9456eb36466e46e21568d20856680f87524878aa
child 174433 260154a65e5743465d6daf4ba72f4778475fea6d
push id41289
push useramarchesini@mozilla.com
push dateThu, 20 Mar 2014 10:46:25 +0000
treeherdermozilla-inbound@bb274bf64633 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, roc, mchen
bugs983984
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 983984 - Default AudioChannel from a pref, r=ehsan, r=roc, r=mchen
content/html/content/src/HTMLMediaElement.cpp
content/html/content/test/test_mozaudiochannel.html
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/test/test_mozaudiochannel.html
dom/audiochannel/AudioChannelCommon.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -457,17 +457,33 @@ NS_INTERFACE_MAP_END_INHERITING(nsGeneri
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(HTMLMediaElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLMediaElement, CrossOrigin, crossorigin)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Controls, controls)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Autoplay, autoplay)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Loop, loop)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, DefaultMuted, muted)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMediaElement, Preload, preload, nullptr)
-NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMediaElement, MozAudioChannelType, mozaudiochannel, "normal")
+
+NS_IMETHODIMP
+HTMLMediaElement::GetMozAudioChannelType(nsAString& aValue)
+{
+  nsString defaultValue;
+  AudioChannelService::GetDefaultAudioChannelString(defaultValue);
+
+  NS_ConvertUTF16toUTF8 str(defaultValue);
+  GetEnumAttr(nsGkAtoms::mozaudiochannel, str.get(), aValue);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HTMLMediaElement::SetMozAudioChannelType(const nsAString& aValue)
+{
+  return SetAttrHelper(nsGkAtoms::mozaudiochannel, aValue);
+}
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::GetMozSrcObject() const
 {
   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
                "MediaStream should have been set up properly");
   nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
   return stream.forget();
@@ -2339,29 +2355,38 @@ bool HTMLMediaElement::ParseAttribute(in
 
 bool HTMLMediaElement::CheckAudioChannelPermissions(const nsAString& aString)
 {
   if (!UseAudioChannelService()) {
     return true;
   }
 
   // Only normal channel doesn't need permission.
-  if (!aString.EqualsASCII("normal")) {
-    nsCOMPtr<nsIPermissionManager> permissionManager =
-      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-    if (!permissionManager) {
-      return false;
-    }
-
-    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
-    permissionManager->TestExactPermissionFromPrincipal(NodePrincipal(),
-      nsCString(NS_LITERAL_CSTRING("audio-channel-") + NS_ConvertUTF16toUTF8(aString)).get(), &perm);
-    if (perm != nsIPermissionManager::ALLOW_ACTION) {
-      return false;
-    }
+  if (aString.EqualsASCII("normal")) {
+    return true;
+  }
+
+  // Maybe this audio channel is equal to the default value from the pref.
+  nsString audioChannel;
+  AudioChannelService::GetDefaultAudioChannelString(audioChannel);
+  if (audioChannel.Equals(aString)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  if (!permissionManager) {
+    return false;
+  }
+
+  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+  permissionManager->TestExactPermissionFromPrincipal(NodePrincipal(),
+    nsCString(NS_LITERAL_CSTRING("audio-channel-") + NS_ConvertUTF16toUTF8(aString)).get(), &perm);
+  if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    return false;
   }
 
   return true;
 }
 
 void HTMLMediaElement::DoneCreatingElement()
 {
    if (HasAttr(kNameSpaceID_None, nsGkAtoms::muted)) {
@@ -3983,17 +4008,17 @@ HTMLMediaElement::MozAudioChannelType() 
 
     case AUDIO_CHANNEL_RINGER:
       return AudioChannel::Ringer;
 
     case AUDIO_CHANNEL_PUBLICNOTIFICATION:
       return AudioChannel::Publicnotification;
 
     default:
-      return AudioChannel::Normal;
+      return AudioChannelService::GetDefaultAudioChannel();
   }
 }
 
 void
 HTMLMediaElement::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
 {
   nsString channel;
   channel.AssignASCII(AudioChannelValues::strings[uint32_t(aValue)].value,
--- a/content/html/content/test/test_mozaudiochannel.html
+++ b/content/html/content/test/test_mozaudiochannel.html
@@ -12,42 +12,92 @@
   <audio id="audio1" />
   <audio id="audio2" mozaudiochannel="foo" />
   <audio id="audio3" mozaudiochannel="content" />
 </div>
 
 <pre id="test">
 <script type="application/javascript">
 
-var audio1 = document.getElementById("audio1");
-ok(audio1, "Audio Element exists");
-ok(audio1.mozAudioChannelType == "normal", "Default audio1 channel == 'normal'");
-try {
-audio1.mozAudioChannelType = "foo";
-} catch(e) {}
-ok(audio1.mozAudioChannelType == "normal", "Default audio1 channel == 'normal'");
-audio1.mozAudioChannelType = "alarm";
-ok(audio1.mozAudioChannelType == "alarm", "Default audio1 channel == 'alarm'");
+function test_basic() {
+  var audio1 = document.getElementById("audio1");
+  ok(audio1, "Audio Element exists");
+  is(audio1.mozAudioChannelType, "normal", "Default audio1 channel == 'normal'");
+  try {
+  audio1.mozAudioChannelType = "foo";
+  } catch(e) {}
+  is(audio1.mozAudioChannelType, "normal", "Default audio1 channel == 'normal'");
+  audio1.mozAudioChannelType = "alarm";
+  is(audio1.mozAudioChannelType, "alarm", "Default audio1 channel == 'alarm'");
+
+  var audio2 = document.getElementById("audio2");
+  ok(audio2, "Audio Element exists");
+  is(audio2.mozAudioChannelType, "normal", "Default audio2 channel == 'normal'");
+  try {
+  audio2.mozAudioChannelType = "foo";
+  } catch(e) {}
+  is(audio2.mozAudioChannelType, "normal", "Default audio2 channel == 'normal'");
+  audio2.mozAudioChannelType = "alarm";
+  is(audio2.mozAudioChannelType, "alarm", "Default audio2 channel == 'alarm'");
+
+  var audio3 = document.getElementById("audio3");
+  ok(audio3, "Audio Element exists");
+  is(audio3.mozAudioChannelType, "content", "Default audio3 channel == 'content'");
+  try {
+  audio3.mozAudioChannelType = "foo";
+  } catch(e) {}
+  is(audio3.mozAudioChannelType, "content", "audio3 channel == 'content'");
+  audio3.mozAudioChannelType = "alarm";
+  is(audio3.mozAudioChannelType, "alarm", "audio3 channel == 'alarm'");
+
+  runTest();
+}
 
-var audio2 = document.getElementById("audio2");
-ok(audio2, "Audio Element exists");
-ok(audio2.mozAudioChannelType == "normal", "Default audio2 channel == 'normal'");
-try {
-audio2.mozAudioChannelType = "foo";
-} catch(e) {}
-ok(audio2.mozAudioChannelType == "normal", "Default audio2 channel == 'normal'");
-audio2.mozAudioChannelType = "alarm";
-ok(audio2.mozAudioChannelType == "alarm", "Default audio2 channel == 'alarm'");
+function test_preferences(aChannel) {
+  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", aChannel ]]},
+    function() {
+      var audio = document.createElement('audio');
+      ok(audio, "Audio Element created");
+      is(audio.mozAudioChannelType, aChannel, "Default audio channel == '" + aChannel + "'");
+      runTest();
+    }
+  );
+}
 
-var audio3 = document.getElementById("audio3");
-ok(audio3, "Audio Element exists");
-ok(audio3.mozAudioChannelType == "content", "Default audio3 channel == 'content'");
-try {
-audio3.mozAudioChannelType = "foo";
-} catch(e) {}
-ok(audio3.mozAudioChannelType == "content", "audio3 channel == 'content'");
-audio3.mozAudioChannelType = "alarm";
-ok(audio3.mozAudioChannelType == "alarm", "audio3 channel == 'alarm'");
+function test_wrong_preferences() {
+  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", 'foobar' ]]},
+    function() {
+      var audio = document.createElement('audio');
+      ok(audio, "Audio Element created");
+      is(audio.mozAudioChannelType, 'normal', "Default audio channel == 'normal'");
+      runTest();
+    }
+  );
+}
+var tests = [
+  test_basic,
 
+  function() { test_preferences("content"); },
+  function() { test_preferences("notification"); },
+  function() { test_preferences("alarm"); },
+  function() { test_preferences("telephony"); },
+  function() { test_preferences("ringer"); },
+  function() { test_preferences("publicnotification"); },
+
+  test_wrong_preferences,
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -3,16 +3,17 @@
 /* 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/. */
 
 #include "AudioDestinationNode.h"
 #include "mozilla/dom/AudioDestinationNodeBinding.h"
 #include "mozilla/Preferences.h"
 #include "AudioChannelAgent.h"
+#include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "MediaStreamGraph.h"
 #include "OfflineAudioCompletionEvent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShell.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
@@ -232,16 +233,22 @@ AudioDestinationNode::AudioDestinationNo
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
 
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
   mStream->AddMainThreadListener(this);
   mStream->AddAudioOutput(&gWebAudioOutputKey);
 
+  AudioChannel channel = AudioChannelService::GetDefaultAudioChannel();
+  if (channel != AudioChannel::Normal) {
+    ErrorResult rv;
+    SetMozAudioChannelType(channel, rv);
+  }
+
   if (!aIsOffline && UseAudioChannelService()) {
     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
     if (target) {
       target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
                                      /* useCapture = */ true,
                                      /* wantsUntrusted = */ false);
     }
 
@@ -431,16 +438,21 @@ AudioDestinationNode::CheckAudioChannelP
     return true;
   }
 
   // Only normal channel doesn't need permission.
   if (aValue == AudioChannel::Normal) {
     return true;
   }
 
+  // Maybe this audio channel is equal to the default one.
+  if (aValue == AudioChannelService::GetDefaultAudioChannel()) {
+    return true;
+  }
+
   nsCOMPtr<nsIPermissionManager> permissionManager =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   if (!permissionManager) {
     return false;
   }
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetOwner());
   NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
--- a/content/media/webaudio/test/test_mozaudiochannel.html
+++ b/content/media/webaudio/test/test_mozaudiochannel.html
@@ -46,34 +46,70 @@ function test_basic() {
 
 function test_permission(aChannel) {
   var ac = new AudioContext();
   ok(ac, "AudioContext created");
 
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
   SpecialPowers.pushPermissions(
-    [{ "type": "audio-channel-" + aChannel, "allow": 1, "context": document }],
+    [{ "type": "audio-channel-" + aChannel, "allow": true, "context": document }],
     function() {
       ac.mozAudioChannelType = aChannel;
       is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'");
       runTest();
     }
   );
 }
 
+function test_preferences(aChannel) {
+  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", aChannel ]]},
+    function() {
+      SpecialPowers.pushPermissions(
+        [{ "type": "audio-channel-" + aChannel, "allow": false, "context": document }],
+        function() {
+          var ac = new AudioContext();
+          ok(ac, "AudioContext created");
+          is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'");
+          runTest();
+        }
+      );
+    }
+  );
+}
+
+function test_wrong_preferences() {
+  SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", 'foobar' ]]},
+    function() {
+      var ac = new AudioContext();
+      ok(ac, "AudioContext created");
+      is(ac.mozAudioChannelType, 'normal', "Default ac channel == 'normal'");
+      runTest();
+    }
+  );
+}
+
 var tests = [
   test_basic,
 
   function() { test_permission("content"); },
   function() { test_permission("notification"); },
   function() { test_permission("alarm"); },
   function() { test_permission("telephony"); },
   function() { test_permission("ringer"); },
-  function() { test_permission("publicnotification"); }
+  function() { test_permission("publicnotification"); },
+
+  function() { test_preferences("content"); },
+  function() { test_preferences("notification"); },
+  function() { test_preferences("alarm"); },
+  function() { test_preferences("telephony"); },
+  function() { test_preferences("ringer"); },
+  function() { test_preferences("publicnotification"); },
+
+  test_wrong_preferences,
 ];
 
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/audiochannel/AudioChannelCommon.h
+++ b/dom/audiochannel/AudioChannelCommon.h
@@ -25,13 +25,14 @@ enum AudioChannelType {
 };
 
 enum AudioChannelState {
   AUDIO_CHANNEL_STATE_NORMAL = 0,
   AUDIO_CHANNEL_STATE_MUTED,
   AUDIO_CHANNEL_STATE_FADED,
   AUDIO_CHANNEL_STATE_LAST
 };
+
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -24,16 +24,18 @@
 #ifdef MOZ_WIDGET_GONK
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIAudioManager.h"
 #include "SpeakerManagerService.h"
 #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
 #endif
 
+#include "mozilla/Preferences.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 StaticRefPtr<AudioChannelService> gAudioChannelService;
 
 // static
 AudioChannelService*
@@ -875,8 +877,57 @@ AudioChannelService::CountWindowEnumerat
 
 uint32_t
 AudioChannelService::CountWindow(nsIDOMWindow* aWindow)
 {
   CountWindowData data(aWindow);
   mAgents.EnumerateRead(CountWindowEnumerator, &data);
   return data.mCount;
 }
+
+// Mappings from 'mozaudiochannel' attribute strings to an enumeration.
+static const struct AudioChannelTable
+{
+  const char* string;
+  AudioChannel value;
+} kMozAudioChannelAttributeTable[] = {
+  { "normal",             AudioChannel::Normal },
+  { "content",            AudioChannel::Content },
+  { "notification",       AudioChannel::Notification },
+  { "alarm",              AudioChannel::Alarm },
+  { "telephony",          AudioChannel::Telephony },
+  { "ringer",             AudioChannel::Ringer },
+  { "publicnotification", AudioChannel::Publicnotification },
+  { nullptr }
+};
+
+/* static */ AudioChannel
+AudioChannelService::GetDefaultAudioChannel()
+{
+  nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
+  if (audioChannel.IsEmpty()) {
+    return AudioChannel::Normal;
+  }
+
+  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].string; ++i) {
+    if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].string)) {
+      return kMozAudioChannelAttributeTable[i].value;
+    }
+  }
+
+  return AudioChannel::Normal;
+}
+
+/* static */ void
+AudioChannelService::GetDefaultAudioChannelString(nsString& aString)
+{
+  aString.AssignASCII("normal");
+
+  nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
+  if (!audioChannel.IsEmpty()) {
+    for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].string; ++i) {
+      if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].string)) {
+        aString = audioChannel;
+        break;
+      }
+    }
+  }
+}
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -10,16 +10,17 @@
 #include "nsAutoPtr.h"
 #include "nsIObserver.h"
 #include "nsTArray.h"
 #include "nsITimer.h"
 
 #include "AudioChannelCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsClassHashtable.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
@@ -97,16 +98,20 @@ public:
     }
   }
 
   void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
   {
     mSpeakerManager.RemoveElement(aSpeakerManager);
   }
 #endif
+
+  static AudioChannel GetDefaultAudioChannel();
+  static void GetDefaultAudioChannelString(nsString& aString);
+
 protected:
   void Notify();
 
   /**
    * Send the audio-channel-changed notification for the given process ID if
    * needed.
    */
   void SendAudioChannelChangedNotification(uint64_t aChildID);