Bug 907352 - Part 1: Update to most recent constraints syntax. r=mt
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 18 Apr 2014 14:00:16 -0400
changeset 180791 abd958ec8fec919fc85f923d07ce471a6ca5c5da
parent 180790 426526ce43670da76bbcf5df8ca20d53f7f06394
child 180792 a8aea4c4977e072fcbe56f63ad092cf811685866
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersmt
bugs907352
milestone31.0a1
Bug 907352 - Part 1: Update to most recent constraints syntax. r=mt
content/media/webspeech/recognition/SpeechRecognition.cpp
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/media/GetUserMediaRequest.cpp
dom/media/GetUserMediaRequest.h
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/MediaPermissionGonk.cpp
dom/media/tests/mochitest/test_getUserMedia_constraints.html
dom/media/tests/mochitest/test_getUserMedia_exceptions.html
dom/webidl/GetUserMediaRequest.webidl
dom/webidl/MediaStream.webidl
dom/webidl/MediaStreamTrack.webidl
dom/webidl/Navigator.webidl
--- a/content/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/content/media/webspeech/recognition/SpeechRecognition.cpp
@@ -706,24 +706,22 @@ SpeechRecognition::Start(ErrorResult& aR
 
   nsresult rv;
   mRecognitionService = do_GetService(speechRecognitionServiceCID.get(), &rv);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   rv = mRecognitionService->Initialize(this->asWeakPtr());
   NS_ENSURE_SUCCESS_VOID(rv);
 
-  AutoSafeJSContext cx;
   MediaStreamConstraints constraints;
   constraints.mAudio.SetAsBoolean() = true;
 
   if (!mTestConfig.mFakeFSMEvents) {
     MediaManager* manager = MediaManager::Get();
-    manager->GetUserMedia(cx,
-                          false,
+    manager->GetUserMedia(false,
                           GetOwner(),
                           constraints,
                           new GetUserMediaSuccessCallback(this),
                           new GetUserMediaErrorCallback(this));
   }
 
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_START);
   NS_DispatchToMainThread(event);
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1289,18 +1289,17 @@ Navigator::SendBeacon(const nsAString& a
     aRv.Throw(rv);
     return false;
   }
   return true;
 }
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 void
-Navigator::MozGetUserMedia(JSContext* aCx,
-                           const MediaStreamConstraints& aConstraints,
+Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
                            NavigatorUserMediaSuccessCallback& aOnSuccess,
                            NavigatorUserMediaErrorCallback& aOnError,
                            ErrorResult& aRv)
 {
   CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
                        nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onsuccess =
     holder1.ToXPCOMCallback();
@@ -1313,22 +1312,22 @@ Navigator::MozGetUserMedia(JSContext* aC
       mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   bool privileged = nsContentUtils::IsChromeDoc(mWindow->GetExtantDoc());
 
   MediaManager* manager = MediaManager::Get();
-  aRv = manager->GetUserMedia(aCx, privileged, mWindow, aConstraints,
+  aRv = manager->GetUserMedia(privileged, mWindow, aConstraints,
                               onsuccess, onerror);
 }
 
 void
-Navigator::MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
+Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints,
                                   MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
                                   NavigatorUserMediaErrorCallback& aOnError,
                                   uint64_t aInnerWindowID,
                                   ErrorResult& aRv)
 {
   CallbackObjectHolder<MozGetUserMediaDevicesSuccessCallback,
                        nsIGetUserMediaDevicesSuccessCallback> holder1(&aOnSuccess);
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onsuccess =
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -28,17 +28,16 @@ class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIDOMBlob;
 
 namespace mozilla {
 namespace dom {
 class Geolocation;
 class systemMessageCallback;
 class MediaStreamConstraints;
-class MediaStreamConstraintsInternal;
 class WakeLock;
 class ArrayBufferViewOrBlobOrStringOrFormData;
 }
 }
 
 #ifdef MOZ_B2G_RIL
 class nsIDOMMozIccManager;
 #endif // MOZ_B2G_RIL
@@ -226,22 +225,21 @@ public:
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 
   bool SendBeacon(const nsAString& aUrl,
                   const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
                   ErrorResult& aRv);
 
 #ifdef MOZ_MEDIA_NAVIGATOR
-  void MozGetUserMedia(JSContext* aCx,
-                       const MediaStreamConstraints& aConstraints,
+  void MozGetUserMedia(const MediaStreamConstraints& aConstraints,
                        NavigatorUserMediaSuccessCallback& aOnSuccess,
                        NavigatorUserMediaErrorCallback& aOnError,
                        ErrorResult& aRv);
-  void MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
+  void MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints,
                               MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
                               NavigatorUserMediaErrorCallback& aOnError,
                               uint64_t aInnerWindowID,
                               ErrorResult& aRv);
 #endif // MOZ_MEDIA_NAVIGATOR
   bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
                     JS::Handle<jsid> aId,
                     JS::MutableHandle<JSPropertyDescriptor> aDesc);
--- a/dom/media/GetUserMediaRequest.cpp
+++ b/dom/media/GetUserMediaRequest.cpp
@@ -11,22 +11,22 @@
 #include "nsCxPusher.h"
 
 namespace mozilla {
 namespace dom {
 
 GetUserMediaRequest::GetUserMediaRequest(
     nsPIDOMWindow* aInnerWindow,
     const nsAString& aCallID,
-    const MediaStreamConstraintsInternal& aConstraints,
+    const MediaStreamConstraints& aConstraints,
     bool aIsSecure)
   : mInnerWindowID(aInnerWindow->WindowID())
   , mOuterWindowID(aInnerWindow->GetOuterWindow()->WindowID())
   , mCallID(aCallID)
-  , mConstraints(new MediaStreamConstraintsInternal(aConstraints))
+  , mConstraints(new MediaStreamConstraints(aConstraints))
   , mIsSecure(aIsSecure)
 {
   SetIsDOMBinding();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(GetUserMediaRequest)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(GetUserMediaRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(GetUserMediaRequest)
@@ -62,15 +62,15 @@ uint64_t GetUserMediaRequest::InnerWindo
 }
 
 bool GetUserMediaRequest::IsSecure()
 {
   return mIsSecure;
 }
 
 void
-GetUserMediaRequest::GetConstraints(MediaStreamConstraintsInternal &result)
+GetUserMediaRequest::GetConstraints(MediaStreamConstraints &result)
 {
   result = *mConstraints;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/GetUserMediaRequest.h
+++ b/dom/media/GetUserMediaRequest.h
@@ -9,43 +9,43 @@
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
-class MediaStreamConstraintsInternal;
+class MediaStreamConstraints;
 
 class GetUserMediaRequest : public nsISupports, public nsWrapperCache
 {
 public:
   GetUserMediaRequest(nsPIDOMWindow* aInnerWindow,
                       const nsAString& aCallID,
-                      const MediaStreamConstraintsInternal& aConstraints,
+                      const MediaStreamConstraints& aConstraints,
                       bool aIsSecure);
   virtual ~GetUserMediaRequest() {};
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GetUserMediaRequest)
 
   virtual JSObject* WrapObject(JSContext* cx)
     MOZ_OVERRIDE;
   nsISupports* GetParentObject();
 
   uint64_t WindowID();
   uint64_t InnerWindowID();
   bool IsSecure();
   void GetCallID(nsString& retval);
-  void GetConstraints(MediaStreamConstraintsInternal &result);
+  void GetConstraints(MediaStreamConstraints &result);
 
 private:
   uint64_t mInnerWindowID, mOuterWindowID;
   const nsString mCallID;
-  nsAutoPtr<MediaStreamConstraintsInternal> mConstraints;
+  nsAutoPtr<MediaStreamConstraints> mConstraints;
   bool mIsSecure;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // GetUserMediaRequest_h__
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -18,16 +18,17 @@
 #include "nsIScriptGlobalObject.h"
 #include "nsIPermissionManager.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
+#include "mozilla/Types.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
@@ -69,63 +70,21 @@ GetMediaManagerLog()
   return sLog;
 }
 #define LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
 #else
 #define LOG(msg)
 #endif
 
 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)
-{
-  JS::Rooted<JSObject*> a(aCx, aA);
-  JSAutoCompartment ac(aCx, aA);
-  JS::Rooted<JS::Value> bval(aCx);
-  aB.ToObject(aCx, &bval);
-  JS::Rooted<JSObject*> b(aCx, &bval.toObject());
-
-  // Iterate over each property in A, and check if it is in B
-
-  JS::AutoIdArray props(aCx, JS_Enumerate(aCx, a));
-
-  for (size_t i = 0; i < props.length(); i++) {
-    JS::Rooted<JS::Value> bprop(aCx);
-    JS::Rooted<jsid> id(aCx, props[i]);
-    if (!JS_GetPropertyById(aCx, b, id, &bprop)) {
-      LOG(("Error parsing dictionary!\n"));
-      return NS_ERROR_UNEXPECTED;
-    }
-    if (bprop.isUndefined()) {
-      // Unknown property found in A. Bail with name
-      JS::Rooted<JS::Value> nameval(aCx);
-      bool success = JS_IdToValue(aCx, props[i], &nameval);
-      NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
-
-      JS::Rooted<JSString*> namestr(aCx, JS::ToString(aCx, nameval));
-      NS_ENSURE_TRUE(namestr, NS_ERROR_UNEXPECTED);
-      aDifference->Assign(JS_GetStringCharsZ(aCx, namestr));
-      return NS_OK;
-    }
-  }
-  aDifference->Truncate();
-  return NS_OK;
-}
+using dom::OwningBooleanOrMediaTrackConstraints;
 
 ErrorCallbackRunnable::ErrorCallbackRunnable(
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aError,
   const nsAString& aErrorMsg, uint64_t aWindowID)
   : mErrorMsg(aErrorMsg)
   , mWindowID(aWindowID)
   , mManager(MediaManager::GetInstance())
@@ -675,64 +634,101 @@ private:
   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) {
+IsOn(const dom::OwningBooleanOrMediaTrackConstraints &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)
+#define lengthof(a) (sizeof(a) / sizeof(*a))
+
+static auto
+GetSupportedConstraintNames(const MediaEngineVideoSource *) ->
+    decltype((dom::SupportedVideoConstraintsValues::strings)) {
+  return dom::SupportedVideoConstraintsValues::strings;
+}
+
+static auto
+GetSupportedConstraintNames(const MediaEngineAudioSource *) ->
+    decltype((dom::SupportedAudioConstraintsValues::strings)) {
+  return dom::SupportedAudioConstraintsValues::strings;
+}
+
+// Reminder: add handling for new constraints both here and in GetSources below!
+
+static bool SatisfyConstraintSet(const MediaEngineVideoSource *,
+                                 const MediaTrackConstraintSet &aConstraints,
+                                 nsIMediaDevice &aCandidate)
 {
   if (aConstraints.mFacingMode.WasPassed()) {
     nsString s;
     aCandidate.GetFacingMode(s);
     if (!s.EqualsASCII(dom::VideoFacingModeEnumValues::strings[
         uint32_t(aConstraints.mFacingMode.Value())].value)) {
       return false;
     }
   }
   // TODO: Add more video-specific constraints
   return true;
 }
 
-static bool SatisfyConstraint(const MediaEngineAudioSource *,
-                              const MediaTrackConstraintSet &aConstraints,
-                              nsIMediaDevice &aCandidate)
+static bool SatisfyConstraintSet(const MediaEngineAudioSource *,
+                                 const MediaTrackConstraintSet &aConstraints,
+                                 nsIMediaDevice &aCandidate)
 {
   // TODO: Add audio-specific constraints
   return true;
 }
 
+// Triage constraints into required and nonrequired + detect missing requireds
+
+class TriageHelper
+{
+public:
+  TriageHelper(const nsTArray<nsString>& aRequire)
+  : mRequire(aRequire)
+  , mNumRequirementsMet(0) {}
+
+  MediaTrackConstraintSet& Triage(const nsAString &name) {
+    if (mRequire.IndexOf(name) != mRequire.NoIndex) {
+      mNumRequirementsMet++;
+      return mRequired;
+    } else {
+      return mNonrequired;
+    }
+  }
+  bool RequirementsAreMet() {
+    MOZ_ASSERT(mNumRequirementsMet <= mRequire.Length());
+    return mNumRequirementsMet == mRequire.Length();
+  }
+  MediaTrackConstraintSet mRequired;
+  MediaTrackConstraintSet mNonrequired;
+private:
+  const nsTArray<nsString> mRequire;
+  uint32_t mNumRequirementsMet;
+};
+
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType>
 static SourceSet *
   GetSources(MediaEngine *engine,
-             const OwningBooleanOrMediaTrackConstraintsInternal &aConstraints,
+             const OwningBooleanOrMediaTrackConstraints &aConstraints,
              void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*),
              char* media_device_name = nullptr)
 {
   ScopedDeletePtr<SourceSet> result(new SourceSet);
 
   if (!IsOn(aConstraints)) {
     return result.forget();
   }
@@ -762,64 +758,114 @@ static SourceSet *
 #endif
         candidateSet.AppendElement(new MediaDevice(sources[i]));
 #ifdef DEBUG
       }
 #endif
     }
   }
 
+  // If unconstrained then return the full list.
+
   if (aConstraints.IsBoolean()) {
     MOZ_ASSERT(aConstraints.GetAsBoolean());
     result->MoveElementsFrom(candidateSet);
     return result.forget();
   }
-  auto& constraints = aConstraints.GetAsMediaTrackConstraintsInternal();
 
-  // Then apply mandatory constraints
+  // Otherwise apply constraints to the list of sources.
 
-  // 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, constraints.mMandatory, *candidateSet[i])) {
-      candidateSet.RemoveElementAt(i--);
+  auto& constraints = aConstraints.GetAsMediaTrackConstraints();
+  const nsTArray<nsString> empty;
+  const auto &require = constraints.mRequire.WasPassed()?
+      constraints.mRequire.Value() : empty;
+  {
+    // Check upfront the names of required constraints that are unsupported for
+    // this media-type. The spec requires these to fail, so getting them out of
+    // the way early provides a necessary invariant for the remaining algorithm
+    // which maximizes code-reuse by ignoring constraints of the other type
+    // (specifically, SatisfyConstraintSet is reused for the advanced algorithm
+    // where the spec requires it to ignore constraints of the other type)
+
+    const auto& supported = GetSupportedConstraintNames(type);
+    for (uint32_t i = 0; i < require.Length(); i++) {
+      bool found = false;
+      // EnumType arrays have a zero-terminator entry at the end. Skip.
+      for (size_t j = 0; j < sizeof(supported)/sizeof(*supported) - 1; j++) {
+        if (require[i].EqualsASCII(supported[j].value)) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        return result.forget();
+      }
     }
   }
 
-  // Then apply optional constraints.
+  // Before we start, triage constraints into required and nonrequired.
+  // Reminder: add handling for new constraints both here & SatisfyConstraintSet
+
+  TriageHelper helper(require);
+
+  if (constraints.mFacingMode.WasPassed()) {
+    helper.Triage(NS_LITERAL_STRING("facingMode")).mFacingMode.Construct(
+        constraints.mFacingMode.Value());
+  }
+  if (!helper.RequirementsAreMet()) {
+    return result.forget();
+  }
+
+  // Now on to the actual algorithm: First apply required constraints.
+
+  for (uint32_t i = 0; i < candidateSet.Length();) {
+    // Overloading instead of template specialization keeps things local
+    if (!SatisfyConstraintSet(type, helper.mRequired, *candidateSet[i])) {
+      candidateSet.RemoveElementAt(i);
+    } else {
+      ++i;
+    }
+  }
+
+  // Then apply advanced (formerly known as 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
   // of media type T that the browser COULD RETURN" (emphasis added).
   //
   // We think users ultimately control which devices we could return, so after
   // determining the webpage's preferred list, we add the remaining choices
   // 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 (constraints.mOptional.WasPassed()) {
-    const auto &array = constraints.mOptional.Value();
+  if (constraints.mAdvanced.WasPassed()) {
+    const auto &array = constraints.mAdvanced.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])) {
+      for (uint32_t j = 0; j < candidateSet.Length();) {
+        if (!SatisfyConstraintSet(type, array[i], *candidateSet[j])) {
           rejects.AppendElement(candidateSet[j]);
-          candidateSet.RemoveElementAt(j--);
+          candidateSet.RemoveElementAt(j);
+        } else {
+          ++j;
         }
       }
       (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
     }
   }
 
+  // Finally, order any remaining sources by how many nonrequired constraints
+  // they satisfy. TODO(jib): TBD once we implement >1 constraint (Bug 907352)
+
+
   result->MoveElementsFrom(candidateSet);
   result->MoveElementsFrom(tailSet);
   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
@@ -828,17 +874,17 @@ static SourceSet *
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
 class GetUserMediaRunnable : public nsRunnable
 {
 public:
   GetUserMediaRunnable(
-    const MediaStreamConstraintsInternal& aConstraints,
+    const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
@@ -849,17 +895,17 @@ public:
     , mManager(MediaManager::GetInstance())
   {}
 
   /**
    * The caller can also choose to provide their own backend instead of
    * using the one provided by MediaManager::GetBackend.
    */
   GetUserMediaRunnable(
-    const MediaStreamConstraintsInternal& aConstraints,
+    const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs,
     MediaEngine* aBackend)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
@@ -955,17 +1001,17 @@ public:
 
     MOZ_ASSERT(!mSuccess);
     MOZ_ASSERT(!mError);
 
     return NS_OK;
   }
 
   nsresult
-  SetContraints(const MediaStreamConstraintsInternal& aConstraints)
+  SetContraints(const MediaStreamConstraints& aConstraints)
   {
     mConstraints = aConstraints;
     return NS_OK;
   }
 
   nsresult
   SetAudioDevice(MediaDevice* aAudioDevice)
   {
@@ -1084,17 +1130,17 @@ public:
 
     MOZ_ASSERT(!mSuccess);
     MOZ_ASSERT(!mError);
 
     return;
   }
 
 private:
-  MediaStreamConstraintsInternal mConstraints;
+  MediaStreamConstraints mConstraints;
 
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaDevice> mAudioDevice;
   nsRefPtr<MediaDevice> mVideoDevice;
   MediaEnginePrefs mPrefs;
@@ -1110,17 +1156,17 @@ private:
  * GetUserMediaDevices function. Enumerates a list of audio & video devices,
  * wraps them up in nsIMediaDevice objects and returns it to the success
  * callback.
  */
 class GetUserMediaDevicesRunnable : public nsRunnable
 {
 public:
   GetUserMediaDevicesRunnable(
-    const MediaStreamConstraintsInternal& aConstraints,
+    const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowId, char* aAudioLoopbackDev, char* aVideoLoopbackDev)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mManager(MediaManager::GetInstance())
     , mWindowId(aWindowId)
@@ -1151,17 +1197,17 @@ public:
                                                               mSuccess, mError,
                                                               final.forget()));
     // DeviceSuccessCallbackRunnable should have taken these.
     MOZ_ASSERT(!mSuccess && !mError);
     return NS_OK;
   }
 
 private:
-  MediaStreamConstraintsInternal mConstraints;
+  MediaStreamConstraints mConstraints;
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
   const nsString mCallId;
   // Audio & Video loopback devices to be used based on
   // the preference settings. This is currently used for
   // automated media tests only.
@@ -1297,66 +1343,30 @@ MediaManager::NotifyRecordingStatusChang
 }
 
 /**
  * The entry point for this file. A call from Navigator::mozGetUserMedia
  * will end up here. MediaManager is a singleton that is responsible
  * for handling all incoming getUserMedia calls from every window.
  */
 nsresult
-MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
-  nsPIDOMWindow* aWindow, const MediaStreamConstraints& aRawConstraints,
+MediaManager::GetUserMedia(bool aPrivileged,
+  nsPIDOMWindow* aWindow, const MediaStreamConstraints& aConstraints,
   nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnError)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   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;
-  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 may have JSObject in mMandatory, so copy everything into
-  // MediaStreamConstraintsInternal which does not.
-
-  dom::RootedDictionary<MediaStreamConstraintsInternal> c(aCx);
-  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);
-
-  // Validate mandatory constraints by detecting any unknown constraints.
-  // Done by comparing the raw MediaTrackConstraints against the normalized copy
-
-  nsString unknownConstraintFound;
-  if (audioObj) {
-    nsresult rv = CompareDictionaries(aCx, audioObj,
-        c.mAudio.GetAsMediaTrackConstraintsInternal().mMandatory,
-        &unknownConstraintFound);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if (videoObj) {
-    nsresult rv = CompareDictionaries(aCx, videoObj,
-        c.mVideo.GetAsMediaTrackConstraintsInternal().mMandatory,
-        &unknownConstraintFound);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   /**
    * 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.
    */
 #if !defined(MOZ_WEBRTC)
@@ -1397,43 +1407,28 @@ MediaManager::GetUserMedia(JSContext* aC
   // This is safe since we're on main-thread, and the windowlist can only
   // be invalidated from the main-thread (see OnNavigation)
   StreamListeners* listeners = GetActiveWindows()->Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     GetActiveWindows()->Put(windowID, listeners);
   }
 
-  if (!unknownConstraintFound.IsEmpty()) {
-    // An unsupported mandatory constraint was found.
-    //
-    // We continue to ignore these for now, because we implement just
-    // facingMode, which means all existing uses of mandatory width/height would
-    // fail on Firefox only otherwise, which is undesirable.
-    //
-    // There's also basis for always ignoring them in a new proposal.
-    // TODO(jib): This is a super-low-risk fix for backport. Clean up later.
-
-    LOG(("Unsupported mandatory constraint: %s\n",
-          NS_ConvertUTF16toUTF8(unknownConstraintFound).get()));
-
-    // unknown constraints existed in aRawConstraints only, which is unused
-    // from here, so continuing here effectively ignores them, as is desired.
-  }
-
   // Ensure there's a thread for gum to proxy to off main thread
   nsIThread *mediaThread = MediaManager::GetThread();
 
   // Create a disabled listener to act as a placeholder
   GetUserMediaCallbackMediaStreamListener* listener =
     new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID);
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
+  MediaStreamConstraints c(aConstraints); // copy
+
   // 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.SetAsBoolean() = false;
   }
 
@@ -1562,17 +1557,17 @@ MediaManager::GetUserMedia(JSContext* aC
     obs->NotifyObservers(req, "getUserMedia:request", nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
-  const MediaStreamConstraintsInternal& aConstraints,
+  const MediaStreamConstraints& aConstraints,
   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnError,
   uint64_t aInnerWindowID)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   NS_ENSURE_TRUE(aOnError, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -490,24 +490,24 @@ public:
   }
   bool IsWindowStillActive(uint64_t aWindowId) {
     return !!GetWindowListeners(aWindowId);
   }
   // Note: also calls aListener->Remove(), even if inactive
   void RemoveFromWindowList(uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener *aListener);
 
-  nsresult GetUserMedia(JSContext* aCx, bool aPrivileged,
+  nsresult GetUserMedia(bool aPrivileged,
     nsPIDOMWindow* aWindow,
     const dom::MediaStreamConstraints& aRawConstraints,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
 
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
-    const dom::MediaStreamConstraintsInternal& aConstraints,
+    const dom::MediaStreamConstraints& aConstraints,
     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError,
     uint64_t aInnerWindowID = 0);
   void OnNavigation(uint64_t aWindowID);
 
   MediaEnginePrefs mPrefs;
 
 private:
--- a/dom/media/MediaPermissionGonk.cpp
+++ b/dom/media/MediaPermissionGonk.cpp
@@ -171,17 +171,17 @@ private:
 
 // MediaPermissionRequest
 NS_IMPL_ISUPPORTS1(MediaPermissionRequest, nsIContentPermissionRequest)
 
 MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
                                                nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
   : mRequest(aRequest)
 {
-  dom::MediaStreamConstraintsInternal constraints;
+  dom::MediaStreamConstraints constraints;
   mRequest->GetConstraints(constraints);
 
   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;
@@ -573,17 +573,17 @@ MediaPermissionManager::HandleRequest(ns
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess =
       new MediaDeviceSuccessCallback(req);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError =
       new MediaDeviceErrorCallback(callID);
 
-  dom::MediaStreamConstraintsInternal constraints;
+  dom::MediaStreamConstraints constraints;
   req->GetConstraints(constraints);
 
   nsRefPtr<MediaManager> MediaMgr = MediaManager::GetInstance();
   nsresult rv = MediaMgr->GetUserMediaDevices(innerWindow, constraints, onSuccess, onError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -15,34 +15,64 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 /**
   Tests covering gUM constraints API for audio, video and fake video. Exercise
-  successful parsing code and ensure that unknown mandatory constraints and
+  successful parsing code and ensure that unknown required constraints and
   overconstraining cases produce appropriate errors.
 */
 var tests = [
   // Each test here tests a different constraint or codepath.
+  { message: "unknown required constraint on video fails",
+    constraints: { video: { somethingUnknown:0, require:["somethingUnknown"] },
+                   fake: true },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "unknown required constraint on audio fails",
+    constraints: { audio: { somethingUnknown:0, require:["somethingUnknown"] },
+                   fake: true },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "missing required constraint on video fails",
+    constraints: { video: { require:["facingMode"] }, fake: true },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "missing required constraint on audio fails",
+    constraints: { audio: { require:["facingMode"] }, fake: true },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
   { message: "video overconstrained by facingMode fails",
-    constraints: { video: { mandatory: { facingMode:'left' } } },
+    constraints: { video: { facingMode:'left', require:["facingMode"] },
+                   fake: true },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "audio overconstrained by facingMode fails",
+    constraints: { audio: { facingMode:'left', require:["facingMode"] },
+                   fake: true },
     error: "NO_DEVICES_FOUND",
     pass: false },
   { message: "Success-path: optional video facingMode + audio ignoring facingMode",
     constraints: { fake: true,
-                   audio: { mandatory: { facingMode:'left' } },
-                   video: { mandatory: { somethingUnknown:0 },
-                            optional: [{ facingMode:'left' },
-                                       { facingMode:'right' },
-                                       { facingMode:'environment' },
+                   audio: { facingMode:'left',
+                            foo:0,
+                            advanced: [{ facingMode:'environment' },
                                        { facingMode:'user' },
-                                       { foo:0 }] } },
+                                       { bar:0 }] },
+                   video: { // TODO: Bug 767924 sequences in unions
+                            //facingMode:['left', 'right', 'user', 'environment'],
+                            //require:["facingMode"],
+                            facingMode:'left',
+                            foo:0,
+                            advanced: [{ facingMode:'environment' },
+                                       { facingMode:'user' },
+                                       { bar:0 }] } },
     error: null,
     pass: false },
   { message: null },
 ];
 
 /**
  * Starts the test run by running through each constraint
  * test by verifying that the right callback and error message is fired.
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -44,17 +44,17 @@ var exceptionTests = [
   { params: [{video: true, fake: true}, 1, unexpectedCall],
     error: "Argument 2 of Navigator.mozGetUserMedia is not an object.",
     message: "wrong object type as second parameter" },
   { params: [{video: true, fake: true}, unexpectedCall, 1],
     error: "Argument 3 of Navigator.mozGetUserMedia is not an object.",
     message: "wrong object type as third parameter" },
 
   // Each test here verifies constraint syntax as defined in webidl
-  { params: [{ fake: true, video: { optional: [{ facingMode:'foo' }] } },
+  { params: [{ fake: true, video: { advanced: [{ facingMode:'foo' }] } },
              unexpectedCall, unexpectedCall],
     error: "'facingMode' member of MediaTrackConstraintSet 'foo' is not a valid value for enumeration VideoFacingModeEnum.",
     message: "invalid facingMode enum value" }
 ];
 
 /**
  * A callback function that is only called if a particular
  * exception was not thrown, resulting in the test failing.
--- a/dom/webidl/GetUserMediaRequest.webidl
+++ b/dom/webidl/GetUserMediaRequest.webidl
@@ -6,11 +6,11 @@
  * This is an internal IDL file
  */
 
 [NoInterfaceObject]
 interface GetUserMediaRequest {
   readonly attribute unsigned long long windowID;
   readonly attribute unsigned long long innerWindowID;
   readonly attribute DOMString callID;
-  MediaStreamConstraintsInternal getConstraints();
+  MediaStreamConstraints getConstraints();
   readonly attribute boolean isSecure;
 };
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -11,25 +11,18 @@
  */
 
 // 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;
+    boolean picture = false; // Mozilla legacy
+    boolean fake = false;    // for testing
 };
 
 interface MediaStream {
     // readonly attribute DOMString    id;
     sequence<AudioStreamTrack> getAudioTracks ();
     sequence<VideoStreamTrack> getVideoTracks ();
     // MediaStreamTrack           getTrackById (DOMString trackId);
     // void                       addTrack (MediaStreamTrack track);
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -6,45 +6,45 @@
  * The origin of this IDL file is
  * 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.
  */
 
 // Important! Do not ever add members that might need tracing (e.g. object)
-// to MediaTrackConstraintSet or any dictionary marked XxxInternal here
+// to MediaTrackConstraintSet
 
 enum VideoFacingModeEnum {
     "user",
     "environment",
     "left",
     "right"
 };
 
-dictionary MediaTrackConstraintSet {
-    VideoFacingModeEnum facingMode;
+enum SupportedVideoConstraints {
+    "facingMode"
+};
+
+enum SupportedAudioConstraints {
+    "dummy"
 };
 
-// MediaTrackConstraint = single-property-subset of MediaTrackConstraintSet
-// Implemented as full set. Test Object.keys(pair).length == 1
-
-// typedef MediaTrackConstraintSet MediaTrackConstraint; // TODO: Bug 913053
-
-dictionary MediaTrackConstraints {
-    object mandatory; // so we can see unknown + unsupported constraints
-    sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
+dictionary MediaTrackConstraintSet {
+    ConstrainVideoFacingMode facingMode;
 };
 
-// Internal dictionary holds result of processing raw MediaTrackConstraints above
+dictionary MediaTrackConstraints : MediaTrackConstraintSet {
+    sequence<DOMString> require;
+    sequence<MediaTrackConstraintSet> advanced;
+};
 
-dictionary MediaTrackConstraintsInternal {
-    MediaTrackConstraintSet mandatory; // holds only supported constraints
-    sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
-};
+typedef VideoFacingModeEnum ConstrainVideoFacingMode;
+// TODO: Bug 767924 sequences in unions
+//typedef (VideoFacingModeEnum or sequence<VideoFacingModeEnum>) ConstrainVideoFacingMode;
 
 interface MediaStreamTrack {
     readonly    attribute DOMString             kind;
     readonly    attribute DOMString             id;
     readonly    attribute DOMString             label;
                 attribute boolean               enabled;
 //    readonly    attribute MediaStreamTrackState readyState;
 //    readonly    attribute SourceTypeEnum        sourceType;
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -332,17 +332,17 @@ partial interface Navigator {
                        NavigatorUserMediaSuccessCallback successCallback,
                        NavigatorUserMediaErrorCallback errorCallback);
 };
 
 // nsINavigatorUserMedia
 callback MozGetUserMediaDevicesSuccessCallback = void (nsIVariant? devices);
 partial interface Navigator {
   [Throws, ChromeOnly]
-  void mozGetUserMediaDevices(MediaStreamConstraintsInternal constraints,
+  void mozGetUserMediaDevices(MediaStreamConstraints constraints,
                               MozGetUserMediaDevicesSuccessCallback onsuccess,
                               NavigatorUserMediaErrorCallback onerror,
                               // The originating innerWindowID is needed to
                               // avoid calling the callbacks if the window has
                               // navigated away. It is optional only as legacy.
                               optional unsigned long long innerWindowID = 0);
 };
 #endif // MOZ_MEDIA_NAVIGATOR