Bug 1033885 - add mediaDevices.getUserMedia with promises. r=bz, r=jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Sat, 20 Sep 2014 02:20:41 -0400
changeset 237138 a461238e8fb2a708acd4f000ae2d03a87f87ee4f
parent 237137 6f383f465a4dd2e98ffdcfd72becd271041a315e
child 237139 162d085b801959d019e45c64cb237d3b17b67da7
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jesup
bugs1033885
milestone36.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 1033885 - add mediaDevices.getUserMedia with promises. r=bz, r=jesup
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/media/DOMMediaStream.cpp
dom/media/MediaDevices.cpp
dom/media/MediaDevices.h
dom/media/MediaStreamError.cpp
dom/media/moz.build
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/MediaDevices.webidl
dom/webidl/Navigator.webidl
dom/webidl/moz.build
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -64,16 +64,17 @@
 #include "nsIDOMNavigatorSystemMessages.h"
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
 #include "mozIThirdPartyUtil.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
+#include "mozilla/dom/MediaDevices.h"
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
@@ -1208,16 +1209,31 @@ Navigator::SendBeacon(const nsAString& a
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
   return true;
 }
 
 #ifdef MOZ_MEDIA_NAVIGATOR
+MediaDevices*
+Navigator::GetMediaDevices(ErrorResult& aRv)
+{
+  if (!mMediaDevices) {
+    if (!mWindow ||
+        !mWindow->GetOuterWindow() ||
+        mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
+      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+      return nullptr;
+    }
+    mMediaDevices = new MediaDevices(mWindow);
+  }
+  return mMediaDevices;
+}
+
 void
 Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
                            NavigatorUserMediaSuccessCallback& aOnSuccess,
                            NavigatorUserMediaErrorCallback& aOnError,
                            ErrorResult& aRv)
 {
   CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
                        nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -27,16 +27,17 @@ class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIPrincipal;
 class nsIURI;
 
 namespace mozilla {
 namespace dom {
 class Geolocation;
 class systemMessageCallback;
+class MediaDevices;
 struct MediaStreamConstraints;
 class WakeLock;
 class ArrayBufferViewOrBlobOrStringOrFormData;
 struct MobileIdOptions;
 class ServiceWorkerContainer;
 }
 }
 
@@ -219,16 +220,17 @@ public:
                          ErrorResult& aRv);
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
   MobileMessageManager* GetMozMobileMessage();
   Telephony* GetMozTelephony(ErrorResult& aRv);
   Voicemail* GetMozVoicemail(ErrorResult& aRv);
   network::Connection* GetConnection(ErrorResult& aRv);
   nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
+  MediaDevices* GetMediaDevices(ErrorResult& aRv);
   void MozSetMessageHandler(const nsAString& aType,
                             systemMessageCallback* aCallback,
                             ErrorResult& aRv);
   bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv);
 #ifdef MOZ_B2G
   already_AddRefed<Promise> GetMobileIdAssertion(const MobileIdOptions& options,
                                                  ErrorResult& aRv);
 #endif
@@ -339,16 +341,17 @@ private:
 #endif
 #ifdef MOZ_B2G_BT
   nsRefPtr<bluetooth::BluetoothManager> mBluetooth;
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   nsRefPtr<system::AudioChannelManager> mAudioChannelManager;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
+  nsRefPtr<MediaDevices> mMediaDevices;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
   nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
   nsRefPtr<time::TimeManager> mTimeManager;
   nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindow> mWindow;
 
   // Hashtable for saving cached objects newresolve created, so we don't create
   // the object twice if asked for it twice, whether due to use of "delete" or
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -28,17 +28,22 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMMe
 
 NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMMediaStream)
   NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_ISUPPORTS_INHERITED0(DOMLocalMediaStream, DOMMediaStream)
+NS_IMPL_ADDREF_INHERITED(DOMLocalMediaStream, DOMMediaStream)
+NS_IMPL_RELEASE_INHERITED(DOMLocalMediaStream, DOMMediaStream)
+
+NS_INTERFACE_MAP_BEGIN(DOMLocalMediaStream)
+  NS_INTERFACE_MAP_ENTRY(DOMLocalMediaStream)
+NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
                                    mStreamNode)
 
 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDevices.cpp
@@ -0,0 +1,97 @@
+/* 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 "mozilla/dom/MediaDevices.h"
+#include "mozilla/dom/MediaStreamBinding.h"
+#include "mozilla/dom/MediaDevicesBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/MediaManager.h"
+#include "nsIEventTarget.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+
+class MediaDevices::GumResolver : public nsIDOMGetUserMediaSuccessCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit GumResolver(Promise* aPromise) : mPromise(aPromise) {}
+
+  NS_IMETHOD
+  OnSuccess(nsISupports* aStream) MOZ_OVERRIDE
+  {
+    nsRefPtr<DOMLocalMediaStream> stream = do_QueryObject(aStream);
+    if (!stream) {
+      return NS_ERROR_FAILURE;
+    }
+    mPromise->MaybeResolve(stream);
+    return NS_OK;
+  }
+
+private:
+  virtual ~GumResolver() {}
+  nsRefPtr<Promise> mPromise;
+};
+
+class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit GumRejecter(Promise* aPromise) : mPromise(aPromise) {}
+
+  NS_IMETHOD
+  OnError(nsISupports* aError) MOZ_OVERRIDE
+  {
+    nsRefPtr<MediaStreamError> error = do_QueryObject(aError);
+    if (!error) {
+      return NS_ERROR_FAILURE;
+    }
+    mPromise->MaybeReject(error);
+    return NS_OK;
+  }
+
+private:
+  virtual ~GumRejecter() {}
+  nsRefPtr<Promise> mPromise;
+};
+
+NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
+NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
+
+already_AddRefed<Promise>
+MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
+                           ErrorResult &aRv)
+{
+  ErrorResult rv;
+  nsPIDOMWindow* window = GetOwner();
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
+  nsRefPtr<Promise> p = Promise::Create(go, aRv);
+  NS_ENSURE_TRUE(!rv.Failed(), nullptr);
+
+  nsRefPtr<GumResolver> resolver = new GumResolver(p);
+  nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
+
+  aRv = MediaManager::Get()->GetUserMedia(window, aConstraints,
+                                          resolver, rejecter);
+  return p.forget();
+}
+
+NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
+NS_INTERFACE_MAP_BEGIN(MediaDevices)
+  NS_INTERFACE_MAP_ENTRY(MediaDevices)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+JSObject*
+MediaDevices::WrapObject(JSContext* aCx)
+{
+  return MediaDevicesBinding::Wrap(aCx, this);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDevices.h
@@ -0,0 +1,50 @@
+/* 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 MediaDevices_h__
+#define MediaDevices_h__
+
+#include "mozilla/ErrorResult.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+struct MediaStreamConstraints;
+
+#define MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID \
+{ 0x2f784d8a, 0x7485, 0x4280, \
+ { 0x9a, 0x36, 0x74, 0xa4, 0xd6, 0x71, 0xa6, 0xc8 } }
+
+class MediaDevices MOZ_FINAL : public DOMEventTargetHelper
+{
+public:
+  MediaDevices(nsPIDOMWindow* aWindow) : DOMEventTargetHelper(aWindow) {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
+
+  JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
+
+  already_AddRefed<Promise>
+  GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
+
+private:
+  class GumResolver;
+  class GumRejecter;
+
+  virtual ~MediaDevices() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(MediaDevices,
+                              MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // MediaDevices_h__
--- a/dom/media/MediaStreamError.cpp
+++ b/dom/media/MediaStreamError.cpp
@@ -45,16 +45,17 @@ MediaStreamError::MediaStreamError(
   , mParent(aParent) {}
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaStreamError, mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamError)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamError)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamError)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(MediaStreamError)
 NS_INTERFACE_MAP_END
 
 JSObject*
 MediaStreamError::WrapObject(JSContext* aCx)
 {
   return MediaStreamErrorBinding::Wrap(aCx, this);
 }
 
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -138,16 +138,17 @@ if CONFIG['MOZ_B2G']:
         'MediaPermissionGonk.h',
     ]
 
 EXPORTS.mozilla.dom += [
     'AudioStreamTrack.h',
     'AudioTrack.h',
     'AudioTrackList.h',
     'GetUserMediaRequest.h',
+    'MediaDevices.h',
     'MediaStreamError.h',
     'MediaStreamTrack.h',
     'TextTrack.h',
     'TextTrackCue.h',
     'TextTrackCueList.h',
     'TextTrackList.h',
     'TextTrackRegion.h',
     'VideoPlaybackQuality.h',
@@ -173,16 +174,17 @@ UNIFIED_SOURCES += [
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaDecoderStateMachineScheduler.cpp',
+    'MediaDevices.cpp',
     'MediaManager.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
     'MediaStreamError.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
     'MediaTaskQueue.cpp',
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Preferences.h"
 #include "PromiseCallback.h"
 #include "PromiseNativeHandler.h"
 #include "PromiseWorkerProxy.h"
 #include "nsContentUtils.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -350,16 +351,21 @@ Promise::MaybeResolve(JSContext* aCx,
 void
 Promise::MaybeReject(JSContext* aCx,
                      JS::Handle<JS::Value> aValue)
 {
   MaybeRejectInternal(aCx, aValue);
 }
 
 void
+Promise::MaybeReject(const nsRefPtr<MediaStreamError>& aArg) {
+  MaybeSomething(aArg, &Promise::MaybeReject);
+}
+
+void
 Promise::PerformMicroTaskCheckpoint()
 {
   CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
   nsTArray<nsRefPtr<nsIRunnable>>& microtaskQueue =
     runtime->GetPromiseMicroTaskQueue();
 
   while (!microtaskQueue.IsEmpty()) {
     nsRefPtr<nsIRunnable> runnable = microtaskQueue.ElementAt(0);
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -24,16 +24,17 @@
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class AnyCallback;
 class DOMError;
+class MediaStreamError;
 class PromiseCallback;
 class PromiseInit;
 class PromiseNativeHandler;
 class PromiseDebugging;
 
 class Promise;
 class PromiseReportRejectFeature : public workers::WorkerFeature
 {
@@ -94,16 +95,19 @@ public:
   void MaybeResolve(const T& aArg) {
     MaybeSomething(aArg, &Promise::MaybeResolve);
   }
 
   inline void MaybeReject(nsresult aArg) {
     MOZ_ASSERT(NS_FAILED(aArg));
     MaybeSomething(aArg, &Promise::MaybeReject);
   }
+
+  void MaybeReject(const nsRefPtr<MediaStreamError>& aArg);
+
   // DO NOT USE MaybeRejectBrokenly with in new code.  Promises should be
   // rejected with Error instances.
   // Note: MaybeRejectBrokenly is a template so we can use it with DOMError
   // without instantiating the DOMError specialization of MaybeSomething in
   // every translation unit that includes this header, because that would
   // require use to include DOMError.h either here or in all those translation
   // units.
   template<typename T>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -631,16 +631,18 @@ var interfaceNamesInGlobalScope =
     "KeyEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "LocalMediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "MediaDevices",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaElementAudioSourceNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaError",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeyError", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaEncryptedEvent", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaDevices.webidl
@@ -0,0 +1,23 @@
+/* -*- 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/.
+ *
+ * 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.
+ */
+
+[Func="Navigator::HasUserMediaSupport"]
+interface MediaDevices : EventTarget {
+//    attribute EventHandler ondevicechange;
+//
+//    void enumerateDevices (MediaDeviceInfoCallback resultCallback);
+//
+//  static Dictionary getSupportedConstraints (DOMString kind);
+
+  [Throws, Func="Navigator::HasUserMediaSupport"]
+  Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
+};
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -353,16 +353,20 @@ partial interface Navigator {
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 callback NavigatorUserMediaSuccessCallback = void (MediaStream stream);
 callback NavigatorUserMediaErrorCallback = void (MediaStreamError error);
 
 partial interface Navigator {
   [Throws, Func="Navigator::HasUserMediaSupport"]
+  readonly attribute MediaDevices mediaDevices;
+
+  // Deprecated. Use mediaDevices.getUserMedia instead.
+  [Throws, Func="Navigator::HasUserMediaSupport"]
   void mozGetUserMedia(MediaStreamConstraints constraints,
                        NavigatorUserMediaSuccessCallback successCallback,
                        NavigatorUserMediaErrorCallback errorCallback);
 };
 
 // nsINavigatorUserMedia
 callback MozGetUserMediaDevicesSuccessCallback = void (nsIVariant? devices);
 partial interface Navigator {
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -250,16 +250,17 @@ WEBIDL_FILES = [
     'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'ListBoxObject.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
+    'MediaDevices.webidl',
     'MediaElementAudioSourceNode.webidl',
     'MediaError.webidl',
     'MediaList.webidl',
     'MediaQueryList.webidl',
     'MediaRecorder.webidl',
     'MediaSource.webidl',
     'MediaStream.webidl',
     'MediaStreamAudioDestinationNode.webidl',