Bug 882145: gUM constraints converted to webidl + gUMDevices() takes constraints. r=bz r=jesup
☠☠ backed out by c833bcf12ad8 ☠ ☠
authorJan-Ivar Bruaroey <jib@mozilla.com>
Sat, 14 Sep 2013 00:48:39 -0400
changeset 147198 cf373e408a6b2ce0d05fadde2611140aee2ec624
parent 147197 cc5d2c0c692e14c64fc565e3b1b8975160c702db
child 147199 bc4ee7cbd7bdc90069832d670ce55fc9e793edc5
push id33802
push userrjesup@wgate.com
push dateSat, 14 Sep 2013 15:46:05 +0000
treeherdermozilla-inbound@bc4ee7cbd7bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jesup
bugs882145
milestone26.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 882145: gUM constraints converted to webidl + gUMDevices() takes constraints. r=bz r=jesup
browser/modules/webrtcUI.jsm
content/media/webspeech/recognition/SpeechRecognition.cpp
content/media/webspeech/recognition/SpeechRecognition.h
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/media/GetUserMediaRequest.cpp
dom/media/GetUserMediaRequest.h
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/moz.build
dom/media/nsIDOMNavigatorUserMedia.idl
dom/media/tests/mochitest/test_getUserMedia_exceptions.html
dom/webidl/GetUserMediaRequest.webidl
dom/webidl/MediaStreamTrack.webidl
dom/webidl/Navigator.webidl
dom/webidl/moz.build
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -60,28 +60,28 @@ function getBrowserForWindowId(aWindowID
 function getBrowserForWindow(aContentWindow) {
   return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIDocShell)
                        .chromeEventHandler;
 }
 
 function handleRequest(aSubject, aTopic, aData) {
-  let {windowID: windowID, callID: callID} = JSON.parse(aData);
-
-  let params = aSubject.QueryInterface(Ci.nsIMediaStreamOptions);
+  let constraints = aSubject.getConstraints();
 
   Services.wm.getMostRecentWindow(null).navigator.mozGetUserMediaDevices(
+    constraints,
     function (devices) {
-      prompt(windowID, callID, params.audio, params.video || params.picture, devices);
+      prompt(aSubject.windowID, aSubject.callID, constraints.audio,
+             constraints.video || constraints.picture, devices);
     },
     function (error) {
       // bug 827146 -- In the future, the UI should catch NO_DEVICES_FOUND
       // and allow the user to plug in a device, instead of immediately failing.
-      denyRequest(callID, error);
+      denyRequest(aSubject.callID, error);
     }
   );
 }
 
 function denyRequest(aCallID, aError) {
   let msg = null;
   if (aError) {
     msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
--- a/content/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/content/media/webspeech/recognition/SpeechRecognition.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SpeechRecognition.h"
 
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 
 #include "mozilla/dom/SpeechRecognitionBinding.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 
 #include "AudioSegment.h"
 #include "endpointer.h"
 
 #include "GeneratedEvents.h"
 #include "nsIDOMSpeechRecognitionEvent.h"
@@ -706,21 +707,26 @@ 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;
+  MediaStreamConstraintsInitializer constraints;
+  constraints.mAudio.SetAsBoolean() = true;
+
   if (!mTestConfig.mFakeFSMEvents) {
     MediaManager* manager = MediaManager::Get();
-    manager->GetUserMedia(false,
+    manager->GetUserMedia(cx,
+                          false,
                           GetOwner(),
-                          new GetUserMediaStreamOptions(),
+                          constraints,
                           new GetUserMediaSuccessCallback(this),
                           new GetUserMediaErrorCallback(this));
   }
 
   nsRefPtr<SpeechEvent> event = new SpeechEvent(this, EVENT_START);
   NS_DispatchToMainThread(event);
 }
 
@@ -917,66 +923,16 @@ SpeechRecognition::GetName(SpeechEvent* 
     "EVENT_RECOGNITIONSERVICE_ERROR"
   };
 
   MOZ_ASSERT(aEvent->mType < EVENT_COUNT);
   MOZ_ASSERT(ArrayLength(names) == EVENT_COUNT);
   return names[aEvent->mType];
 }
 
-NS_IMPL_ISUPPORTS1(SpeechRecognition::GetUserMediaStreamOptions, nsIMediaStreamOptions)
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetFake(bool* aFake)
-{
-  *aFake = false;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetAudio(bool* aAudio)
-{
-  *aAudio = true;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetVideo(bool* aVideo)
-{
-  *aVideo = false;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetPicture(bool* aPicture)
-{
-  *aPicture = false;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetCamera(nsAString& aCamera)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetAudioDevice(nsIMediaDevice** aAudioDevice)
-{
-  *aAudioDevice = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-SpeechRecognition::GetUserMediaStreamOptions::GetVideoDevice(nsIMediaDevice** aVideoDevice)
-{
-  *aVideoDevice = nullptr;
-  return NS_OK;
-}
-
 SpeechEvent::~SpeechEvent()
 {
   delete mAudioSegment;
 }
 
 NS_IMETHODIMP
 SpeechEvent::Run()
 {
--- a/content/media/webspeech/recognition/SpeechRecognition.h
+++ b/content/media/webspeech/recognition/SpeechRecognition.h
@@ -171,26 +171,16 @@ private:
     STATE_RECOGNIZING,
     STATE_WAITING_FOR_RESULT,
     STATE_COUNT
   };
 
   void SetState(FSMState state);
   bool StateBetween(FSMState begin, FSMState end);
 
-  class GetUserMediaStreamOptions : public nsIMediaStreamOptions
-  {
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEDIASTREAMOPTIONS
-
-    GetUserMediaStreamOptions() {}
-    virtual ~GetUserMediaStreamOptions() {}
-  };
-
   class GetUserMediaSuccessCallback : public nsIDOMGetUserMediaSuccessCallback
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIDOMGETUSERMEDIASUCCESSCALLBACK
 
     GetUserMediaSuccessCallback(SpeechRecognition* aRecognition)
       : mRecognition(aRecognition)
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -981,64 +981,67 @@ Navigator::GetGeolocation(ErrorResult& a
     return nullptr;
   }
 
   return mGeolocation;
 }
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 void
-Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams,
-                           MozDOMGetUserMediaSuccessCallback* aOnSuccess,
-                           MozDOMGetUserMediaErrorCallback* aOnError,
+Navigator::MozGetUserMedia(JSContext* aCx,
+                           const MediaStreamConstraints& aConstraints,
+                           NavigatorUserMediaSuccessCallback& aOnSuccess,
+                           NavigatorUserMediaErrorCallback& aOnError,
                            ErrorResult& aRv)
 {
-  CallbackObjectHolder<MozDOMGetUserMediaSuccessCallback,
-                       nsIDOMGetUserMediaSuccessCallback> holder1(aOnSuccess);
+  CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
+                       nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onsuccess =
     holder1.ToXPCOMCallback();
 
-  CallbackObjectHolder<MozDOMGetUserMediaErrorCallback,
-                       nsIDOMGetUserMediaErrorCallback> holder2(aOnError);
+  CallbackObjectHolder<NavigatorUserMediaErrorCallback,
+                       nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
 
   if (!mWindow || !mWindow->GetOuterWindow() ||
       mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   bool privileged = nsContentUtils::IsChromeDoc(mWindow->GetExtantDoc());
 
   MediaManager* manager = MediaManager::Get();
-  aRv = manager->GetUserMedia(privileged, mWindow, aParams, onsuccess, onerror);
+  aRv = manager->GetUserMedia(aCx, privileged, mWindow, aConstraints,
+                              onsuccess, onerror);
 }
 
 void
-Navigator::MozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback* aOnSuccess,
-                                  MozDOMGetUserMediaErrorCallback* aOnError,
+Navigator::MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
+                                  MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
+                                  NavigatorUserMediaErrorCallback& aOnError,
                                   ErrorResult& aRv)
 {
   CallbackObjectHolder<MozGetUserMediaDevicesSuccessCallback,
-                       nsIGetUserMediaDevicesSuccessCallback> holder1(aOnSuccess);
+                       nsIGetUserMediaDevicesSuccessCallback> holder1(&aOnSuccess);
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onsuccess =
     holder1.ToXPCOMCallback();
 
-  CallbackObjectHolder<MozDOMGetUserMediaErrorCallback,
-                       nsIDOMGetUserMediaErrorCallback> holder2(aOnError);
+  CallbackObjectHolder<NavigatorUserMediaErrorCallback,
+                       nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
 
   if (!mWindow || !mWindow->GetOuterWindow() ||
       mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   MediaManager* manager = MediaManager::Get();
-  aRv = manager->GetUserMediaDevices(mWindow, onsuccess, onerror);
+  aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror);
 }
 #endif
 
 DesktopNotificationCenter*
 Navigator::GetMozNotification(ErrorResult& aRv)
 {
   if (mNotification) {
     return mNotification;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -17,24 +17,25 @@
 #include "nsTArray.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMMozConnection;
 class nsIDOMMozMobileMessageManager;
 class nsIDOMNavigatorSystemMessages;
-class nsIMediaStreamOptions;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 
 namespace mozilla {
 namespace dom {
 class Geolocation;
 class systemMessageCallback;
+class MediaStreamConstraints;
+class MediaStreamConstraintsInternal;
 }
 }
 
 #ifdef MOZ_B2G_RIL
 class nsIDOMMozMobileConnection;
 class nsIDOMMozIccManager;
 #endif // MOZ_B2G_RIL
 
@@ -57,18 +58,18 @@ class FMRadio;
 
 class DesktopNotificationCenter;
 class MobileMessageManager;
 class MozIdleObserver;
 #ifdef MOZ_GAMEPAD
 class Gamepad;
 #endif // MOZ_GAMEPAD
 #ifdef MOZ_MEDIA_NAVIGATOR
-class MozDOMGetUserMediaSuccessCallback;
-class MozDOMGetUserMediaErrorCallback;
+class NavigatorUserMediaSuccessCallback;
+class NavigatorUserMediaErrorCallback;
 class MozGetUserMediaDevicesSuccessCallback;
 #endif // MOZ_MEDIA_NAVIGATOR
 
 namespace icc {
 #ifdef MOZ_B2G_RIL
 class IccManager;
 #endif
 }
@@ -235,22 +236,24 @@ public:
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 #ifdef MOZ_MEDIA_NAVIGATOR
-  void MozGetUserMedia(nsIMediaStreamOptions* aParams,
-                       MozDOMGetUserMediaSuccessCallback* aOnSuccess,
-                       MozDOMGetUserMediaErrorCallback* aOnError,
+  void MozGetUserMedia(JSContext* aCx,
+                       const MediaStreamConstraints& aConstraints,
+                       NavigatorUserMediaSuccessCallback& aOnSuccess,
+                       NavigatorUserMediaErrorCallback& aOnError,
                        ErrorResult& aRv);
-  void MozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback* aOnSuccess,
-                              MozDOMGetUserMediaErrorCallback* aOnError,
+  void MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
+                              MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
+                              NavigatorUserMediaErrorCallback& aOnError,
                               ErrorResult& aRv);
 #endif // MOZ_MEDIA_NAVIGATOR
   bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
                     JS::Handle<jsid> aId, JS::MutableHandle<JS::Value> aValue);
   void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
                            ErrorResult& aRv);
 
   // WebIDL helper methods
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1780,18 +1780,16 @@ addExternalIface('imgIRequest', nativeTy
 addExternalIface('LockedFile')
 addExternalIface('MediaList')
 addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
 addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
 addExternalIface('MozConnection', headerFile='nsIDOMConnection.h')
 addExternalIface('MozControllers', nativeType='nsIControllers')
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
 addExternalIface('MozIccManager', headerFile='nsIDOMIccManager.h')
-addExternalIface('MozMediaStreamOptions', nativeType='nsIMediaStreamOptions',
-                 headerFile='nsIDOMNavigatorUserMedia.h')
 addExternalIface('MozMobileConnection', headerFile='nsIDOMMobileConnection.h')
 addExternalIface('MozMobileMessageManager', headerFile='nsIDOMMobileMessageManager.h')
 addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
 addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
                  notflattened=True)
 addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
 addExternalIface('MozTelephony', nativeType='nsIDOMTelephony')
 addExternalIface('MozTreeBoxObject', nativeType='nsITreeBoxObject',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -630,16 +630,20 @@ class CGHeaders(CGWrapper):
                     # member works.
                     declareIncludes.add("mozilla/dom/Nullable.h")
                 else:
                     bindingHeaders.add("mozilla/dom/Nullable.h")
             unrolled = t.unroll()
             if unrolled.isUnion():
                 # UnionConversions.h includes UnionTypes.h
                 bindingHeaders.add("mozilla/dom/UnionConversions.h")
+                if dictionary:
+                    # Our dictionary definition is in the header and
+                    # needs the union type.
+                    declareIncludes.add("mozilla/dom/UnionTypes.h")
             elif unrolled.isDate():
                 if dictionary or jsImplementedDescriptors:
                     headerSet = declareIncludes
                 else:
                     headerSet = bindingHeaders
                 headerSet.add("mozilla/dom/Date.h")
             elif unrolled.isInterface():
                 if unrolled.isSpiderMonkeyInterface():
new file mode 100644
--- /dev/null
+++ b/dom/media/GetUserMediaRequest.cpp
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+#include "GetUserMediaRequest.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/GetUserMediaRequestBinding.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsPIDOMWindow.h"
+#include "nsCxPusher.h"
+
+namespace mozilla {
+namespace dom {
+
+GetUserMediaRequest::GetUserMediaRequest(
+    nsPIDOMWindow* aInnerWindow,
+    const nsAString& aCallID,
+    const MediaStreamConstraintsInternal& aConstraints)
+  : mInnerWindow(aInnerWindow)
+  , mWindowID(aInnerWindow->GetOuterWindow()->WindowID())
+  , mCallID(aCallID)
+  , mConstraints(aConstraints)
+{
+  SetIsDOMBinding();
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(GetUserMediaRequest, mInnerWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GetUserMediaRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GetUserMediaRequest)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GetUserMediaRequest)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+GetUserMediaRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return GetUserMediaRequestBinding::Wrap(aCx, aScope, this);
+}
+
+nsISupports* GetUserMediaRequest::GetParentObject()
+{
+  return mInnerWindow;
+}
+
+void GetUserMediaRequest::GetCallID(nsString& retval)
+{
+  retval = mCallID;
+}
+
+uint64_t GetUserMediaRequest::WindowID()
+{
+  return mWindowID;
+}
+
+void
+GetUserMediaRequest::GetConstraints(MediaStreamConstraintsInternal &result)
+{
+  result = mConstraints;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/GetUserMediaRequest.h
@@ -0,0 +1,48 @@
+/* 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/. */
+
+#ifndef GetUserMediaRequest_h__
+#define GetUserMediaRequest_h__
+
+#include "mozilla/ErrorResult.h"
+#include "nsISupportsImpl.h"
+#include "nsAutoPtr.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+class MediaStreamConstraintsInternal;
+
+class GetUserMediaRequest : public nsISupports, public nsWrapperCache
+{
+public:
+  GetUserMediaRequest(nsPIDOMWindow* aInnerWindow, const nsAString& aCallID,
+                      const MediaStreamConstraintsInternal& aConstraints);
+  virtual ~GetUserMediaRequest() {};
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GetUserMediaRequest)
+
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
+    MOZ_OVERRIDE;
+  nsISupports* GetParentObject();
+
+  uint64_t WindowID();
+  void GetCallID(nsString& retval);
+  void GetConstraints(MediaStreamConstraintsInternal &result);
+
+private:
+  nsCOMPtr<nsPIDOMWindow> mInnerWindow;
+  uint64_t mWindowID;
+  const nsString mCallID;
+  MediaStreamConstraintsInternal mConstraints;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // GetUserMediaRequest_h__
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1,29 +1,32 @@
 /* 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 "MediaManager.h"
 
 #include "MediaStreamGraph.h"
+#include "GetUserMediaRequest.h"
 #ifdef MOZ_WIDGET_GONK
 #include "nsIAudioManager.h"
 #endif
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/GetUserMediaRequestBinding.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
@@ -56,16 +59,85 @@ 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::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;
+
+// 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, JS::NullPtr(), &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);
+    if (!JS_GetPropertyById(aCx, b, props[i], &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.address());
+      NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
+
+      JS::Rooted<JSString*> namestr(aCx, JS_ValueToString(aCx, nameval));
+      NS_ENSURE_TRUE(namestr, NS_ERROR_UNEXPECTED);
+      aDifference->Assign(JS_GetStringCharsZ(aCx, namestr));
+      return NS_OK;
+    }
+  }
+  aDifference->Truncate();
+  return NS_OK;
+}
+
+// Look for and return any unknown mandatory constraint. Done by comparing
+// a raw MediaTrackConstraints against a normalized copy, both passed in.
+
+static nsresult ValidateTrackConstraints(
+    JSContext *aCx, JSObject *aRaw,
+    const MediaTrackConstraintsInternal &aNormalized,
+    nsString *aOutUnknownConstraint)
+{
+  // First find raw mMandatory member (use MediaTrackConstraints as helper)
+  dom::RootedDictionary<MediaTrackConstraints> track(aCx);
+  JS::Rooted<JS::Value> rawval(aCx, JS::ObjectValue(*aRaw));
+  bool success = track.Init(aCx, rawval);
+  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+
+  if (track.mMandatory.WasPassed()) {
+    nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
+                                      aNormalized.mMandatory,
+                                      aOutUnknownConstraint);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread. The success callback is also passed here
  * so it can be released correctly.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
 public:
@@ -515,46 +587,16 @@ private:
  * are sent back to the DOM.
  *
  * 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:
-  /**
-   * The caller can choose to provide a MediaDevice as the last argument,
-   * if one is not provided, a default device is automatically chosen.
-   */
-  GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
-    MediaEnginePrefs &aPrefs,
-    MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
-    : mAudio(aAudio)
-    , mVideo(aVideo)
-    , mPicture(aPicture)
-    , mSuccess(aSuccess)
-    , mError(aError)
-    , mWindowID(aWindowID)
-    , mListener(aListener)
-    , mPrefs(aPrefs)
-    , mDeviceChosen(true)
-    , mBackendChosen(false)
-    , mManager(MediaManager::GetInstance())
-  {
-    if (mAudio) {
-      mAudioDevice = aAudioDevice;
-    }
-    if (mVideo) {
-      mVideoDevice = aVideoDevice;
-    }
-  }
-
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
@@ -842,24 +884,25 @@ 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,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowId)
-    : mSuccess(aSuccess)
+    : mConstraints(aConstraints)
+    , mSuccess(aSuccess)
     , mError(aError)
     , mManager(MediaManager::GetInstance())
-    , mWindowId(aWindowId)
-  {}
+    , mWindowId(aWindowId) {}
 
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
     uint32_t audioCount, videoCount, i;
 
@@ -891,16 +934,17 @@ public:
 
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(
       mSuccess, mError, devices // give ownership of the nsTArray to the runnable
     ));
     return NS_OK;
   }
 
 private:
+  MediaStreamConstraintsInternal mConstraints;
   already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
 };
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
@@ -968,85 +1012,87 @@ MediaManager::GetInstance()
 }
 
 /**
  * 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(bool aPrivileged, nsPIDOMWindow* aWindow,
-  nsIMediaStreamOptions* aParams,
+MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
+  nsPIDOMWindow* aWindow, const MediaStreamConstraints& aRawConstraints,
   nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnError)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
   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);
 
-  /* Get options */
-  nsresult rv;
-  bool fake, audio, video, picture;
-
-  rv = aParams->GetFake(&fake);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aParams->GetPicture(&picture);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aParams->GetAudio(&audio);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aParams->GetVideo(&video);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIMediaDevice> audiodevice;
-  rv = aParams->GetAudioDevice(getter_AddRefs(audiodevice));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIMediaDevice> videodevice;
-  rv = aParams->GetVideoDevice(getter_AddRefs(videodevice));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // If a device was provided, make sure it support the type of stream requested.
-  if (audiodevice) {
-    nsString type;
-    audiodevice->GetType(type);
-    if (audio && !type.EqualsLiteral("audio")) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-  if (videodevice) {
-    nsString type;
-    videodevice->GetType(type);
-    if ((picture || video) && !type.EqualsLiteral("video")) {
-        return NS_ERROR_FAILURE;
-    }
+  Maybe<JSAutoCompartment> ac;
+  if (aRawConstraints.mAudio.IsObject() || aRawConstraints.mVideo.IsObject()) {
+    ac.construct(aCx, (aRawConstraints.mVideo.IsObject()?
+                       aRawConstraints.mVideo.GetAsObject() :
+                       aRawConstraints.mAudio.GetAsObject()));
   }
 
-  // We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
-  nsString cameraType;
-  rv = aParams->GetCamera(cameraType);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // aRawConstraints has JSObjects in it, so process it by copying it into
+  // MediaStreamConstraintsInternal which does not.
+
+  dom::RootedDictionary<MediaStreamConstraintsInternal> c(aCx);
+
+  // TODO: Simplify this part once Bug 767924 is fixed.
+  // Since we cannot yet use unions on non-objects, we process the raw object
+  // into discrete members for internal use until Bug 767924 is fixed
+
+  nsresult rv;
+  nsString unknownConstraintFound;
+
+  if (aRawConstraints.mAudio.IsObject()) {
+    JS::Rooted<JS::Value> temp(aCx,
+        JS::ObjectValue(*aRawConstraints.mAudio.GetAsObject()));
+    bool success = c.mAudiom.Init(aCx, temp);
+    NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+
+    rv = ValidateTrackConstraints(aCx, aRawConstraints.mAudio.GetAsObject(),
+                                  c.mAudiom, &unknownConstraintFound);
+    NS_ENSURE_SUCCESS(rv, rv);
+    c.mAudio = true;
+  } else {
+    c.mAudio = aRawConstraints.mAudio.GetAsBoolean();
+  }
+  if (aRawConstraints.mVideo.IsObject()) {
+    JS::Rooted<JS::Value> temp(aCx,
+        JS::ObjectValue(*aRawConstraints.mVideo.GetAsObject()));
+    bool success = c.mVideom.Init(aCx, temp);
+    NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+
+    rv = ValidateTrackConstraints(aCx, aRawConstraints.mVideo.GetAsObject(),
+                                  c.mVideom, &unknownConstraintFound);
+    NS_ENSURE_SUCCESS(rv, rv);
+    c.mVideo = true;
+  } else {
+    c.mVideo = aRawConstraints.mVideo.GetAsBoolean();
+  }
+  c.mPicture = aRawConstraints.mPicture;
+  c.mFake = aRawConstraints.mFake;
 
   /**
    * If we were asked to get a picture, before getting a snapshot, we check if
    * the calling page is allowed to open a popup. We do this because
    * {picture:true} will open a new "window" to let the user preview or select
    * an image, on Android. The desktop UI for {picture:true} is TBD, at which
    * may point we can decide whether to extend this test there as well.
    */
 #if !defined(MOZ_WEBRTC)
-  if (picture && !aPrivileged) {
+  if (c.mPicture && !aPrivileged) {
     if (aWindow->GetPopupControlState() > openControlled) {
       nsCOMPtr<nsIPopupWindowManager> pm =
         do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
       if (!pm) {
         return NS_OK;
       }
       uint32_t permission;
       nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
@@ -1074,16 +1120,32 @@ MediaManager::GetUserMedia(bool aPrivile
   nsRefPtr<GetUserMediaRunnable> gUMRunnable;
   // 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.
+    // Things are set up enough here that we can fire Error callback.
+
+    LOG(("Unsupported mandatory constraint: %s\n",
+          NS_ConvertUTF16toUTF8(unknownConstraintFound).get()));
+
+    nsString errormsg(NS_LITERAL_STRING("NOT_SUPPORTED_ERR: "));
+    errormsg.Append(unknownConstraintFound);
+    NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess.forget(),
+                                                      onError.forget(),
+                                                      errormsg, windowID));
+    return NS_OK;
+  }
+
   // 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.
@@ -1091,62 +1153,48 @@ MediaManager::GetUserMedia(bool aPrivile
 
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     aPrivileged = true;
   }
 
   /**
    * Pass runnables along to GetUserMediaRunnable so it can add the
-   * MediaStreamListener to the runnable list. The last argument can
-   * optionally be a MediaDevice object, which should provided if one was
-   * selected by the user via the UI, or was provided by privileged code
-   * via the device: attribute via nsIMediaStreamOptions.
-   *
-   * If a fake stream was requested, we force the use of the default backend.
+   * MediaStreamListener to the runnable list.
    */
-  // XXX take options from constraints instead of prefs
-  if (fake) {
+  if (c.mFake) {
     // Fake stream from default backend.
     gUMRunnable = new GetUserMediaRunnable(
-      audio, video, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs,
-      new MediaEngineDefault()
-                                           );
-  } else if (audiodevice || videodevice) {
-    // Stream from provided device.
-    gUMRunnable = new GetUserMediaRunnable(
-      audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs,
-      static_cast<MediaDevice*>(audiodevice.get()),
-      static_cast<MediaDevice*>(videodevice.get())
-                                           );
+      c.mAudio, c.mVideo, onSuccess.forget(),
+      onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
   } else {
     // Stream from default device from WebRTC backend.
     gUMRunnable = new GetUserMediaRunnable(
-      audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs
-                                           );
+      c.mAudio, c.mVideo, c.mPicture,
+      onSuccess.forget(), onError.forget(), windowID, listener, mPrefs);
   }
 
 #ifdef MOZ_B2G_CAMERA
   if (mCameraManager == nullptr) {
     aPrivileged = nsDOMCameraManager::CheckPermission(aWindow);
     if (aPrivileged) {
       mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
     }
   }
 #endif
 
 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
-  if (picture) {
+  if (c.mPicture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
     NS_DispatchToMainThread(gUMRunnable);
     return NS_OK;
   }
 #endif
   // XXX No full support for picture in Desktop yet (needs proper UI)
-  if (aPrivileged || fake) {
+  if (aPrivileged || c.mFake) {
     mMediaThread->Dispatch(gUMRunnable, NS_DISPATCH_NORMAL);
   } else {
     // Ask for user permission, and dispatch runnable (or not) when a response
     // is received via an observer notification. Each call is paired with its
     // runnable by a GUID.
     nsresult rv;
     nsCOMPtr<nsIUUIDGenerator> uuidgen =
       do_GetService("@mozilla.org/uuid-generator;1", &rv);
@@ -1159,52 +1207,41 @@ MediaManager::GetUserMedia(bool aPrivile
 
     char buffer[NSID_LENGTH];
     id.ToProvidedString(buffer);
     NS_ConvertUTF8toUTF16 callID(buffer);
 
     // Store the current callback.
     mActiveCallbacks.Put(callID, gUMRunnable);
 
-    // Construct JSON structure with both the windowID and the callID.
-    nsAutoString data;
-    data.Append(NS_LITERAL_STRING("{\"windowID\":"));
-
-    // Convert window ID to string.
-    char windowBuffer[32];
-    PR_snprintf(windowBuffer, sizeof(windowBuffer), "%llu",
-                aWindow->GetOuterWindow()->WindowID());
-    data.Append(NS_ConvertUTF8toUTF16(windowBuffer));
-
-    data.Append(NS_LITERAL_STRING(", \"callID\":\""));
-    data.Append(callID);
-    data.Append(NS_LITERAL_STRING("\"}"));
-
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-    obs->NotifyObservers(aParams, "getUserMedia:request", data.get());
+    nsRefPtr<GetUserMediaRequest> req = new GetUserMediaRequest(aWindow,
+                                                                callID, c);
+    obs->NotifyObservers(req, "getUserMedia:request", nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
+  const MediaStreamConstraintsInternal& aConstraints,
   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnError)
 {
   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);
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
 
   nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
-    onSuccess.forget(), onError.forget(), aWindow->WindowID()
+    aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID()
   );
 
   nsCOMPtr<nsIThread> deviceThread;
   nsresult rv = NS_NewThread(getter_AddRefs(deviceThread));
   NS_ENSURE_SUCCESS(rv, rv);
 
 
   deviceThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -15,28 +15,34 @@
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "prlog.h"
 #include "DOMMediaStream.h"
 
 #ifdef MOZ_WEBRTC
 #include "mtransport/runnable_utils.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "DOMCameraManager.h"
 #endif
 
 namespace mozilla {
+namespace dom {
+class MediaStreamConstraints;
+class NavigatorUserMediaSuccessCallback;
+class NavigatorUserMediaErrorCallback;
+}
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaManagerLog();
 #define MM_LOG(msg) PR_LOG(GetMediaManagerLog(), PR_LOG_DEBUG, msg)
 #else
 #define MM_LOG(msg)
 #endif
 
@@ -402,21 +408,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(bool aPrivileged, nsPIDOMWindow* aWindow,
-    nsIMediaStreamOptions* aParams,
+  nsresult GetUserMedia(JSContext* aCx, bool aPrivileged,
+    nsPIDOMWindow* aWindow,
+    const dom::MediaStreamConstraints& aRawConstraints,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
+
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
+    const dom::MediaStreamConstraintsInternal& aConstraints,
     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
   void OnNavigation(uint64_t aWindowID);
 
   MediaEnginePrefs mPrefs;
 
 private:
   WindowTable *GetActiveWindows() {
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -14,21 +14,26 @@ XPIDL_SOURCES += [
     'nsIDOMNavigatorUserMedia.idl',
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 MODULE = 'dom'
 
+EXPORTS.mozilla.dom += [
+    'GetUserMediaRequest.h',
+]
+
 EXPORTS.mozilla += [
     'MediaManager.h',
 ]
 
 CPP_SOURCES += [
+    'GetUserMediaRequest.cpp',
     'MediaManager.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'PeerConnection.js',
     'PeerConnection.manifest',
 ]
 
--- a/dom/media/nsIDOMNavigatorUserMedia.idl
+++ b/dom/media/nsIDOMNavigatorUserMedia.idl
@@ -30,20 +30,8 @@ interface nsIDOMGetUserMediaSuccessCallb
   void onSuccess(in nsISupports value);
 };
 
 [scriptable, function, uuid(2614bbcf-85cc-43e5-8740-964f52bdc7ca)]
 interface nsIDOMGetUserMediaErrorCallback : nsISupports
 {
   void onError(in DOMString error);
 };
-
-[scriptable, uuid(f34a3616-395a-43cd-b275-bf81750ac8b9)]
-interface nsIMediaStreamOptions : nsISupports
-{
-  readonly attribute boolean fake;
-  readonly attribute boolean audio;
-  readonly attribute boolean video;
-  readonly attribute boolean picture;
-  readonly attribute DOMString camera;
-  readonly attribute nsIMediaDevice audioDevice;
-  readonly attribute nsIMediaDevice videoDevice;
-};
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -34,17 +34,17 @@ var exceptionTests = [
   { params: [{video: true, fake: true}, unexpectedCall],
     error: "Not enough arguments to Navigator.mozGetUserMedia.",
     message: "two arguments specified" },
 
   // Each test here verifies that providing an incorret object
   // type to any mozGetUserMedia parameter should throw
   // the correct exception specified
   { params: [1, unexpectedCall, unexpectedCall],
-    error: "Argument 1 of Navigator.mozGetUserMedia is not an object.",
+    error: "Argument 1 of Navigator.mozGetUserMedia can't be converted to a dictionary.",
     message: "wrong object type as first parameter" },
   { 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" }
 ];
new file mode 100644
--- /dev/null
+++ b/dom/webidl/GetUserMediaRequest.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * This is an internal IDL file
+ */
+
+[NoInterfaceObject]
+interface GetUserMediaRequest {
+  readonly attribute unsigned long long windowID;
+  readonly attribute DOMString callID;
+  MediaStreamConstraintsInternal getConstraints();
+};
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -5,16 +5,65 @@
  *
  * 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
+
+enum VideoFacingModeEnum {
+    "user",
+    "environment",
+    "left",
+    "right"
+};
+
+dictionary MediaTrackConstraintSet {
+    VideoFacingModeEnum facingMode;
+};
+
+// MediaTrackConstraint = single-property-subset of MediaTrackConstraintSet
+// Implemented as full set. Test Object.keys(pair).length == 1
+
+// typedef MediaTrackConstraintSet MediaTrackConstraint; // TODO: Bug 913053
+
+dictionary MediaStreamConstraints {
+    (boolean or object) audio = false; // TODO: Once Bug 767924 fixed change to
+    (boolean or object) video = false; // (boolean or MediaTrackConstraints)
+    boolean picture = false;
+    boolean fake = false;
+};
+
+// Internal dictionary to process the raw objects in (boolean or object)
+// workaround above. Since we cannot yet use unions on non-objects, we process
+// the data into discrete members for internal use until Bug 767924 is fixed:
+
+dictionary MediaStreamConstraintsInternal {
+    boolean audio = false;
+    boolean video = false;
+    boolean picture = false;
+    boolean fake = false;
+    MediaTrackConstraintsInternal audiom;
+    MediaTrackConstraintsInternal videom;
+};
+
+dictionary MediaTrackConstraints {
+    object mandatory; // so we can see unknown + unsupported constraints
+    sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
+};
+
+dictionary MediaTrackConstraintsInternal {
+    MediaTrackConstraintSet mandatory; // holds only supported constraints
+    sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
+};
+
 interface MediaStreamTrack {
     readonly    attribute DOMString             kind;
     readonly    attribute DOMString             id;
     readonly    attribute DOMString             label;
                 attribute boolean               enabled;
 //    readonly    attribute MediaStreamTrackState readyState;
 //    readonly    attribute SourceTypeEnum        sourceType;
 //    readonly    attribute DOMString             sourceId;
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -315,27 +315,27 @@ partial interface Navigator {
 // nsIMozNavigatorAudioChannelManager
 partial interface Navigator {
   [Throws]
   readonly attribute AudioChannelManager mozAudioChannelManager;
 };
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 
 #ifdef MOZ_MEDIA_NAVIGATOR
-// nsIDOMNavigatorUserMedia
-callback MozDOMGetUserMediaSuccessCallback = void (nsISupports? value);
-callback MozDOMGetUserMediaErrorCallback = void (DOMString error);
-interface MozMediaStreamOptions;
+callback NavigatorUserMediaSuccessCallback = void (MediaStream stream);
+callback NavigatorUserMediaErrorCallback = void (DOMString error);
+
 partial interface Navigator {
   [Throws, Func="Navigator::HasUserMediaSupport"]
-  void mozGetUserMedia(MozMediaStreamOptions? params,
-                       MozDOMGetUserMediaSuccessCallback? onsuccess,
-                       MozDOMGetUserMediaErrorCallback? onerror);
+  void mozGetUserMedia(MediaStreamConstraints constraints,
+                       NavigatorUserMediaSuccessCallback successCallback,
+                       NavigatorUserMediaErrorCallback errorCallback);
 };
 
 // nsINavigatorUserMedia
 callback MozGetUserMediaDevicesSuccessCallback = void (nsIVariant? devices);
 partial interface Navigator {
   [Throws, ChromeOnly]
-  void mozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback? onsuccess,
-                              MozDOMGetUserMediaErrorCallback? onerror);
+  void mozGetUserMediaDevices(MediaStreamConstraintsInternal constraints,
+                              MozGetUserMediaDevicesSuccessCallback onsuccess,
+                              NavigatorUserMediaErrorCallback onerror);
 };
 #endif // MOZ_MEDIA_NAVIGATOR
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -96,16 +96,17 @@ WEBIDL_FILES = [
     'FileReader.webidl',
     'FileReaderSync.webidl',
     'FileRequest.webidl',
     'FocusEvent.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
     'Geolocation.webidl',
+    'GetUserMediaRequest.webidl',
     'History.webidl',
     'HTMLAnchorElement.webidl',
     'HTMLAppletElement.webidl',
     'HTMLAreaElement.webidl',
     'HTMLAudioElement.webidl',
     'HTMLBaseElement.webidl',
     'HTMLBodyElement.webidl',
     'HTMLBRElement.webidl',