Bug 1119335 - support ideal/exact constraint syntax. r=mrbkap, r=mt
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 20 Feb 2015 17:06:26 -0500
changeset 257153 e9a8fe34405ccb260609e18ff885921502a78327
parent 257152 e8cfd49ac790eb7a8db2ff79f6fad74c7f567129
child 257154 c96050cdedd5f7b9336678499f91cf136fea7b04
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, mt
bugs1119335
milestone38.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 1119335 - support ideal/exact constraint syntax. r=mrbkap, r=mt
CLOBBER
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/tests/mochitest/test_getUserMedia_constraints.html
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineCameraVideoSource.cpp
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineDefault.cpp
dom/media/webrtc/MediaEngineDefault.h
dom/media/webrtc/MediaEngineGonkVideoSource.cpp
dom/media/webrtc/MediaEngineGonkVideoSource.h
dom/media/webrtc/MediaEngineTabVideoSource.cpp
dom/media/webrtc/MediaEngineTabVideoSource.h
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
dom/media/webrtc/MediaEngineWebRTCVideo.cpp
dom/media/webrtc/MediaTrackConstraints.cpp
dom/media/webrtc/MediaTrackConstraints.h
dom/media/webrtc/moz.build
dom/webidl/Constraints.webidl
dom/webidl/MediaStreamTrack.webidl
dom/webidl/MediaTrackConstraintSet.webidl
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # 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 879861 - Touch CLOBBER because adding a new IDL is a crapshoot these days.
+Bug 1119335 - (DOMString or sequence<DOMString> or ConstrainDOMStringParameters)
+needs binding flush (Bug 1103153).
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -94,19 +94,19 @@ GetMediaManagerLog()
     sLog = PR_NewLogModule("MediaManager");
   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::MediaTrackConstraintSet;        // Mandatory or optional constraints
-using dom::MediaTrackConstraints;          // Raw mMandatory (as JSObject)
+using dom::MediaStreamConstraints;
+using dom::MediaTrackConstraintSet;
+using dom::MediaTrackConstraints;
 using dom::MediaStreamError;
 using dom::GetUserMediaRequest;
 using dom::Sequence;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using dom::SupportedAudioConstraints;
 using dom::SupportedVideoConstraints;
 
 static bool
@@ -426,56 +426,65 @@ VideoDevice::VideoDevice(MediaEngineVide
 
 /**
  * Helper functions that implement the constraints algorithm from
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
  */
 
 // Reminder: add handling for new constraints both here and in GetSources below!
 
-bool
-VideoDevice::SatisfiesConstraintSets(
+uint32_t
+VideoDevice::GetBestFitnessDistance(
     const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
   // Interrogate device-inherent properties first.
   for (size_t i = 0; i < aConstraintSets.Length(); i++) {
     auto& c = *aConstraintSets[i];
-    if (c.mFacingMode.WasPassed()) {
-      auto& value = c.mFacingMode.Value();
-      nsString s;
-      GetFacingMode(s);
-      if (value.IsString()) {
-        if (s != value.GetAsString()) {
-          return false;
+    if (!c.mFacingMode.IsConstrainDOMStringParameters() ||
+        c.mFacingMode.GetAsConstrainDOMStringParameters().mExact.WasPassed()) {
+      nsString deviceFacingMode;
+      GetFacingMode(deviceFacingMode);
+      if (c.mFacingMode.IsString()) {
+        if (c.mFacingMode.GetAsString() != deviceFacingMode) {
+          return UINT32_MAX;
+        }
+      } else if (c.mFacingMode.IsStringSequence()) {
+        if (!c.mFacingMode.GetAsStringSequence().Contains(deviceFacingMode)) {
+          return UINT32_MAX;
         }
       } else {
-        if (!value.GetAsStringSequence().Contains(s)) {
-          return false;
+        auto& exact = c.mFacingMode.GetAsConstrainDOMStringParameters().mExact.Value();
+        if (exact.IsString()) {
+          if (exact.GetAsString() != deviceFacingMode) {
+            return UINT32_MAX;
+          }
+        } else if (!exact.GetAsStringSequence().Contains(deviceFacingMode)) {
+          return UINT32_MAX;
         }
       }
     }
     nsString s;
     GetMediaSource(s);
     if (s != c.mMediaSource) {
-      return false;
+      return UINT32_MAX;
     }
   }
   // Forward request to underlying object to interrogate per-mode capabilities.
-  return GetSource()->SatisfiesConstraintSets(aConstraintSets);
+  return GetSource()->GetBestFitnessDistance(aConstraintSets);
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
-bool
-AudioDevice::SatisfiesConstraintSets(
+uint32_t
+AudioDevice::GetBestFitnessDistance(
     const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
   // TODO: Add audio-specific constraints
-  return true;
+  return 0;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
 {
   aName.Assign(mName);
   return NS_OK;
 }
@@ -956,17 +965,20 @@ static void
   typedef nsTArray<nsRefPtr<DeviceType>> SourceSet;
 
   nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<typename DeviceType::Source> > sources;
 
-    (engine->*aEnumerate)(aConstraints.mMediaSourceEnumValue, &sources);
+    MediaSourceEnum src = StringToEnum(dom::MediaSourceEnumValues::strings,
+                                       aConstraints.mMediaSource,
+                                       dom::MediaSourceEnum::Other);
+    (engine->*aEnumerate)(src, &sources);
     /**
       * We're allowing multiple tabs to access the same camera for parity
       * with Chrome.  See bug 811757 for some of the issues surrounding
       * this decision.  To disallow, we'd filter by IsAvailable() as we used
       * to.
       */
     for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
       sources[i]->GetName(deviceName);
@@ -979,96 +991,56 @@ static void
         candidateSet.AppendElement(new DeviceType(sources[i]));
       }
     }
   }
 
   // Apply constraints to the list of sources.
 
   auto& c = aConstraints;
-  if (c.mUnsupportedRequirement) {
-    // 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, SatisfiesConstraintSets is reused for the advanced algorithm
-    // where the spec requires it to ignore constraints of the other type)
-    return;
-  }
 
-  // Now on to the actual algorithm: First apply required constraints.
+  // First apply top-level constraints.
 
   // Stack constraintSets that pass, starting with the required one, because the
   // whole stack must be re-satisfied each time a capability-set is ruled out
-  // (this avoids storing state and pushing algorithm into the lower-level code).
+  // (this avoids storing state or pushing algorithm into the lower-level code).
   nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
-  aggregateConstraints.AppendElement(&c.mRequired);
+  aggregateConstraints.AppendElement(&c);
 
   for (uint32_t i = 0; i < candidateSet.Length();) {
-    // Overloading instead of template specialization keeps things local
-    if (!candidateSet[i]->SatisfiesConstraintSets(aggregateConstraints)) {
+    if (candidateSet[i]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
-  // TODO(jib): Proper non-ordered handling of nonrequired constraints (907352)
-  //
-  // For now, put nonrequired constraints at tail of Advanced list.
-  // This isn't entirely accurate, as order will matter, but few will notice
-  // the difference until we get camera selection and a few more constraints.
-  if (c.mNonrequired.Length()) {
-    if (!c.mAdvanced.WasPassed()) {
-      c.mAdvanced.Construct();
-    }
-    c.mAdvanced.Value().MoveElementsFrom(c.mNonrequired);
-  }
-
-  // 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;
+  // Then apply advanced constraints.
 
   if (c.mAdvanced.WasPassed()) {
     auto &array = c.mAdvanced.Value();
 
     for (int i = 0; i < int(array.Length()); i++) {
       aggregateConstraints.AppendElement(&array[i]);
       SourceSet rejects;
       for (uint32_t j = 0; j < candidateSet.Length();) {
-        if (!candidateSet[j]->SatisfiesConstraintSets(aggregateConstraints)) {
+        if (candidateSet[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
           rejects.AppendElement(candidateSet[j]);
           candidateSet.RemoveElementAt(j);
         } else {
           ++j;
         }
       }
-      (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
       if (!candidateSet.Length()) {
+        candidateSet.MoveElementsFrom(rejects);
         aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
       }
     }
   }
-
-  // TODO: Proper non-ordered handling of nonrequired constraints (Bug 907352)
-
   aResult.MoveElementsFrom(candidateSet);
-  aResult.MoveElementsFrom(tailSet);
 }
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia is called, and the results are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
@@ -1223,33 +1195,31 @@ public:
   }
 
   nsresult
   SelectDevice(MediaEngine* backend)
   {
     MOZ_ASSERT(mOnSuccess);
     MOZ_ASSERT(mOnFailure);
     if (IsOn(mConstraints.mVideo)) {
-      VideoTrackConstraintsN constraints(GetInvariant(mConstraints.mVideo));
       nsTArray<nsRefPtr<VideoDevice>> sources;
-      GetSources(backend, constraints, &MediaEngine::EnumerateVideoDevices, sources);
-
+      GetSources(backend, GetInvariant(mConstraints.mVideo),
+                 &MediaEngine::EnumerateVideoDevices, sources);
       if (!sources.Length()) {
         Fail(NS_LITERAL_STRING("NotFoundError"));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mVideoDevice = sources[0];
       LOG(("Selected video device"));
     }
     if (IsOn(mConstraints.mAudio)) {
-      AudioTrackConstraintsN constraints(GetInvariant(mConstraints.mAudio));
       nsTArray<nsRefPtr<AudioDevice>> sources;
-      GetSources(backend, constraints, &MediaEngine::EnumerateAudioDevices, sources);
-
+      GetSources(backend, GetInvariant(mConstraints.mAudio),
+                 &MediaEngine::EnumerateAudioDevices, sources);
       if (!sources.Length()) {
         Fail(NS_LITERAL_STRING("NotFoundError"));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mAudioDevice = sources[0];
       LOG(("Selected audio device"));
     }
@@ -1377,29 +1347,27 @@ public:
       backend = new MediaEngineDefault(mConstraints.mFakeTracks);
     else
       backend = mManager->GetBackend(mWindowId);
 
     typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
 
     ScopedDeletePtr<SourceSet> final(new SourceSet);
     if (IsOn(mConstraints.mVideo)) {
-      VideoTrackConstraintsN constraints(GetInvariant(mConstraints.mVideo));
       nsTArray<nsRefPtr<VideoDevice>> s;
-      GetSources(backend, constraints, &MediaEngine::EnumerateVideoDevices, s,
-                 mLoopbackVideoDevice.get());
+      GetSources(backend, GetInvariant(mConstraints.mVideo),
+                 &MediaEngine::EnumerateVideoDevices, s, mLoopbackVideoDevice.get());
       for (uint32_t i = 0; i < s.Length(); i++) {
         final->AppendElement(s[i]);
       }
     }
     if (IsOn(mConstraints.mAudio)) {
-      AudioTrackConstraintsN constraints(GetInvariant(mConstraints.mAudio));
       nsTArray<nsRefPtr<AudioDevice>> s;
-      GetSources(backend, constraints, &MediaEngine::EnumerateAudioDevices, s,
-                 mLoopbackAudioDevice.get());
+      GetSources(backend, GetInvariant(mConstraints.mAudio),
+                 &MediaEngine::EnumerateAudioDevices, s, mLoopbackAudioDevice.get());
       for (uint32_t i = 0; i < s.Length(); i++) {
         final->AppendElement(s[i]);
       }
     }
 
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
                                                               mOnSuccess, mOnFailure,
                                                               final.forget()));
@@ -1624,33 +1592,16 @@ MediaManager::GetUserMedia(
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     privileged = true;
   }
   if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
     c.mVideo.SetAsBoolean() = false;
   }
 
-  if (c.mVideo.IsMediaTrackConstraints() && !privileged) {
-    auto& tc = c.mVideo.GetAsMediaTrackConstraints();
-    // only allow privileged content to set the window id
-    if (tc.mBrowserWindow.WasPassed()) {
-      tc.mBrowserWindow.Construct(-1);
-    }
-
-    if (tc.mAdvanced.WasPassed()) {
-      size_t length = tc.mAdvanced.Value().Length();
-      for (size_t i = 0; i < length; i++) {
-        if (tc.mAdvanced.Value()[i].mBrowserWindow.WasPassed()) {
-          tc.mAdvanced.Value()[i].mBrowserWindow.Construct(-1);
-        }
-      }
-    }
-  }
-
   // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
   nsAutoPtr<GetUserMediaTask> task;
   if (c.mFake) {
     // Fake stream from default backend.
     task = new GetUserMediaTask(c, onSuccess.forget(),
       onFailure.forget(), windowID, listener, mPrefs, new MediaEngineDefault(c.mFakeTracks));
   } else {
     // Stream from default device from WebRTC backend.
@@ -1666,22 +1617,47 @@ MediaManager::GetUserMedia(
   NS_ENSURE_SUCCESS(rv, rv);
   rv = docURI->EqualsExceptRef(loopURI, &isLoop);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (isLoop) {
     privileged = true;
   }
 
+  if (c.mVideo.IsMediaTrackConstraints()) {
+    auto& vc = c.mVideo.GetAsMediaTrackConstraints();
+    MediaSourceEnum src = StringToEnum(dom::MediaSourceEnumValues::strings,
+                                       vc.mMediaSource,
+                                       dom::MediaSourceEnum::Other);
+    if (vc.mAdvanced.WasPassed()) {
+      if (src != dom::MediaSourceEnum::Camera) {
+        // iterate through advanced, forcing mediaSource to match "root"
+        const char *camera = EnumToASCII(dom::MediaSourceEnumValues::strings,
+                                         dom::MediaSourceEnum::Camera);
+        for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
+          if (cs.mMediaSource.EqualsASCII(camera)) {
+            cs.mMediaSource = vc.mMediaSource;
+          }
+        }
+      }
+    }
+    if (!privileged) {
+      // only allow privileged content to set the window id
+      if (vc.mBrowserWindow.WasPassed()) {
+        vc.mBrowserWindow.Construct(-1);
+      }
+      if (vc.mAdvanced.WasPassed()) {
+        for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
+          if (cs.mBrowserWindow.WasPassed()) {
+            cs.mBrowserWindow.Construct(-1);
+          }
+        }
+      }
+    }
 
-  if (c.mVideo.IsMediaTrackConstraints()) {
-    auto& tc = c.mVideo.GetAsMediaTrackConstraints();
-    MediaSourceEnum src = StringToEnum(dom::MediaSourceEnumValues::strings,
-                                       tc.mMediaSource,
-                                       dom::MediaSourceEnum::Other);
     switch (src) {
     case dom::MediaSourceEnum::Camera:
       break;
 
     case dom::MediaSourceEnum::Browser:
     case dom::MediaSourceEnum::Screen:
     case dom::MediaSourceEnum::Application:
     case dom::MediaSourceEnum::Window:
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -518,29 +518,29 @@ protected:
 class VideoDevice : public MediaDevice
 {
 public:
   typedef MediaEngineVideoSource Source;
 
   explicit VideoDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
-  bool SatisfiesConstraintSets(
+  uint32_t GetBestFitnessDistance(
     const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
 };
 
 class AudioDevice : public MediaDevice
 {
 public:
   typedef MediaEngineAudioSource Source;
 
   explicit AudioDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
-  bool SatisfiesConstraintSets(
+  uint32_t GetBestFitnessDistance(
     const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
 };
 
 // we could add MediaManager if needed
 typedef void (*WindowListenerCallback)(MediaManager *aThis,
                                        uint64_t aWindowID,
                                        StreamListeners *aListeners,
                                        void *aData);
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -10,68 +10,68 @@
 createHTML({ title: "Test getUserMedia constraints", bug: "882145" });
 /**
   Tests covering gUM constraints API for audio, video and fake video. Exercise
   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"] },
+  { message: "unknown required constraint on video ignored",
+    constraints: { video: { somethingUnknown: { exact: 0 } },
                    fake: true },
-    error: "NotFoundError" },
-  { message: "unknown required constraint on audio fails",
-    constraints: { audio: { somethingUnknown:0, require:["somethingUnknown"] },
+    error: null },
+  { message: "unknown required constraint on audio ignored",
+    constraints: { audio: { somethingUnknown: { exact: 0 } },
                    fake: true },
-    error: "NotFoundError" },
+    error: null },
   { message: "video overconstrained by facingMode fails",
-    constraints: { video: { facingMode:'left', require:["facingMode"] },
+    constraints: { video: { facingMode:{ exact: 'left' } },
                    fake: true },
     error: "NotFoundError" },
   { message: "video overconstrained by facingMode array fails",
-    constraints: { video: { facingMode:['left', 'right'], require:["facingMode"] },
+    constraints: { video: { facingMode:{ exact: ['left', 'right'] } },
                    fake: true },
     error: "NotFoundError" },
-  { message: "audio overconstrained by facingMode fails",
-    constraints: { audio: { facingMode:'left', require:["facingMode"] },
+  { message: "audio overconstrained by facingMode ignored",
+    constraints: { audio: { facingMode: { exact: 'left' } },
                    fake: true },
-    error: "NotFoundError" },
+    error: null },
   { message: "full screensharing requires permission",
-    constraints: { video: { mediaSource:'screen' } },
+    constraints: { video: { mediaSource: 'screen' } },
     error: "PermissionDeniedError" },
   { message: "application screensharing requires permission",
-    constraints: { video: { mediaSource:'application' } },
+    constraints: { video: { mediaSource: 'application' } },
     error: "PermissionDeniedError" },
   { message: "window screensharing requires permission",
-    constraints: { video: { mediaSource:'window' } },
+    constraints: { video: { mediaSource: 'window' } },
     error: "PermissionDeniedError" },
   { message: "browser screensharing requires permission",
-    constraints: { video: { mediaSource:'browser' } },
+    constraints: { video: { mediaSource: 'browser' } },
     error: "PermissionDeniedError" },
   { message: "unknown mediaSource fails",
-    constraints: { video: { mediaSource:'uncle' } },
+    constraints: { video: { mediaSource: 'uncle' } },
     error: "NotFoundError" },
   { message: "Success-path: optional video facingMode + audio ignoring facingMode",
     constraints: { fake: true,
-                   audio: { mediaSource:'microphone',
-                            facingMode:'left',
-                            foo:0,
-                            advanced: [{ facingMode:'environment' },
-                                       { facingMode:'user' },
-                                       { bar:0 }] },
-                   video: { mediaSource:'camera',
-                            facingMode:['left', 'right', 'user', 'environment'],
-                            foo:0,
-                            advanced: [{ facingMode:'environment' },
-                                       { facingMode:['user'] },
-                                       { bar:0 }] } },
+                   audio: { mediaSource: 'microphone',
+                            facingMode: 'left',
+                            foo: 0,
+                            advanced: [{ facingMode: 'environment' },
+                                       { facingMode: 'user' },
+                                       { bar: 0 }] },
+                   video: { mediaSource: 'camera',
+                            foo: 0,
+                            advanced: [{ facingMode: 'environment' },
+                                       { facingMode: ['user'] },
+                                       { facingMode: ['left', 'right', 'user'] },
+                                       { bar: 0 }] } },
     error: null },
   { message: "legacy facingMode ignored",
-    constraints: { video: { mandatory: { facingMode:'left' } }, fake: true },
+    constraints: { video: { mandatory: { facingMode: 'left' } }, fake: true },
     error: null },
 ];
 
 /**
  * Starts the test run by running through each constraint
  * test by verifying that the right resolution and rejection is fired.
  */
 
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -13,19 +13,16 @@
 #include "mozilla/dom/VideoStreamTrack.h"
 
 namespace mozilla {
 
 namespace dom {
 class File;
 }
 
-struct VideoTrackConstraintsN;
-struct AudioTrackConstraintsN;
-
 /**
  * Abstract interface for managing audio and video devices. Each platform
  * must implement a concrete class that will map these classes and methods
  * to the appropriate backend. For example, on Desktop platforms, these will
  * correspond to equivalent webrtc (GIPS) calls, and on B2G they will map to
  * a Gonk interface.
  */
 class MediaEngineVideoSource;
@@ -214,20 +211,20 @@ private:
 };
 
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 
   /* This call reserves but does not start the device. */
-  virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
 
-  virtual bool SatisfiesConstraintSets(
+  virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) = 0;
 
 protected:
   explicit MediaEngineVideoSource(MediaEngineState aState)
     : MediaEngineSource(aState) {}
   MediaEngineVideoSource()
     : MediaEngineSource(kReleased) {}
 };
@@ -236,17 +233,17 @@ protected:
  * Audio source and friends.
  */
 class MediaEngineAudioSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineAudioSource() {}
 
   /* This call reserves but does not start the device. */
-  virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
 protected:
   explicit MediaEngineAudioSource(MediaEngineState aState)
     : MediaEngineSource(aState) {}
   MediaEngineAudioSource()
     : MediaEngineSource(kReleased) {}
 
 };
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -1,58 +1,34 @@
 /* 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 "MediaEngineCameraVideoSource.h"
 
+#include <limits>
+
 namespace mozilla {
 
 using namespace mozilla::gfx;
+using dom::OwningLongOrConstrainLongRange;
 using dom::ConstrainLongRange;
+using dom::OwningDoubleOrConstrainDoubleRange;
 using dom::ConstrainDoubleRange;
 using dom::MediaTrackConstraintSet;
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaManagerLog();
 #define LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
 #define LOGFRAME(msg) PR_LOG(GetMediaManagerLog(), 6, msg)
 #else
 #define LOG(msg)
 #define LOGFRAME(msg)
 #endif
 
-/* static */ bool
-MediaEngineCameraVideoSource::IsWithin(int32_t n, const ConstrainLongRange& aRange) {
-  return aRange.mMin <= n && n <= aRange.mMax;
-}
-
-/* static */ bool
-MediaEngineCameraVideoSource::IsWithin(double n, const ConstrainDoubleRange& aRange) {
-  return aRange.mMin <= n && n <= aRange.mMax;
-}
-
-/* static */ int32_t
-MediaEngineCameraVideoSource::Clamp(int32_t n, const ConstrainLongRange& aRange) {
-  return std::max(aRange.mMin, std::min(n, aRange.mMax));
-}
-
-/* static */ bool
-MediaEngineCameraVideoSource::AreIntersecting(const ConstrainLongRange& aA, const ConstrainLongRange& aB) {
-  return aA.mMax >= aB.mMin && aA.mMin <= aB.mMax;
-}
-
-/* static */ bool
-MediaEngineCameraVideoSource::Intersect(ConstrainLongRange& aA, const ConstrainLongRange& aB) {
-  MOZ_ASSERT(AreIntersecting(aA, aB));
-  aA.mMin = std::max(aA.mMin, aB.mMin);
-  aA.mMax = std::min(aA.mMax, aB.mMax);
-  return true;
-}
-
 // guts for appending data to the MSG track
 bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource,
                                                  layers::Image* aImage,
                                                  TrackID aID,
                                                  StreamTime delta)
 {
   MOZ_ASSERT(aSource);
 
@@ -82,163 +58,237 @@ MediaEngineCameraVideoSource::GetCapabil
 {
   MOZ_ASSERT(aIndex < mHardcodedCapabilities.Length());
   aOut = mHardcodedCapabilities[aIndex];
 }
 
 // The full algorithm for all cameras. Sources that don't list capabilities
 // need to fake it and hardcode some by populating mHardcodedCapabilities above.
 
-/*static*/
-bool
-MediaEngineCameraVideoSource::SatisfiesConstraintSet(const MediaTrackConstraintSet &aConstraints,
-                                                     const webrtc::CaptureCapability& aCandidate) {
-  if (!IsWithin(aCandidate.width, aConstraints.mWidth) ||
-      !IsWithin(aCandidate.height, aConstraints.mHeight)) {
-    return false;
+// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
+
+template<class ValueType, class ConstrainRange>
+/* static */ uint32_t
+MediaEngineCameraVideoSource::FitnessDistance(ValueType n,
+                                              const ConstrainRange& aRange)
+{
+  if ((aRange.mExact.WasPassed() && aRange.mExact.Value() != n) ||
+      (aRange.mMin.WasPassed() && aRange.mMin.Value() > n) ||
+      (aRange.mMax.WasPassed() && aRange.mMax.Value() < n)) {
+    return UINT32_MAX;
+  }
+  if (!aRange.mIdeal.WasPassed() || n == aRange.mIdeal.Value()) {
+    return 0;
   }
-  if (!IsWithin(aCandidate.maxFPS, aConstraints.mFrameRate)) {
-    return false;
+  return uint32_t(ValueType((std::abs(n - aRange.mIdeal.Value()) * 1000) /
+                            std::max(std::abs(n), std::abs(aRange.mIdeal.Value()))));
+}
+
+// Binding code doesn't templatize well...
+
+template<>
+/* static */ uint32_t
+MediaEngineCameraVideoSource::FitnessDistance(int32_t n,
+    const OwningLongOrConstrainLongRange& aConstraint)
+{
+  if (aConstraint.IsLong()) {
+    ConstrainLongRange range;
+    range.mIdeal.Construct(aConstraint.GetAsLong());
+    return FitnessDistance(n, range);
+  } else {
+    return FitnessDistance(n, aConstraint.GetAsConstrainLongRange());
   }
-  return true;
 }
 
-// SatisfiesConstraintSets (plural) answers for the capture device as a whole
-// whether it can satisfy an accumulated number of capabilitySets.
+template<>
+/* static */ uint32_t
+MediaEngineCameraVideoSource::FitnessDistance(double n,
+    const OwningDoubleOrConstrainDoubleRange& aConstraint)
+{
+  if (aConstraint.IsDouble()) {
+    ConstrainDoubleRange range;
+    range.mIdeal.Construct(aConstraint.GetAsDouble());
+    return FitnessDistance(n, range);
+  } else {
+    return FitnessDistance(n, aConstraint.GetAsConstrainDoubleRange());
+  }
+}
+
+/*static*/ uint32_t
+MediaEngineCameraVideoSource::GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
+                                                 const MediaTrackConstraintSet &aConstraints)
+{
+  uint64_t distance =
+    uint64_t(FitnessDistance(int32_t(aCandidate.width), aConstraints.mWidth)) +
+    uint64_t(FitnessDistance(int32_t(aCandidate.height), aConstraints.mHeight)) +
+    uint64_t(FitnessDistance(double(aCandidate.maxFPS), aConstraints.mFrameRate));
+  return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
+}
+
+// Find best capability by removing inferiors. May leave >1 of equal distance
 
-bool
-MediaEngineCameraVideoSource::SatisfiesConstraintSets(
+/* static */ void
+MediaEngineCameraVideoSource::TrimLessFitCandidates(CapabilitySet& set) {
+  uint32_t best = UINT32_MAX;
+  for (auto& candidate : set) {
+    if (best > candidate.mDistance) {
+      best = candidate.mDistance;
+    }
+  }
+  for (size_t i = 0; i < set.Length();) {
+    if (set[i].mDistance > best) {
+      set.RemoveElementAt(i);
+    } else {
+      ++i;
+    }
+  }
+  MOZ_ASSERT(set.Length());
+}
+
+// GetBestFitnessDistance returns the best distance the capture device can offer
+// as a whole, given an accumulated number of ConstraintSets.
+// Ideal values are considered in the first ConstraintSet only.
+// Infinity = UINT32_MAX e.g. device cannot satisfy accumulated ConstraintSets.
+// A finite result may be used to calculate this device's ranking as a choice.
+
+uint32_t
+MediaEngineCameraVideoSource::GetBestFitnessDistance(
     const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
   size_t num = NumCapabilities();
 
   CapabilitySet candidateSet;
   for (size_t i = 0; i < num; i++) {
     candidateSet.AppendElement(i);
   }
 
+  bool first = true;
   for (const MediaTrackConstraintSet* cs : aConstraintSets) {
     for (size_t i = 0; i < candidateSet.Length();  ) {
+      auto& candidate = candidateSet[i];
       webrtc::CaptureCapability cap;
-      GetCapability(candidateSet[i], cap);
-      if (!SatisfiesConstraintSet(*cs, cap)) {
+      GetCapability(candidate.mIndex, cap);
+      uint32_t distance = GetFitnessDistance(cap, *cs);
+      if (distance == UINT32_MAX) {
         candidateSet.RemoveElementAt(i);
       } else {
         ++i;
+        if (first) {
+          candidate.mDistance = distance;
+        }
       }
     }
+    first = false;
   }
-  return !!candidateSet.Length();
+  if (!candidateSet.Length()) {
+    return UINT32_MAX;
+  }
+  TrimLessFitCandidates(candidateSet);
+  return candidateSet[0].mDistance;
 }
 
-void
+bool
 MediaEngineCameraVideoSource::ChooseCapability(
-    const VideoTrackConstraintsN &aConstraints,
+    const dom::MediaTrackConstraints &aConstraints,
     const MediaEnginePrefs &aPrefs)
 {
   LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
        aPrefs.mWidth, aPrefs.mHeight, aPrefs.mFPS, aPrefs.mMinFPS));
 
   size_t num = NumCapabilities();
 
   CapabilitySet candidateSet;
   for (size_t i = 0; i < num; i++) {
     candidateSet.AppendElement(i);
   }
 
-  // Pick among capabilities: First apply required constraints.
+  // First, filter capabilities by required constraints (min, max, exact).
 
   for (size_t i = 0; i < candidateSet.Length();) {
+    auto& candidate = candidateSet[i];
     webrtc::CaptureCapability cap;
-    GetCapability(candidateSet[i], cap);
-    if (!SatisfiesConstraintSet(aConstraints.mRequired, cap)) {
+    GetCapability(candidate.mIndex, cap);
+    candidate.mDistance = GetFitnessDistance(cap, aConstraints);
+    if (candidate.mDistance == UINT32_MAX) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
-  CapabilitySet tailSet;
-
-  // Then apply advanced (formerly known as optional) constraints.
+  // Filter further with all advanced constraints (that don't overconstrain).
 
   if (aConstraints.mAdvanced.WasPassed()) {
     for (const MediaTrackConstraintSet &cs : aConstraints.mAdvanced.Value()) {
       CapabilitySet rejects;
       for (size_t i = 0; i < candidateSet.Length();) {
+        auto& candidate = candidateSet[i];
         webrtc::CaptureCapability cap;
-        GetCapability(candidateSet[i], cap);
-        if (!SatisfiesConstraintSet(cs, cap)) {
-          rejects.AppendElement(candidateSet[i]);
+        GetCapability(candidate.mIndex, cap);
+        if (GetFitnessDistance(cap, cs) == UINT32_MAX) {
+          rejects.AppendElement(candidate);
           candidateSet.RemoveElementAt(i);
         } else {
           ++i;
         }
       }
-      (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
-    }
-  }
-
-  if (!candidateSet.Length()) {
-    candidateSet.AppendElement(0);
-  }
-
-  int prefWidth = aPrefs.GetWidth();
-  int prefHeight = aPrefs.GetHeight();
-
-  // Default is closest to available capability but equal to or below;
-  // otherwise closest above.  Since we handle the num=0 case above and
-  // take the first entry always, we can never exit uninitialized.
-
-  webrtc::CaptureCapability cap;
-  bool higher = true;
-  for (size_t i = 0; i < candidateSet.Length(); i++) {
-    GetCapability(candidateSet[i], cap);
-    if (higher) {
-      if (i == 0 ||
-          (mCapability.width > cap.width && mCapability.height > cap.height)) {
-        // closer than the current choice
-        mCapability = cap;
-        // FIXME: expose expected capture delay?
-      }
-      if (cap.width <= (uint32_t) prefWidth && cap.height <= (uint32_t) prefHeight) {
-        higher = false;
-      }
-    } else {
-      if (cap.width > (uint32_t) prefWidth || cap.height > (uint32_t) prefHeight ||
-          cap.maxFPS < (uint32_t) aPrefs.mMinFPS) {
-        continue;
-      }
-      if (mCapability.width < cap.width && mCapability.height < cap.height) {
-        mCapability = cap;
-        // FIXME: expose expected capture delay?
-      }
-    }
-    // Same resolution, maybe better format or FPS match
-    if (mCapability.width == cap.width && mCapability.height == cap.height) {
-      // FPS too low
-      if (cap.maxFPS < (uint32_t) aPrefs.mMinFPS) {
-        continue;
-      }
-      // Better match
-      if (cap.maxFPS < mCapability.maxFPS) {
-        mCapability = cap;
-      } else if (cap.maxFPS == mCapability.maxFPS) {
-        // Resolution and FPS the same, check format
-        if (cap.rawType == webrtc::RawVideoType::kVideoI420
-          || cap.rawType == webrtc::RawVideoType::kVideoYUY2
-          || cap.rawType == webrtc::RawVideoType::kVideoYV12) {
-          mCapability = cap;
-        }
+      if (!candidateSet.Length()) {
+        candidateSet.MoveElementsFrom(rejects);
       }
     }
   }
+  if (!candidateSet.Length()) {
+    LOG(("failed to find capability match from %d choices",num));
+    return false;
+  }
+
+  // Remaining algorithm is up to the UA.
+
+  TrimLessFitCandidates(candidateSet);
+
+  // Any remaining multiples all have the same distance. A common case of this
+  // occurs when no ideal is specified. Lean toward defaults.
+  {
+    MediaTrackConstraintSet prefs;
+    prefs.mWidth.SetAsLong() = aPrefs.GetWidth();
+    prefs.mHeight.SetAsLong() = aPrefs.GetHeight();
+    prefs.mFrameRate.SetAsDouble() = aPrefs.mMinFPS;
+
+    for (auto& candidate : candidateSet) {
+      webrtc::CaptureCapability cap;
+      GetCapability(candidate.mIndex, cap);
+      candidate.mDistance = GetFitnessDistance(cap, prefs);
+    }
+    TrimLessFitCandidates(candidateSet);
+  }
+
+  // Any remaining multiples all have the same distance, but may vary on
+  // format. Some formats are more desirable for certain use like WebRTC.
+  // E.g. I420 over RGB24 can remove a needless format conversion.
+
+  bool found = false;
+  for (auto& candidate : candidateSet) {
+    webrtc::CaptureCapability cap;
+    GetCapability(candidate.mIndex, cap);
+    if (cap.rawType == webrtc::RawVideoType::kVideoI420 ||
+        cap.rawType == webrtc::RawVideoType::kVideoYUY2 ||
+        cap.rawType == webrtc::RawVideoType::kVideoYV12) {
+      mCapability = cap;
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    GetCapability(candidateSet[0].mIndex, mCapability);
+  }
+
   LOG(("chose cap %dx%d @%dfps codec %d raw %d",
        mCapability.width, mCapability.height, mCapability.maxFPS,
        mCapability.codecType, mCapability.rawType));
+  return true;
 }
 
 void
 MediaEngineCameraVideoSource::GetName(nsAString& aName)
 {
   aName = mDeviceName;
 }
 
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -53,41 +53,44 @@ public:
       return dom::MediaSourceEnum::Camera;
   }
 
   virtual nsresult TakePhoto(PhotoCallback* aCallback) MOZ_OVERRIDE
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  bool SatisfiesConstraintSets(
+  uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) MOZ_OVERRIDE;
 
 protected:
-  typedef nsTArray<size_t> CapabilitySet;
+  struct CapabilityCandidate {
+    CapabilityCandidate(uint8_t index, uint32_t distance = 0)
+    : mIndex(index), mDistance(distance) {}
+
+    size_t mIndex;
+    uint32_t mDistance;
+  };
+  typedef nsTArray<CapabilityCandidate> CapabilitySet;
 
   ~MediaEngineCameraVideoSource() {}
 
   // guts for appending data to the MSG track
   virtual bool AppendToTrack(SourceMediaStream* aSource,
                              layers::Image* aImage,
                              TrackID aID,
                              StreamTime delta);
-
-  static bool IsWithin(int32_t n, const dom::ConstrainLongRange& aRange);
-  static bool IsWithin(double n, const dom::ConstrainDoubleRange& aRange);
-  static int32_t Clamp(int32_t n, const dom::ConstrainLongRange& aRange);
-  static bool AreIntersecting(const dom::ConstrainLongRange& aA,
-                              const dom::ConstrainLongRange& aB);
-  static bool Intersect(dom::ConstrainLongRange& aA, const dom::ConstrainLongRange& aB);
-  static bool SatisfiesConstraintSet(const dom::MediaTrackConstraintSet& aConstraints,
-                                     const webrtc::CaptureCapability& aCandidate);
+  template<class ValueType, class ConstrainRange>
+  static uint32_t FitnessDistance(ValueType n, const ConstrainRange& aRange);
+  static uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
+                                     const dom::MediaTrackConstraintSet &aConstraints);
+  static void TrimLessFitCandidates(CapabilitySet& set);
   virtual size_t NumCapabilities();
   virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut);
-  void ChooseCapability(const VideoTrackConstraintsN &aConstraints,
+  bool ChooseCapability(const dom::MediaTrackConstraints &aConstraints,
                         const MediaEnginePrefs &aPrefs);
 
   // Engine variables.
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).
   // mMonitor also protects mSources[] access/changes.
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -64,17 +64,17 @@ MediaEngineDefaultVideoSource::GetName(n
 void
 MediaEngineDefaultVideoSource::GetUUID(nsAString& aUUID)
 {
   aUUID.AssignLiteral(MOZ_UTF16("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"));
   return;
 }
 
 nsresult
-MediaEngineDefaultVideoSource::Allocate(const VideoTrackConstraintsN &aConstraints,
+MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs)
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
   mOpts = aPrefs;
   mOpts.mWidth = mOpts.mWidth ? mOpts.mWidth : MediaEngine::DEFAULT_43_VIDEO_WIDTH;
@@ -344,17 +344,17 @@ MediaEngineDefaultAudioSource::GetName(n
 void
 MediaEngineDefaultAudioSource::GetUUID(nsAString& aUUID)
 {
   aUUID.AssignLiteral(MOZ_UTF16("B7CBD7C1-53EF-42F9-8353-73F61C70C092"));
   return;
 }
 
 nsresult
-MediaEngineDefaultAudioSource::Allocate(const AudioTrackConstraintsN &aConstraints,
+MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs)
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
   mState = kAllocated;
   // generate 1Khz sine wave
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -35,31 +35,31 @@ class MediaEngineDefaultVideoSource : pu
                                       public MediaEngineVideoSource
 {
 public:
   MediaEngineDefaultVideoSource();
 
   virtual void GetName(nsAString&) MOZ_OVERRIDE;
   virtual void GetUUID(nsAString&) MOZ_OVERRIDE;
 
-  virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) MOZ_OVERRIDE;
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual void SetDirectListeners(bool aHasDirectListeners) MOZ_OVERRIDE {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) MOZ_OVERRIDE { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) MOZ_OVERRIDE;
-  virtual bool SatisfiesConstraintSets(
+  virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) MOZ_OVERRIDE
   {
     return true;
   }
 
   virtual bool IsFake() MOZ_OVERRIDE {
     return true;
   }
@@ -103,17 +103,17 @@ class MediaEngineDefaultAudioSource : pu
                                       public MediaEngineAudioSource
 {
 public:
   MediaEngineDefaultAudioSource();
 
   virtual void GetName(nsAString&) MOZ_OVERRIDE;
   virtual void GetUUID(nsAString&) MOZ_OVERRIDE;
 
-  virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) MOZ_OVERRIDE;
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual void SetDirectListeners(bool aHasDirectListeners) MOZ_OVERRIDE {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -146,17 +146,17 @@ MediaEngineGonkVideoSource::NumCapabilit
         mHardcodedCapabilities.AppendElement(c); // landscape
       }
     }
   }
   return mHardcodedCapabilities.Length();
 }
 
 nsresult
-MediaEngineGonkVideoSource::Allocate(const VideoTrackConstraintsN& aConstraints,
+MediaEngineGonkVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                      const MediaEnginePrefs& aPrefs)
 {
   LOG((__FUNCTION__));
 
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
   if (mState == kReleased && mInitDone) {
     ChooseCapability(aConstraints, aPrefs);
     NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineGonkVideoSource>(this),
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.h
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.h
@@ -55,17 +55,17 @@ public:
     , mCameraControl(nullptr)
     , mRotation(0)
     , mBackCamera(false)
     , mOrientationChanged(true) // Correct the orientation at first time takePhoto.
     {
       Init();
     }
 
-  virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) MOZ_OVERRIDE;
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) MOZ_OVERRIDE;
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) MOZ_OVERRIDE;
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -22,17 +22,16 @@
 #include "VideoUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrefService.h"
 #include "MediaTrackConstraints.h"
 
 namespace mozilla {
 
 using namespace mozilla::gfx;
-using dom::ConstrainLongRange;
 
 NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
 
 MediaEngineTabVideoSource::MediaEngineTabVideoSource()
 : mData(NULL), mDataSize(0), mMonitor("MediaEngineTabVideoSource"), mTabSource(nullptr)
 {
 }
 
@@ -111,55 +110,36 @@ MediaEngineTabVideoSource::GetName(nsASt
 
 void
 MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid)
 {
   aUuid.AssignLiteral(MOZ_UTF16("uuid"));
 }
 
 nsresult
-MediaEngineTabVideoSource::Allocate(const VideoTrackConstraintsN& aConstraints,
+MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                     const MediaEnginePrefs& aPrefs)
 {
-
-  ConstrainLongRange cWidth(aConstraints.mRequired.mWidth);
-  ConstrainLongRange cHeight(aConstraints.mRequired.mHeight);
+  // windowId and scrollWithPage are not proper constraints, so just read them.
+  // They have no well-defined behavior in advanced, so ignore them there.
 
-  mWindowId = aConstraints.mBrowserWindow.WasPassed() ? aConstraints.mBrowserWindow.Value() : -1;
-  bool haveScrollWithPage = aConstraints.mScrollWithPage.WasPassed();
-  mScrollWithPage =  haveScrollWithPage ? aConstraints.mScrollWithPage.Value() : true;
+  mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
+              aConstraints.mBrowserWindow.Value() : -1;
+  mScrollWithPage = aConstraints.mScrollWithPage.WasPassed() ?
+                    aConstraints.mScrollWithPage.Value() : true;
 
-  if (aConstraints.mAdvanced.WasPassed()) {
-    const auto& advanced = aConstraints.mAdvanced.Value();
-    for (uint32_t i = 0; i < advanced.Length(); i++) {
-      if (cWidth.mMax >= advanced[i].mWidth.mMin && cWidth.mMin <= advanced[i].mWidth.mMax &&
-         cHeight.mMax >= advanced[i].mHeight.mMin && cHeight.mMin <= advanced[i].mHeight.mMax) {
-        cWidth.mMin = std::max(cWidth.mMin, advanced[i].mWidth.mMin);
-        cHeight.mMin = std::max(cHeight.mMin, advanced[i].mHeight.mMin);
-        cWidth.mMax = std::min(cWidth.mMax, advanced[i].mWidth.mMax);
-        cHeight.mMax = std::min(cHeight.mMax, advanced[i].mHeight.mMax);
-      }
+  FlattenedConstraints c(aConstraints);
 
-      if (mWindowId == -1 && advanced[i].mBrowserWindow.WasPassed()) {
-        mWindowId = advanced[i].mBrowserWindow.Value();
-      }
-
-      if (!haveScrollWithPage && advanced[i].mScrollWithPage.WasPassed()) {
-        mScrollWithPage = advanced[i].mScrollWithPage.Value();
-        haveScrollWithPage = true;
-      }
-    }
-  }
-
-  ConstrainLongRange defaultRange;
-
-  mBufWidthMax  = defaultRange.mMax > cWidth.mMax ? cWidth.mMax : aPrefs.GetWidth(false);
-  mBufHeightMax  = defaultRange.mMax > cHeight.mMax ? cHeight.mMax : aPrefs.GetHeight(false);
-
-  mTimePerFrame = aPrefs.mFPS ? 1000 / aPrefs.mFPS : aPrefs.mFPS;
+  mBufWidthMax = c.mWidth.Clamp(c.mWidth.mIdeal.WasPassed() ?
+                                c.mWidth.mIdeal.Value() : aPrefs.GetWidth(false));
+  mBufHeightMax = c.mHeight.Clamp(c.mHeight.mIdeal.WasPassed() ?
+                                  c.mHeight.mIdeal.Value() : aPrefs.GetHeight(false));
+  double frameRate = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.WasPassed() ?
+                                     c.mFrameRate.mIdeal.Value() : aPrefs.mFPS);
+  mTimePerFrame = std::max(10, int(1000.0 / (frameRate > 0? frameRate : 1)));
   return NS_OK;
 }
 
 nsresult
 MediaEngineTabVideoSource::Deallocate()
 {
   return NS_OK;
 }
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -15,32 +15,32 @@ class MediaEngineTabVideoSource : public
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDOMEVENTLISTENER
     NS_DECL_NSITIMERCALLBACK
     MediaEngineTabVideoSource();
 
     virtual void GetName(nsAString_internal&) MOZ_OVERRIDE;
     virtual void GetUUID(nsAString_internal&) MOZ_OVERRIDE;
-    virtual nsresult Allocate(const VideoTrackConstraintsN &,
+    virtual nsresult Allocate(const dom::MediaTrackConstraints &,
                               const mozilla::MediaEnginePrefs&) MOZ_OVERRIDE;
     virtual nsresult Deallocate() MOZ_OVERRIDE;
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) MOZ_OVERRIDE;
     virtual void SetDirectListeners(bool aHasDirectListeners) MOZ_OVERRIDE {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) MOZ_OVERRIDE;
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) MOZ_OVERRIDE;
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) MOZ_OVERRIDE;
     virtual bool IsFake() MOZ_OVERRIDE;
     virtual const dom::MediaSourceEnum GetMediaSource() MOZ_OVERRIDE {
       return dom::MediaSourceEnum::Browser;
     }
-    virtual bool SatisfiesConstraintSets(
+    virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) MOZ_OVERRIDE
     {
-      return true;
+      return 0;
     }
 
     virtual nsresult TakePhoto(PhotoCallback* aCallback) MOZ_OVERRIDE
     {
       return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     void Draw();
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -84,17 +84,17 @@ public:
     , mVideoEngine(aVideoEnginePtr)
     , mMinFps(-1)
     , mMediaSource(aMediaSource)
   {
     MOZ_ASSERT(aVideoEnginePtr);
     Init();
   }
 
-  virtual nsresult Allocate(const VideoTrackConstraintsN& aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                             const MediaEnginePrefs& aPrefs) MOZ_OVERRIDE;
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream*, TrackID) MOZ_OVERRIDE;
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) MOZ_OVERRIDE;
@@ -155,17 +155,17 @@ public:
     mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
     mDeviceUUID.Assign(NS_ConvertUTF8toUTF16(uuid));
     Init();
   }
 
   virtual void GetName(nsAString& aName) MOZ_OVERRIDE;
   virtual void GetUUID(nsAString& aUUID) MOZ_OVERRIDE;
 
-  virtual nsresult Allocate(const AudioTrackConstraintsN& aConstraints,
+  virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                             const MediaEnginePrefs& aPrefs) MOZ_OVERRIDE;
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) MOZ_OVERRIDE;
   virtual void SetDirectListeners(bool aHasDirectListeners) MOZ_OVERRIDE {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -260,17 +260,17 @@ MediaEngineWebRTCAudioSource::Config(boo
       0 != (error = mVoEProcessing->SetNsStatus(mNoiseOn, (webrtc::NsModes) aNoise))) {
       LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
     }
   }
   return NS_OK;
 }
 
 nsresult
-MediaEngineWebRTCAudioSource::Allocate(const AudioTrackConstraintsN &aConstraints,
+MediaEngineWebRTCAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                        const MediaEnginePrefs &aPrefs)
 {
   if (mState == kReleased) {
     if (mInitDone) {
       ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw(webrtc::VoEHardware::GetInterface(mVoiceEngine));
       if (!ptrVoEHw || ptrVoEHw->SetRecordingDevice(mCapIndex)) {
         return NS_ERROR_FAILURE;
       }
--- a/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -194,26 +194,27 @@ MediaEngineWebRTCVideoSource::GetCapabil
   if (!mHardcodedCapabilities.IsEmpty()) {
     MediaEngineCameraVideoSource::GetCapability(aIndex, aOut);
   }
   NS_ConvertUTF16toUTF8 uniqueId(mUniqueId); // TODO: optimize this?
   mViECapture->GetCaptureCapability(uniqueId.get(), kMaxUniqueIdLength, aIndex, aOut);
 }
 
 nsresult
-MediaEngineWebRTCVideoSource::Allocate(const VideoTrackConstraintsN &aConstraints,
+MediaEngineWebRTCVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                        const MediaEnginePrefs &aPrefs)
 {
   LOG((__FUNCTION__));
   if (mState == kReleased && mInitDone) {
     // Note: if shared, we don't allow a later opener to affect the resolution.
     // (This may change depending on spec changes for Constraints/settings)
 
-    ChooseCapability(aConstraints, aPrefs);
-
+    if (!ChooseCapability(aConstraints, aPrefs)) {
+      return NS_ERROR_UNEXPECTED;
+    }
     if (mViECapture->AllocateCaptureDevice(NS_ConvertUTF16toUTF8(mUniqueId).get(),
                                            kMaxUniqueIdLength, mCaptureIndex)) {
       return NS_ERROR_FAILURE;
     }
     mState = kAllocated;
     LOG(("Video device %d allocated", mCaptureIndex));
   } else {
 #ifdef PR_LOGGING
new file mode 100644
--- /dev/null
+++ b/dom/media/webrtc/MediaTrackConstraints.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 "MediaTrackConstraints.h"
+
+#include <limits>
+
+namespace mozilla {
+
+template<class ValueType>
+template<class ConstrainRange>
+void
+NormalizedConstraintSet::Range<ValueType>::SetFrom(const ConstrainRange& aOther)
+{
+  if (aOther.mIdeal.WasPassed()) {
+    mIdeal.Construct(aOther.mIdeal.Value());
+  }
+  if (aOther.mExact.WasPassed()) {
+    mMin = aOther.mExact.Value();
+    mMax = aOther.mExact.Value();
+  } else {
+    if (aOther.mMin.WasPassed()) {
+      mMin = aOther.mMin.Value();
+    }
+    if (aOther.mMax.WasPassed()) {
+      mMax = aOther.mMax.Value();
+    }
+  }
+}
+
+NormalizedConstraintSet::LongRange::LongRange(
+    const dom::OwningLongOrConstrainLongRange& aOther, bool advanced)
+: Range<int32_t>(1 + INT32_MIN, INT32_MAX) // +1 avoids Windows compiler bug
+{
+  if (aOther.IsLong()) {
+    if (advanced) {
+      mMin = mMax = aOther.GetAsLong();
+    } else {
+      mIdeal.Construct(aOther.GetAsLong());
+    }
+  } else {
+    SetFrom(aOther.GetAsConstrainLongRange());
+  }
+}
+
+NormalizedConstraintSet::DoubleRange::DoubleRange(
+    const dom::OwningDoubleOrConstrainDoubleRange& aOther, bool advanced)
+: Range<double>(-std::numeric_limits<double>::infinity(),
+                std::numeric_limits<double>::infinity())
+{
+  if (aOther.IsDouble()) {
+    if (advanced) {
+      mMin = mMax = aOther.GetAsDouble();
+    } else {
+      mIdeal.Construct(aOther.GetAsDouble());
+    }
+  } else {
+    SetFrom(aOther.GetAsConstrainDoubleRange());
+  }
+}
+
+FlattenedConstraints::FlattenedConstraints(const dom::MediaTrackConstraints& aOther)
+: NormalizedConstraintSet(aOther, false)
+{
+  if (aOther.mAdvanced.WasPassed()) {
+    const auto& advanced = aOther.mAdvanced.Value();
+    for (size_t i = 0; i < advanced.Length(); i++) {
+      NormalizedConstraintSet set(advanced[i], true);
+      // Must only apply compatible i.e. inherently non-overconstraining sets
+      // This rule is pretty much why this code is centralized here.
+      if (mWidth.Intersects(set.mWidth) &&
+          mHeight.Intersects(set.mHeight) &&
+          mFrameRate.Intersects(set.mFrameRate)) {
+        mWidth.Intersect(set.mWidth);
+        mHeight.Intersect(set.mHeight);
+        mFrameRate.Intersect(set.mFrameRate);
+      }
+    }
+  }
+}
+
+}
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -4,127 +4,84 @@
 
 // This file should not be included by other includes, as it contains code
 
 #ifndef MEDIATRACKCONSTRAINTS_H_
 #define MEDIATRACKCONSTRAINTS_H_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/MediaTrackConstraintSetBinding.h"
 
 namespace mozilla {
 
 template<class EnumValuesStrings, class Enum>
 static const char* EnumToASCII(const EnumValuesStrings& aStrings, Enum aValue) {
   return aStrings[uint32_t(aValue)].value;
 }
 
 template<class EnumValuesStrings, class Enum>
-static Enum StringToEnum(const EnumValuesStrings& aStrings, const nsAString& aValue,
-                         Enum aDefaultValue) {
+static Enum StringToEnum(const EnumValuesStrings& aStrings,
+                         const nsAString& aValue, Enum aDefaultValue) {
   for (size_t i = 0; aStrings[i].value; i++) {
     if (aValue.EqualsASCII(aStrings[i].value)) {
       return Enum(i);
     }
   }
   return aDefaultValue;
 }
 
-// Normalized internal version of MediaTrackConstraints to simplify downstream
-// processing. This implementation-only helper is included as needed by both
-// MediaManager (for gUM camera selection) and MediaEngine (for applyConstraints).
-
-template<typename T>
-class MediaTrackConstraintsN : public dom::MediaTrackConstraints
-{
-public:
-  typedef T Kind;
-  dom::Sequence<Kind> mRequireN;
-  bool mUnsupportedRequirement;
-  MediaTrackConstraintSet mRequired;
-  dom::Sequence<MediaTrackConstraintSet> mNonrequired;
-  dom::MediaSourceEnum mMediaSourceEnumValue;
+// Helper classes for orthogonal constraints without interdependencies.
+// Instead of constraining values, constrain the constraints themselves.
 
-  MediaTrackConstraintsN(const dom::MediaTrackConstraints &aOther,
-                         const dom::EnumEntry* aStrings)
-  : dom::MediaTrackConstraints(aOther)
-  , mUnsupportedRequirement(false)
-  , mStrings(aStrings)
+struct NormalizedConstraintSet
+{
+  template<class ValueType>
+  struct Range
   {
-    if (mRequire.WasPassed()) {
-      auto& array = mRequire.Value();
-      for (size_t i = 0; i < array.Length(); i++) {
-        auto value = StringToEnum(mStrings, array[i], Kind::Other);
-        if (value != Kind::Other) {
-          mRequireN.AppendElement(value);
-        } else {
-          mUnsupportedRequirement = true;
-        }
-      }
+    ValueType mMin, mMax;
+    dom::Optional<ValueType> mIdeal;
+
+    Range(ValueType aMin, ValueType aMax) : mMin(aMin), mMax(aMax) {}
+
+    template<class ConstrainRange>
+    void SetFrom(const ConstrainRange& aOther);
+    ValueType Clamp(ValueType n) const { return std::max(mMin, std::min(n, mMax)); }
+    bool Intersects(const Range& aOther) const {
+      return mMax >= aOther.mMin && mMin <= aOther.mMax;
     }
-    // treat MediaSource special because it's always required
-    mRequired.mMediaSource = mMediaSource;
+    void Intersect(const Range& aOther) {
+      MOZ_ASSERT(Intersects(aOther));
+      mMin = std::max(mMin, aOther.mMin);
+      mMax = std::min(mMax, aOther.mMax);
+    }
+  };
+
+  struct LongRange : public Range<int32_t>
+  {
+    LongRange(const dom::OwningLongOrConstrainLongRange& aOther, bool advanced);
+  };
 
-    mMediaSourceEnumValue = StringToEnum(dom::MediaSourceEnumValues::strings,
-                                         mMediaSource,
-                                         dom::MediaSourceEnum::Other);
-    if (mAdvanced.WasPassed()) {
-      if(mMediaSourceEnumValue != dom::MediaSourceEnum::Camera) {
-        // iterate through advanced, forcing mediaSource to match "root"
-        auto& array = mAdvanced.Value();
-        for (uint32_t i = 0; i < array.Length(); i++) {
-          auto& ms = array[i].mMediaSource;
-          if (ms.EqualsASCII(EnumToASCII(dom::MediaSourceEnumValues::strings,
-                                         dom::MediaSourceEnum::Camera))) {
-            ms = mMediaSource;
-          }
-        }
-      }
-    }
-  }
-protected:
-  MediaTrackConstraintSet& Triage(const Kind kind) {
-    if (mRequireN.IndexOf(kind) != mRequireN.NoIndex) {
-      return mRequired;
-    } else {
-      mNonrequired.AppendElement(MediaTrackConstraintSet());
-      return mNonrequired[mNonrequired.Length()-1];
-    }
-  }
-private:
-  const dom::EnumEntry* mStrings;
+  struct DoubleRange : public Range<double>
+  {
+    DoubleRange(const dom::OwningDoubleOrConstrainDoubleRange& aOther,
+                bool advanced);
+  };
+
+  // Do you need to add your constraint here? Only if your code uses flattening
+  LongRange mWidth, mHeight;
+  DoubleRange mFrameRate;
+
+  NormalizedConstraintSet(const dom::MediaTrackConstraintSet& aOther,
+                          bool advanced)
+  : mWidth(aOther.mWidth, advanced)
+  , mHeight(aOther.mHeight, advanced)
+  , mFrameRate(aOther.mFrameRate, advanced) {}
 };
 
-struct AudioTrackConstraintsN :
-  public MediaTrackConstraintsN<dom::SupportedAudioConstraints>
-{
-  MOZ_IMPLICIT AudioTrackConstraintsN(const dom::MediaTrackConstraints &aOther)
-  : MediaTrackConstraintsN<dom::SupportedAudioConstraints>(aOther, // B2G ICS compiler bug
-                           dom::SupportedAudioConstraintsValues::strings) {}
-};
-
-struct VideoTrackConstraintsN :
-    public MediaTrackConstraintsN<dom::SupportedVideoConstraints>
+struct FlattenedConstraints : public NormalizedConstraintSet
 {
-  MOZ_IMPLICIT VideoTrackConstraintsN(const dom::MediaTrackConstraints &aOther)
-  : MediaTrackConstraintsN<dom::SupportedVideoConstraints>(aOther,
-                           dom::SupportedVideoConstraintsValues::strings) {
-    if (mFacingMode.WasPassed()) {
-      Triage(Kind::FacingMode).mFacingMode.Construct(mFacingMode.Value());
-    }
-    // Reminder: add handling for new constraints both here & SatisfyConstraintSet
-    Triage(Kind::Width).mWidth = mWidth;
-    Triage(Kind::Height).mHeight = mHeight;
-    Triage(Kind::FrameRate).mFrameRate = mFrameRate;
-    if (mBrowserWindow.WasPassed()) {
-      Triage(Kind::BrowserWindow).mBrowserWindow.Construct(mBrowserWindow.Value());
-    }
-    if (mScrollWithPage.WasPassed()) {
-      Triage(Kind::ScrollWithPage).mScrollWithPage.Construct(mScrollWithPage.Value());
-    }
-    // treat MediaSource special because it's always required
-    mRequired.mMediaSource = mMediaSource;
-  }
+  FlattenedConstraints(const dom::MediaTrackConstraints& aOther);
 };
 
 }
 
 #endif /* MEDIATRACKCONSTRAINTS_H_ */
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -16,16 +16,17 @@ EXPORTS += [
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += ['AudioOutputObserver.h',
                 'MediaEngineWebRTC.h']
     UNIFIED_SOURCES += [
         'MediaEngineCameraVideoSource.cpp',
         'MediaEngineTabVideoSource.cpp',
         'MediaEngineWebRTCAudio.cpp',
         'MediaEngineWebRTCVideo.cpp',
+        'MediaTrackConstraints.cpp',
     ]
     # MediaEngineWebRTC.cpp needs to be built separately.
     SOURCES += [
         'MediaEngineWebRTC.cpp',
     ]
     LOCAL_INCLUDES += [
         '/dom/base',
         '/dom/camera',
--- a/dom/webidl/Constraints.webidl
+++ b/dom/webidl/Constraints.webidl
@@ -24,16 +24,30 @@ enum MediaSourceEnum {
     "application",
     "window",
     "browser",
     "microphone",
     "other"
 };
 
 dictionary ConstrainLongRange {
-    long min = -2147483647; // +1 works around windows compiler bug
-    long max = 2147483647;
+    long min;
+    long max;
+    long exact;
+    long ideal;
 };
 
 dictionary ConstrainDoubleRange {
-    unrestricted double min = -Infinity;
-    unrestricted double max = Infinity;
+    double min;
+    double max;
+    double exact;
+    double ideal;
 };
+
+dictionary ConstrainBooleanParameters {
+    boolean exact;
+    boolean ideal;
+};
+
+dictionary ConstrainDOMStringParameters {
+    (DOMString or sequence<DOMString>) exact;
+    (DOMString or sequence<DOMString>) ideal;
+};
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -6,17 +6,16 @@
  * 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.
  */
 
 dictionary MediaTrackConstraints : MediaTrackConstraintSet {
-    sequence<DOMString> require;
     sequence<MediaTrackConstraintSet> advanced;
 };
 
 interface MediaStreamTrack {
     readonly    attribute DOMString             kind;
     readonly    attribute DOMString             id;
     readonly    attribute DOMString             label;
                 attribute boolean               enabled;
--- a/dom/webidl/MediaTrackConstraintSet.webidl
+++ b/dom/webidl/MediaTrackConstraintSet.webidl
@@ -18,20 +18,21 @@ enum SupportedVideoConstraints {
     "scrollWithPage"
 };
 
 enum SupportedAudioConstraints {
     "other"
 };
 
 dictionary MediaTrackConstraintSet {
-    ConstrainLongRange width;
-    ConstrainLongRange height;
-    ConstrainDoubleRange frameRate;
+    ConstrainLong width;
+    ConstrainLong height;
+    ConstrainDouble frameRate;
     ConstrainDOMString facingMode;
     DOMString mediaSource = "camera";
     long long browserWindow;
     boolean scrollWithPage;
 };
 
 typedef (long or ConstrainLongRange) ConstrainLong;
 typedef (double or ConstrainDoubleRange) ConstrainDouble;
-typedef (DOMString or sequence<DOMString>) ConstrainDOMString;
+typedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
+typedef (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) ConstrainDOMString;