Bug 907352 - Part 1: Update to most recent constraints syntax. r=mt
☠☠ backed out by 6632c970ef7c ☠ ☠
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 18 Apr 2014 03:23:29 -0400
changeset 197789 5fa0c8f5ccb2bd124a49f058ebdad002eff9695e
parent 197788 e5bffd7d60c5780f300425e9f55bcc55c9d80eae
child 197790 d8d01c95be038fb3989cc88e8e9bf371c4545f04
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)
reviewersmt
bugs907352
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 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())
@@ -667,64 +626,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();
   }
@@ -754,64 +750,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
@@ -820,17 +866,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)
@@ -841,17 +887,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)
@@ -947,17 +993,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)
   {
@@ -1076,17 +1122,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;
@@ -1102,17 +1148,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)
@@ -1143,17 +1189,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.
@@ -1289,66 +1335,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)
@@ -1389,43 +1399,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;
   }
 
@@ -1554,17 +1549,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,60 @@ 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"] } },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "unknown required constraint on audio fails",
+    constraints: { audio: { somethingUnknown:0, require:["somethingUnknown"] } },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "missing required constraint on video fails",
+    constraints: { video: { require:["facingMode"] } },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "missing required constraint on audio fails",
+    constraints: { audio: { require:["facingMode"] } },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
   { message: "video overconstrained by facingMode fails",
-    constraints: { video: { mandatory: { facingMode:'left' } } },
+    constraints: { video: { facingMode:'left', require:["facingMode"] } },
+    error: "NO_DEVICES_FOUND",
+    pass: false },
+  { message: "audio overconstrained by facingMode fails",
+    constraints: { audio: { facingMode:'left', require:["facingMode"] } },
     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