Bug 916012 - Clean up gUM to use webidl unions and filter by media-type. r=bz, r=bwc
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 04 Apr 2014 05:54:25 -0400
changeset 195801 2b3cf40ab7d3dd670e679e6d354181d2ffe73682
parent 195800 efd2b6b51237e5017967b5e6e5b8db342881f3c1
child 195802 f70d7183aaef76837597747f33300bd86aef2487
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, bwc
bugs916012
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 916012 - Clean up gUM to use webidl unions and filter by media-type. r=bz, r=bwc
CLOBBER
dom/media/GetUserMediaRequest.cpp
dom/media/GetUserMediaRequest.h
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/MediaPermissionGonk.cpp
dom/webidl/MediaStream.webidl
dom/webidl/MediaStreamTrack.webidl
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 991256 requires a clobber because it renames generated mobile/android/base/widget files.
+Bug 916012 moves definition from one WEBIDL_FILE to another (Bug 979886)
--- a/dom/media/GetUserMediaRequest.cpp
+++ b/dom/media/GetUserMediaRequest.cpp
@@ -1,31 +1,31 @@
 /* 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 "base/basictypes.h"
 #include "GetUserMediaRequest.h"
-#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsCxPusher.h"
 
 namespace mozilla {
 namespace dom {
 
 GetUserMediaRequest::GetUserMediaRequest(
     nsPIDOMWindow* aInnerWindow,
     const nsAString& aCallID,
     const MediaStreamConstraintsInternal& aConstraints)
   : mInnerWindowID(aInnerWindow->WindowID())
   , mOuterWindowID(aInnerWindow->GetOuterWindow()->WindowID())
   , mCallID(aCallID)
-  , mConstraints(aConstraints)
+  , mConstraints(new MediaStreamConstraintsInternal(aConstraints))
 {
   SetIsDOMBinding();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(GetUserMediaRequest)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(GetUserMediaRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(GetUserMediaRequest)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GetUserMediaRequest)
@@ -57,13 +57,13 @@ uint64_t GetUserMediaRequest::WindowID()
 uint64_t GetUserMediaRequest::InnerWindowID()
 {
   return mInnerWindowID;
 }
 
 void
 GetUserMediaRequest::GetConstraints(MediaStreamConstraintsInternal &result)
 {
-  result = mConstraints;
+  result = *mConstraints;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/GetUserMediaRequest.h
+++ b/dom/media/GetUserMediaRequest.h
@@ -5,17 +5,16 @@
 #ifndef GetUserMediaRequest_h__
 #define GetUserMediaRequest_h__
 
 #include "mozilla/ErrorResult.h"
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 class MediaStreamConstraintsInternal;
 
 class GetUserMediaRequest : public nsISupports, public nsWrapperCache
 {
@@ -35,15 +34,15 @@ public:
   uint64_t WindowID();
   uint64_t InnerWindowID();
   void GetCallID(nsString& retval);
   void GetConstraints(MediaStreamConstraintsInternal &result);
 
 private:
   uint64_t mInnerWindowID, mOuterWindowID;
   const nsString mCallID;
-  MediaStreamConstraintsInternal mConstraints;
+  nsAutoPtr<MediaStreamConstraintsInternal> mConstraints;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // GetUserMediaRequest_h__
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -19,16 +19,17 @@
 #include "nsIPermissionManager.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 
 #include "Latency.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
@@ -74,16 +75,17 @@ GetMediaManagerLog()
 
 using dom::MediaStreamConstraints;         // Outside API (contains JSObject)
 using dom::MediaStreamConstraintsInternal; // Storable supported constraints
 using dom::MediaTrackConstraintsInternal;  // Video or audio constraints
 using dom::MediaTrackConstraintSet;        // Mandatory or optional constraints
 using dom::MediaTrackConstraints;          // Raw mMandatory (as JSObject)
 using dom::GetUserMediaRequest;
 using dom::Sequence;
+using dom::OwningBooleanOrMediaTrackConstraintsInternal;
 
 // Used to compare raw MediaTrackConstraintSet against normalized dictionary
 // version to detect member differences, e.g. unsupported constraints.
 
 static nsresult CompareDictionaries(JSContext* aCx, JSObject *aA,
                                     const MediaTrackConstraintSet &aB,
                                     nsString *aDifference)
 {
@@ -115,39 +117,16 @@ static nsresult CompareDictionaries(JSCo
       aDifference->Assign(JS_GetStringCharsZ(aCx, namestr));
       return NS_OK;
     }
   }
   aDifference->Truncate();
   return NS_OK;
 }
 
-// Look for and return any unknown mandatory constraint. Done by comparing
-// a raw MediaTrackConstraints against a normalized copy, both passed in.
-
-static nsresult ValidateTrackConstraints(
-    JSContext *aCx, JSObject *aRaw,
-    const MediaTrackConstraintsInternal &aNormalized,
-    nsString *aOutUnknownConstraint)
-{
-  // First find raw mMandatory member (use MediaTrackConstraints as helper)
-  JS::Rooted<JS::Value> rawval(aCx, JS::ObjectValue(*aRaw));
-  dom::RootedDictionary<MediaTrackConstraints> track(aCx);
-  bool success = track.Init(aCx, rawval);
-  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
-
-  if (track.mMandatory.WasPassed()) {
-    nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
-                                      aNormalized.mMandatory,
-                                      aOutUnknownConstraint);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  return NS_OK;
-}
-
 ErrorCallbackRunnable::ErrorCallbackRunnable(
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aError,
   const nsAString& aErrorMsg, uint64_t aWindowID)
   : mErrorMsg(aErrorMsg)
   , mWindowID(aWindowID)
   , mManager(MediaManager::GetInstance())
 {
@@ -687,16 +666,28 @@ private:
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaEngineSource> mAudioSource;
   nsRefPtr<MediaEngineSource> mVideoSource;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
+static bool
+IsOn(const dom::OwningBooleanOrMediaTrackConstraintsInternal &aUnion) {
+  return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
+}
+
+static JSObject *
+GetMandatoryJSObj(const dom::OwningBooleanOrMediaTrackConstraints &aUnion) {
+  return (aUnion.IsMediaTrackConstraints() &&
+          aUnion.GetAsMediaTrackConstraints().mMandatory.WasPassed()) ?
+          aUnion.GetAsMediaTrackConstraints().mMandatory.Value() : nullptr;
+}
+
 /**
  * Helper functions that implement the constraints algorithm from
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
  */
 
 static bool SatisfyConstraint(const MediaEngineVideoSource *,
                               const MediaTrackConstraintSet &aConstraints,
                               nsIMediaDevice &aCandidate)
@@ -723,20 +714,26 @@ static bool SatisfyConstraint(const Medi
 
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType>
 static SourceSet *
   GetSources(MediaEngine *engine,
-             const MediaTrackConstraintsInternal &aConstraints,
+             const OwningBooleanOrMediaTrackConstraintsInternal &aConstraints,
              void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*),
              char* media_device_name = nullptr)
 {
+  ScopedDeletePtr<SourceSet> result(new SourceSet);
+
+  if (!IsOn(aConstraints)) {
+    return result.forget();
+  }
+
   const SourceType * const type = nullptr;
   nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<SourceType> > sources;
     (engine->*aEnumerate)(&sources);
     /**
@@ -757,22 +754,29 @@ static SourceSet *
 #endif
         candidateSet.AppendElement(new MediaDevice(sources[i]));
 #ifdef DEBUG
       }
 #endif
     }
   }
 
+  if (aConstraints.IsBoolean()) {
+    MOZ_ASSERT(aConstraints.GetAsBoolean());
+    result->MoveElementsFrom(candidateSet);
+    return result.forget();
+  }
+  auto& constraints = aConstraints.GetAsMediaTrackConstraintsInternal();
+
   // Then apply mandatory constraints
 
   // Note: Iterator must be signed as it can dip below zero
   for (int i = 0; i < int(candidateSet.Length()); i++) {
     // Overloading instead of template specialization keeps things local
-    if (!SatisfyConstraint(type, aConstraints.mMandatory, *candidateSet[i])) {
+    if (!SatisfyConstraint(type, constraints.mMandatory, *candidateSet[i])) {
       candidateSet.RemoveElementAt(i--);
     }
   }
 
   // Then apply optional constraints.
   //
   // These are only effective when there are multiple sources to pick from.
   // Spec as-of-this-writing says to run algorithm on "all possible tracks
@@ -783,35 +787,34 @@ static SourceSet *
   // to the tail, reasoning that they would all have passed individually,
   // i.e. if the user had any one of them as their sole device (enabled).
   //
   // This avoids users having to unplug/disable devices should a webpage pick
   // the wrong one (UX-fail). Webpage-preferred devices will be listed first.
 
   SourceSet tailSet;
 
-  if (aConstraints.mOptional.WasPassed()) {
-    const Sequence<MediaTrackConstraintSet> &array = aConstraints.mOptional.Value();
+  if (constraints.mOptional.WasPassed()) {
+    const auto &array = constraints.mOptional.Value();
     for (int i = 0; i < int(array.Length()); i++) {
       SourceSet rejects;
       // Note: Iterator must be signed as it can dip below zero
       for (int j = 0; j < int(candidateSet.Length()); j++) {
         if (!SatisfyConstraint(type, array[i], *candidateSet[j])) {
           rejects.AppendElement(candidateSet[j]);
           candidateSet.RemoveElementAt(j--);
         }
       }
       (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
     }
   }
 
-  SourceSet *result = new SourceSet;
   result->MoveElementsFrom(candidateSet);
   result->MoveElementsFrom(tailSet);
-  return result;
+  return result.forget();
 }
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia or ProcessGetUserMediaSnapshot is called, and the results
  * are sent back to the DOM.
  *
@@ -891,30 +894,31 @@ public:
     if (!mDeviceChosen) {
       nsresult rv = SelectDevice(backend);
       if (rv != NS_OK) {
         return rv;
       }
     }
 
     // It is an error if audio or video are requested along with picture.
-    if (mConstraints.mPicture && (mConstraints.mAudio || mConstraints.mVideo)) {
+    if (mConstraints.mPicture &&
+        (IsOn(mConstraints.mAudio) || IsOn(mConstraints.mVideo))) {
       Fail(NS_LITERAL_STRING("NOT_SUPPORTED_ERR"));
       return NS_OK;
     }
 
     if (mConstraints.mPicture) {
       ProcessGetUserMediaSnapshot(mVideoDevice->GetSource(), 0);
       return NS_OK;
     }
 
     // There's a bug in the permission code that can leave us with mAudio but no audio device
-    ProcessGetUserMedia(((mConstraints.mAudio && mAudioDevice) ?
+    ProcessGetUserMedia(((IsOn(mConstraints.mAudio) && mAudioDevice) ?
                          mAudioDevice->GetSource() : nullptr),
-                        ((mConstraints.mVideo && mVideoDevice) ?
+                        ((IsOn(mConstraints.mVideo) && mVideoDevice) ?
                          mVideoDevice->GetSource() : nullptr));
     return NS_OK;
   }
 
   nsresult
   Denied(const nsAString& aErrorMsg)
   {
     MOZ_ASSERT(mSuccess);
@@ -970,32 +974,32 @@ public:
     return NS_OK;
   }
 
   nsresult
   SelectDevice(MediaEngine* backend)
   {
     MOZ_ASSERT(mSuccess);
     MOZ_ASSERT(mError);
-    if (mConstraints.mPicture || mConstraints.mVideo) {
+    if (mConstraints.mPicture || IsOn(mConstraints.mVideo)) {
       ScopedDeletePtr<SourceSet> sources (GetSources(backend,
-          mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices));
+          mConstraints.mVideo, &MediaEngine::EnumerateVideoDevices));
 
       if (!sources->Length()) {
         Fail(NS_LITERAL_STRING("NO_DEVICES_FOUND"));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mVideoDevice = do_QueryObject((*sources)[0]);
       LOG(("Selected video device"));
     }
 
-    if (mConstraints.mAudio) {
+    if (IsOn(mConstraints.mAudio)) {
       ScopedDeletePtr<SourceSet> sources (GetSources(backend,
-          mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
+          mConstraints.mAudio, &MediaEngine::EnumerateAudioDevices));
 
       if (!sources->Length()) {
         Fail(NS_LITERAL_STRING("NO_DEVICES_FOUND"));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mAudioDevice = do_QueryObject((*sources)[0]);
       LOG(("Selected audio device"));
@@ -1121,21 +1125,21 @@ public:
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
     nsRefPtr<MediaEngine> backend;
     if (mConstraints.mFake)
       backend = new MediaEngineDefault();
     else
       backend = mManager->GetBackend(mWindowId);
 
-    ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideom,
+    ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideo,
                                           &MediaEngine::EnumerateVideoDevices,
                                           mLoopbackVideoDevice));
     {
-      ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudiom,
+      ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudio,
                                         &MediaEngine::EnumerateAudioDevices,
                                         mLoopbackAudioDevice));
       final->MoveElementsFrom(*s);
     }
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
                                                               mSuccess, mError,
                                                               final.forget()));
     // DeviceSuccessCallbackRunnable should have taken these.
@@ -1300,62 +1304,50 @@ MediaManager::GetUserMedia(JSContext* aC
   NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnError, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
 
   Maybe<JSAutoCompartment> ac;
-  if (aRawConstraints.mAudio.IsObject() || aRawConstraints.mVideo.IsObject()) {
-    ac.construct(aCx, (aRawConstraints.mVideo.IsObject()?
-                       aRawConstraints.mVideo.GetAsObject() :
-                       aRawConstraints.mAudio.GetAsObject()));
+  JS::Rooted<JSObject*> audioObj (aCx, GetMandatoryJSObj(aRawConstraints.mAudio));
+  JS::Rooted<JSObject*> videoObj (aCx, GetMandatoryJSObj(aRawConstraints.mVideo));
+  if (audioObj || videoObj) {
+    ac.construct(aCx, audioObj? audioObj : videoObj);
   }
 
-  // aRawConstraints has JSObjects in it, so process it by copying it into
+  // aRawConstraints may have JSObject in mMandatory, so copy everything into
   // MediaStreamConstraintsInternal which does not.
 
   dom::RootedDictionary<MediaStreamConstraintsInternal> c(aCx);
-
-  // TODO: Simplify this part once Bug 767924 is fixed.
-  // Since we cannot yet use unions on non-objects, we process the raw object
-  // into discrete members for internal use until Bug 767924 is fixed
-
-  nsresult rv;
-  nsString unknownConstraintFound;
+  JS::Rooted<JS::Value> temp(aCx);
+  // This isn't the fastest way to copy a MediaStreamConstraints into a
+  // MediaStreamConstraintsInternal, but requires less code maintenance than an
+  // explicit member-by-member copy, and should be safe given the circumstances.
+  aRawConstraints.ToObject(aCx, &temp);
+  bool success = c.Init(aCx, temp);
+  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
-  if (aRawConstraints.mAudio.IsObject()) {
-    JS::Rooted<JS::Value> temp(aCx,
-        JS::ObjectValue(*aRawConstraints.mAudio.GetAsObject()));
-    bool success = c.mAudiom.Init(aCx, temp);
-    NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+  // Validate mandatory constraints by detecting any unknown constraints.
+  // Done by comparing the raw MediaTrackConstraints against the normalized copy
 
-    rv = ValidateTrackConstraints(aCx, aRawConstraints.mAudio.GetAsObject(),
-                                  c.mAudiom, &unknownConstraintFound);
+  nsString unknownConstraintFound;
+  if (audioObj) {
+    nsresult rv = CompareDictionaries(aCx, audioObj,
+        c.mAudio.GetAsMediaTrackConstraintsInternal().mMandatory,
+        &unknownConstraintFound);
     NS_ENSURE_SUCCESS(rv, rv);
-    c.mAudio = true;
-  } else {
-    c.mAudio = aRawConstraints.mAudio.GetAsBoolean();
   }
-  if (aRawConstraints.mVideo.IsObject()) {
-    JS::Rooted<JS::Value> temp(aCx,
-        JS::ObjectValue(*aRawConstraints.mVideo.GetAsObject()));
-    bool success = c.mVideom.Init(aCx, temp);
-    NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
-
-    rv = ValidateTrackConstraints(aCx, aRawConstraints.mVideo.GetAsObject(),
-                                  c.mVideom, &unknownConstraintFound);
+  if (videoObj) {
+    nsresult rv = CompareDictionaries(aCx, videoObj,
+        c.mVideo.GetAsMediaTrackConstraintsInternal().mMandatory,
+        &unknownConstraintFound);
     NS_ENSURE_SUCCESS(rv, rv);
-    c.mVideo = true;
-  } else {
-    c.mVideo = aRawConstraints.mVideo.GetAsBoolean();
   }
-  c.mPicture = aRawConstraints.mPicture;
-  c.mFake = aRawConstraints.mFake;
 
   /**
    * If we were asked to get a picture, before getting a snapshot, we check if
    * the calling page is allowed to open a popup. We do this because
    * {picture:true} will open a new "window" to let the user preview or select
    * an image, on Android. The desktop UI for {picture:true} is TBD, at which
    * may point we can decide whether to extend this test there as well.
    */
@@ -1429,17 +1421,17 @@ MediaManager::GetUserMedia(JSContext* aC
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     aPrivileged = true;
   }
   if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
-    c.mVideo = false;
+    c.mVideo.SetAsBoolean() = false;
   }
 
   // Pass callbacks and MediaStreamListener along to GetUserMediaRunnable.
   nsRefPtr<GetUserMediaRunnable> runnable;
   if (c.mFake) {
     // Fake stream from default backend.
     runnable = new GetUserMediaRunnable(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
@@ -1469,47 +1461,47 @@ MediaManager::GetUserMedia(JSContext* aC
   } else {
     // Check if this site has persistent permissions.
     nsresult rv;
     nsCOMPtr<nsIPermissionManager> permManager =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
-    if (c.mAudio) {
+    if (IsOn(c.mAudio)) {
       rv = permManager->TestExactPermissionFromPrincipal(
         aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm);
       NS_ENSURE_SUCCESS(rv, rv);
       if (audioPerm == nsIPermissionManager::PROMPT_ACTION) {
         audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
       }
     }
 
     uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
-    if (c.mVideo) {
+    if (IsOn(c.mVideo)) {
       rv = permManager->TestExactPermissionFromPrincipal(
         aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm);
       NS_ENSURE_SUCCESS(rv, rv);
       if (videoPerm == nsIPermissionManager::PROMPT_ACTION) {
         videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
       }
     }
 
-    if ((!c.mAudio || audioPerm) && (!c.mVideo || videoPerm)) {
+    if ((!IsOn(c.mAudio) || audioPerm) && (!IsOn(c.mVideo) || videoPerm)) {
       // All permissions we were about to request already have a saved value.
-      if (c.mAudio && audioPerm == nsIPermissionManager::DENY_ACTION) {
-        c.mAudio = false;
+      if (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) {
+        c.mAudio.SetAsBoolean() = false;
         runnable->SetContraints(c);
       }
-      if (c.mVideo && videoPerm == nsIPermissionManager::DENY_ACTION) {
-        c.mVideo = false;
+      if (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION) {
+        c.mVideo.SetAsBoolean() = false;
         runnable->SetContraints(c);
       }
 
-      if (!c.mAudio && !c.mVideo) {
+      if (!IsOn(c.mAudio) && !IsOn(c.mVideo)) {
         return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
       }
 
       return mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
     }
 
     // Ask for user permission, and dispatch runnable (or not) when a response
     // is received via an observer notification. Each call is paired with its
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -16,16 +16,17 @@
 #include "nsIPrefBranch.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "prlog.h"
 #include "DOMMediaStream.h"
 
 #ifdef MOZ_WEBRTC
 #include "mtransport/runnable_utils.h"
 #endif
 
--- a/dom/media/MediaPermissionGonk.cpp
+++ b/dom/media/MediaPermissionGonk.cpp
@@ -174,18 +174,18 @@ NS_IMPL_ISUPPORTS1(MediaPermissionReques
 
 MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
                                                nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
   : mRequest(aRequest)
 {
   dom::MediaStreamConstraintsInternal constraints;
   mRequest->GetConstraints(constraints);
 
-  mAudio = constraints.mAudio;
-  mVideo = constraints.mVideo;
+  mAudio = !constraints.mAudio.IsBoolean() || constraints.mAudio.GetAsBoolean();
+  mVideo = !constraints.mVideo.IsBoolean() || constraints.mVideo.GetAsBoolean();
 
   for (uint32_t i = 0; i < aDevices.Length(); ++i) {
     nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
     nsAutoString deviceType;
     device->GetType(deviceType);
     if (mAudio && deviceType.EqualsLiteral("audio")) {
       mAudioDevices.AppendElement(device);
     }
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -5,16 +5,33 @@
  *
  * The origins of this IDL file are
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html
  *
  * Copyright � 2012 W3C� (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+// These dictionaries need to be in a separate file from their
+// MediaTrackConstraints* counterparts due to a webidl compiler limitation.
+
+dictionary MediaStreamConstraints {
+    (boolean or MediaTrackConstraints) audio = false;
+    (boolean or MediaTrackConstraints) video = false;
+    boolean picture = false;
+    boolean fake = false;
+};
+
+dictionary MediaStreamConstraintsInternal {
+    (boolean or MediaTrackConstraintsInternal) audio;
+    (boolean or MediaTrackConstraintsInternal) video;
+    boolean picture = false;
+    boolean fake = false;
+};
+
 interface MediaStream {
     // readonly attribute DOMString    id;
     sequence<AudioStreamTrack> getAudioTracks ();
     sequence<VideoStreamTrack> getVideoTracks ();
     // MediaStreamTrack           getTrackById (DOMString trackId);
     // void                       addTrack (MediaStreamTrack track);
     // void                       removeTrack (MediaStreamTrack track);
     //         attribute boolean      ended;
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -24,41 +24,23 @@ dictionary MediaTrackConstraintSet {
     VideoFacingModeEnum facingMode;
 };
 
 // MediaTrackConstraint = single-property-subset of MediaTrackConstraintSet
 // Implemented as full set. Test Object.keys(pair).length == 1
 
 // typedef MediaTrackConstraintSet MediaTrackConstraint; // TODO: Bug 913053
 
-dictionary MediaStreamConstraints {
-    (boolean or object) audio = false; // TODO: Once Bug 767924 fixed change to
-    (boolean or object) video = false; // (boolean or MediaTrackConstraints)
-    boolean picture = false;
-    boolean fake = false;
-};
-
-// Internal dictionary to process the raw objects in (boolean or object)
-// workaround above. Since we cannot yet use unions on non-objects, we process
-// the data into discrete members for internal use until Bug 767924 is fixed:
-
-dictionary MediaStreamConstraintsInternal {
-    boolean audio = false;
-    boolean video = false;
-    boolean picture = false;
-    boolean fake = false;
-    MediaTrackConstraintsInternal audiom;
-    MediaTrackConstraintsInternal videom;
-};
-
 dictionary MediaTrackConstraints {
     object mandatory; // so we can see unknown + unsupported constraints
     sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
 };
 
+// Internal dictionary holds result of processing raw MediaTrackConstraints above
+
 dictionary MediaTrackConstraintsInternal {
     MediaTrackConstraintSet mandatory; // holds only supported constraints
     sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
 };
 
 interface MediaStreamTrack {
     readonly    attribute DOMString             kind;
     readonly    attribute DOMString             id;