Backed out changeset 6e588c56764b (bug 909542) for Gaia unit test perma-fail.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 14 Feb 2014 08:27:44 -0500
changeset 179955 b47b10e405a5831013e362918519a1e7f3bb4dd3
parent 179954 f65bbcfa1598dad0b5533529fac2ad2a63a9b8ab
child 179956 bdfdcaeccbcfb938cade449aa978f22935c49f25
push id5439
push userffxbld
push dateMon, 17 Mar 2014 23:08:15 +0000
treeherdermozilla-aurora@c0befb3c8038 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs909542
milestone30.0a1
backs out6e588c56764b60b96ed24af813b955db02a3f936
Backed out changeset 6e588c56764b (bug 909542) for Gaia unit test perma-fail.
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
content/media/webrtc/MediaEngineWebRTC.cpp
content/media/webrtc/MediaEngineWebRTC.h
content/media/webrtc/MediaEngineWebRTCVideo.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/bindings/Bindings.conf
dom/camera/AutoRwLock.h
dom/camera/CameraCommon.h
dom/camera/CameraControlImpl.cpp
dom/camera/CameraControlImpl.h
dom/camera/CameraControlListener.h
dom/camera/CameraRecorderProfiles.cpp
dom/camera/CameraRecorderProfiles.h
dom/camera/DOMCameraCapabilities.cpp
dom/camera/DOMCameraCapabilities.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/camera/DOMCameraControlListener.cpp
dom/camera/DOMCameraControlListener.h
dom/camera/DOMCameraManager.cpp
dom/camera/DOMCameraManager.h
dom/camera/DOMCameraPreview.cpp
dom/camera/DOMCameraPreview.h
dom/camera/FallbackCameraCapabilities.cpp
dom/camera/FallbackCameraControl.cpp
dom/camera/FallbackCameraManager.cpp
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkCameraHwMgr.cpp
dom/camera/GonkCameraHwMgr.h
dom/camera/GonkCameraManager.cpp
dom/camera/GonkCameraParameters.cpp
dom/camera/GonkCameraParameters.h
dom/camera/GonkCameraSource.cpp
dom/camera/GonkRecorderProfiles.cpp
dom/camera/GonkRecorderProfiles.h
dom/camera/ICameraControl.h
dom/camera/moz.build
dom/camera/nsIDOMCameraManager.idl
dom/camera/test/mochitest.ini
dom/camera/test/test_camera.html
dom/camera/test/test_camera_2.html
dom/media/MediaManager.cpp
dom/webidl/CameraCapabilities.webidl
dom/webidl/CameraControl.webidl
dom/webidl/CameraManager.webidl
dom/webidl/moz.build
js/xpconnect/src/dictionary_helper_gen.conf
mobile/android/installer/package-manifest.in
testing/mochitest/b2g-debug.json
widget/gonk/nativewindow/GonkNativeWindowICS.cpp
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -162,16 +162,17 @@
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_cellbroadcast.xpt
 @BINPATH@/components/dom_wappush.xpt
 @BINPATH@/components/dom_mobileconnection.xpt
 #endif
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
+@BINPATH@/components/dom_camera.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_contacts.xpt
 @BINPATH@/components/dom_alarm.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_devicestorage.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_file.xpt
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -192,16 +192,17 @@
 @BINPATH@/components/dom_messages.xpt
 #endif
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_system.xpt
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
+@BINPATH@/components/dom_camera.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_alarm.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_devicestorage.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_file.xpt
 @BINPATH@/components/dom_geolocation.xpt
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -37,75 +37,76 @@ GetUserMediaLog()
 #include "AndroidJNIWrapper.h"
 #include "AndroidBridge.h"
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
-
+#ifndef MOZ_B2G_CAMERA
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
   : mMutex("mozilla::MediaEngineWebRTC")
   , mVideoEngine(nullptr)
   , mVoiceEngine(nullptr)
   , mVideoEngineInit(false)
   , mAudioEngineInit(false)
   , mHasTabVideoSource(false)
 {
-#ifndef MOZ_B2G_CAMERA
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
   }
-#else
-  AsyncLatencyLogger::Get()->AddRef();
-#endif
   if (aPrefs.mLoadAdapt) {
       mLoadMonitor = new LoadMonitor();
       mLoadMonitor->Init(mLoadMonitor);
   }
 }
+#endif
+
 
 void
 MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
 #ifdef MOZ_B2G_CAMERA
   MutexAutoLock lock(mMutex);
+  if (!mCameraManager) {
+    return;
+  }
 
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
    * Enumeration is not neccessary if GIPS reports the same set of devices
    * for a given instance of the engine. Likewise, if a device was plugged out,
    * mVideoSources must be updated.
    */
   int num = 0;
   nsresult result;
-  result = ICameraControl::GetNumberOfCameras(num);
+  result = mCameraManager->GetNumberOfCameras(num);
   if (num <= 0 || result != NS_OK) {
     return;
   }
 
   for (int i = 0; i < num; i++) {
     nsCString cameraName;
-    result = ICameraControl::GetCameraName(i, cameraName);
+    result = mCameraManager->GetCameraName(i, cameraName);
     if (result != NS_OK) {
       continue;
     }
 
     nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
     NS_ConvertUTF8toUTF16 uuid(cameraName);
     if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
       // We've already seen this device, just append.
       aVSources->AppendElement(vSource.get());
     } else {
-      vSource = new MediaEngineWebRTCVideoSource(i);
+      vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i, mWindowId);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
   return;
 #else
   webrtc::ViEBase* ptrViEBase;
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -41,18 +41,19 @@
 #include "webrtc/voice_engine/include/voe_audio_processing.h"
 
 // Video Engine
 #include "webrtc/video_engine/include/vie_base.h"
 #include "webrtc/video_engine/include/vie_codec.h"
 #include "webrtc/video_engine/include/vie_render.h"
 #include "webrtc/video_engine/include/vie_capture.h"
 #ifdef MOZ_B2G_CAMERA
-#include "CameraControlListener.h"
-#include "ICameraControl.h"
+#include "CameraPreviewMediaStream.h"
+#include "DOMCameraManager.h"
+#include "GonkCameraControl.h"
 #include "ImageContainer.h"
 #include "nsGlobalWindow.h"
 #include "prprf.h"
 #endif
 
 #include "NullTransport.h"
 
 namespace mozilla {
@@ -67,45 +68,55 @@ class GetCameraNameRunnable;
  *
  * On B2G platform, member data may accessed from different thread after construction:
  *
  * MediaThread:
  *   mState, mImage, mWidth, mHeight, mCapability, mPrefs, mDeviceName, mUniqueId, mInitDone,
  *   mSources, mImageContainer, mSources, mState, mImage, mLastCapture
  *
  * MainThread:
- *   mCaptureIndex, mWindowId,
+ *   mDOMCameraControl, mCaptureIndex, mCameraThread, mWindowId, mCameraManager,
  *   mNativeCameraControl, mPreviewStream, mState, mLastCapture, mWidth, mHeight
  *
  * Where mWidth, mHeight, mImage are protected by mMonitor
  *       mState, mLastCapture is protected by mCallbackMonitor
  * Other variable is accessed only from single thread
  */
 class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource
                                    , public nsRunnable
 #ifdef MOZ_B2G_CAMERA
-                                   , public CameraControlListener
+                                   , public nsICameraGetCameraCallback
+                                   , public nsICameraPreviewStreamCallback
+                                   , public nsICameraTakePictureCallback
+                                   , public nsICameraReleaseCallback
+                                   , public nsICameraErrorCallback
+                                   , public CameraPreviewFrameCallback
 #else
                                    , public webrtc::ExternalRenderer
 #endif
 {
 public:
 #ifdef MOZ_B2G_CAMERA
-  MediaEngineWebRTCVideoSource(int aIndex)
-    : mCameraControl(nullptr)
+  MediaEngineWebRTCVideoSource(nsDOMCameraManager* aCameraManager,
+    int aIndex, uint64_t aWindowId)
+    : mCameraManager(aCameraManager)
+    , mNativeCameraControl(nullptr)
+    , mPreviewStream(nullptr)
+    , mWindowId(aWindowId)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mCaptureIndex(aIndex)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr)
   {
     mState = kReleased;
+    NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
     Init();
   }
 #else
   // ViEExternalRenderer.
   virtual int FrameSizeChange(unsigned int, unsigned int, unsigned int);
   virtual int DeliverFrame(unsigned char*,int, uint32_t , int64_t,
                            void *handle);
   /**
@@ -151,27 +162,30 @@ public:
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 #ifdef MOZ_B2G_CAMERA
-  void OnHardwareStateChange(HardwareState aState);
-  void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration);
-  bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
-  void OnError(CameraErrorContext aContext, const nsACString& aError);
-  void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
+  NS_DECL_NSICAMERAGETCAMERACALLBACK
+  NS_DECL_NSICAMERAPREVIEWSTREAMCALLBACK
+  NS_DECL_NSICAMERATAKEPICTURECALLBACK
+  NS_DECL_NSICAMERARELEASECALLBACK
+  NS_DECL_NSICAMERAERRORCALLBACK
 
   void AllocImpl();
   void DeallocImpl();
   void StartImpl(webrtc::CaptureCapability aCapability);
   void StopImpl();
   void SnapshotImpl();
+
+  virtual void OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage);
+
 #endif
 
   // This runnable is for creating a temporary file on the main thread.
   NS_IMETHODIMP
   Run()
   {
     nsCOMPtr<nsIFile> tmp;
     nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp));
@@ -193,18 +207,30 @@ private:
   static const unsigned int KMaxUniqueIdLength = 256;
 
   // Initialize the needed Video engine interfaces.
   void Init();
   void Shutdown();
 
   // Engine variables.
 #ifdef MOZ_B2G_CAMERA
-  nsRefPtr<ICameraControl> mCameraControl;
+  // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
+  // Their life time is always much longer than this object. Use a raw-pointer
+  // here should be safe.
+  // We need raw pointer here since such DOM-object should not addref/release on
+  // any thread other than main thread, but we must use this object for now. To
+  // avoid any bad thing do to addref/release DOM-object on other thread, we use
+  // raw-pointer for now.
+  nsDOMCameraManager* mCameraManager;
+  nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
+  nsRefPtr<nsGonkCameraControl> mNativeCameraControl;
+  nsRefPtr<DOMCameraPreview> mPreviewStream;
+  uint64_t mWindowId;
   mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling
+  nsRefPtr<nsIThread> mCameraThread;
   nsRefPtr<nsIDOMFile> mLastCapture;
 #else
   webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free.
   webrtc::ViEBase* mViEBase;
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
 #endif
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
@@ -320,17 +346,34 @@ private:
   webrtc::NsModes  mNoiseSuppress;
 
   NullTransport *mNullTransport;
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
 public:
+#ifdef MOZ_B2G_CAMERA
+  MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId)
+    : mMutex("mozilla::MediaEngineWebRTC")
+    , mVideoEngine(nullptr)
+    , mVoiceEngine(nullptr)
+    , mVideoEngineInit(false)
+    , mAudioEngineInit(false)
+    , mCameraManager(aCameraManager)
+    , mWindowId(aWindowId)
+    , mHasTabVideoSource(false)
+  {
+    AsyncLatencyLogger::Get(true)->AddRef();
+    mLoadMonitor = new LoadMonitor();
+    mLoadMonitor->Init(mLoadMonitor);
+  }
+#else
   MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
+#endif
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
   }
 
   // Clients should ensure to clean-up sources video/audio sources
@@ -352,14 +395,26 @@ private:
   bool mAudioEngineInit;
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 
-  nsRefPtr<LoadMonitor> mLoadMonitor;
+#ifdef MOZ_B2G_CAMERA
+  // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
+  // Their life time is always much longer than this object. Use a raw-pointer
+  // here should be safe.
+  // We need raw pointer here since such DOM-object should not addref/release on
+  // any thread other than main thread, but we must use this object for now. To
+  // avoid any bad thing do to addref/release DOM-object on other thread, we use
+  // raw-pointer for now.
+  nsDOMCameraManager* mCameraManager;
+  uint64_t mWindowId;
+#endif
+
+   nsRefPtr<LoadMonitor> mLoadMonitor;
 };
 
 }
 
 #endif /* NSMEDIAENGINEWEBRTC_H_ */
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -307,19 +307,17 @@ MediaEngineWebRTCVideoSource::Deallocate
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   LOG((__FUNCTION__));
-#ifndef MOZ_B2G_CAMERA
   int error = 0;
-#endif
   if (!mInitDone || !aStream) {
     return NS_ERROR_FAILURE;
   }
 
   mSources.AppendElement(aStream);
 
   aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment());
   aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
@@ -410,17 +408,17 @@ MediaEngineWebRTCVideoSource::Snapshot(u
  * constructor and destructor respectively.
  */
 
 void
 MediaEngineWebRTCVideoSource::Init()
 {
 #ifdef MOZ_B2G_CAMERA
   nsAutoCString deviceName;
-  ICameraControl::GetCameraName(mCaptureIndex, deviceName);
+  mCameraManager->GetCameraName(mCaptureIndex, deviceName);
   CopyUTF8toUTF16(deviceName, mDeviceName);
   CopyUTF8toUTF16(deviceName, mUniqueId);
 #else
   // fix compile warning for these being unused. (remove once used)
   (void) mFps;
   (void) mMinFps;
 
   LOG((__FUNCTION__));
@@ -489,101 +487,131 @@ MediaEngineWebRTCVideoSource::Shutdown()
 
 #ifdef MOZ_B2G_CAMERA
 
 // All these functions must be run on MainThread!
 void
 MediaEngineWebRTCVideoSource::AllocImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mCameraControl = ICameraControl::Create(mCaptureIndex, nullptr);
-  mCameraControl->AddListener(this);
+  mDOMCameraControl = new nsDOMCameraControl(mCaptureIndex,
+                                             mCameraThread,
+                                             this,
+                                             this,
+                                             nsGlobalWindow::GetInnerWindowWithId(mWindowId));
+  mCameraManager->Register(mDOMCameraControl);
 }
 
 void
 MediaEngineWebRTCVideoSource::DeallocImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mCameraControl->ReleaseHardware();
-  mCameraControl = nullptr;
+  mNativeCameraControl->ReleaseHardware(this, this);
+  mNativeCameraControl = nullptr;
 }
 
 void
 MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
-  ICameraControl::Configuration config;
-  config.mMode = ICameraControl::kPictureMode;
-  config.mPreviewSize.width = aCapability.width;
-  config.mPreviewSize.height = aCapability.height;
-  mCameraControl->SetConfiguration(config);
-  mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, config.mPreviewSize);
+  idl::CameraSize size;
+  size.width = aCapability.width;
+  size.height = aCapability.height;
+  mNativeCameraControl->GetPreviewStream(size, this, this);
 }
 
 void
 MediaEngineWebRTCVideoSource::StopImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mCameraControl->StopPreview();
+  mNativeCameraControl->StopPreview();
+  mPreviewStream = nullptr;
 }
 
 void
 MediaEngineWebRTCVideoSource::SnapshotImpl() {
+
   MOZ_ASSERT(NS_IsMainThread());
-  mCameraControl->TakePicture();
+
+  idl::CameraSize size;
+  size.width = mCapability.width;
+  size.height = mCapability.height;
+
+  idl::CameraPosition cameraPosition;
+  cameraPosition.latitude = NAN;
+  cameraPosition.longitude = NAN;
+  cameraPosition.altitude = NAN;
+  cameraPosition.timestamp = NAN;
+
+  mNativeCameraControl->TakePicture(size, 0, NS_LITERAL_STRING("jpeg"), cameraPosition, PR_Now() / 1000000, this, this);
 }
 
-void
-MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState)
-{
+// nsICameraGetCameraCallback
+nsresult
+MediaEngineWebRTCVideoSource::HandleEvent(nsISupports* /* unused */) {
+  MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
-  if (aState == CameraControlListener::kHardwareOpen) {
-    mState = kAllocated;
-  } else {
-    mState = kReleased;
-    mCameraControl->RemoveListener(this);
-  }
+  mNativeCameraControl = static_cast<nsGonkCameraControl*>(mDOMCameraControl->GetNativeCameraControl().get());
+  mState = kAllocated;
   mCallbackMonitor.Notify();
+  return NS_OK;
 }
 
-void
-MediaEngineWebRTCVideoSource::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
-{
+// nsICameraPreviewStreamCallback
+nsresult
+MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMMediaStream* stream) {
+  MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
+  mPreviewStream = static_cast<DOMCameraPreview*>(stream);
+  mPreviewStream->Start();
+  CameraPreviewMediaStream* cameraStream = static_cast<CameraPreviewMediaStream*>(mPreviewStream->GetStream());
+  cameraStream->SetFrameCallback(this);
   mState = kStarted;
   mCallbackMonitor.Notify();
+  return NS_OK;
 }
 
-void
-MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, const nsACString& aError)
-{
+// nsICameraTakePictureCallback
+nsresult
+MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMBlob* picture) {
+  MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
+  mLastCapture = static_cast<nsIDOMFile*>(picture);
   mCallbackMonitor.Notify();
+  return NS_OK;
+}
+
+// nsICameraReleaseCallback
+nsresult
+MediaEngineWebRTCVideoSource::HandleEvent() {
+  MOZ_ASSERT(NS_IsMainThread());
+  ReentrantMonitorAutoEnter sync(mCallbackMonitor);
+  mState = kReleased;
+  mCallbackMonitor.Notify();
+  return NS_OK;
 }
 
-void
-MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
-{
+// nsICameraErrorCallback
+nsresult
+MediaEngineWebRTCVideoSource::HandleEvent(const nsAString& error) {
+  MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter sync(mCallbackMonitor);
-  mLastCapture =
-    static_cast<nsIDOMFile*>(new nsDOMMemoryFile(static_cast<void*>(aData),
-                                                 static_cast<uint64_t>(aLength),
-                                                 aMimeType));
   mCallbackMonitor.Notify();
+  return NS_OK;
 }
 
-bool
-MediaEngineWebRTCVideoSource::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) {
+//Except this one. This callback should called on camera preview thread.
+void
+MediaEngineWebRTCVideoSource::OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage) {
   MonitorAutoLock enter(mMonitor);
   if (mState == kStopped) {
-    return false;
+    return;
   }
   mImage = aImage;
-  if (mWidth != static_cast<int>(aWidth) || mHeight != static_cast<int>(aHeight)) {
-    mWidth = aWidth;
-    mHeight = aHeight;
+  if (mWidth != aIntrinsicSize.width || mHeight != aIntrinsicSize.height) {
+    mWidth = aIntrinsicSize.width;
+    mHeight = aIntrinsicSize.height;
     LOG(("Video FrameSizeChange: %ux%u", mWidth, mHeight));
   }
-  return true; // return true because we're accepting the frame
 }
 #endif
 
 }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -155,16 +155,17 @@
 #include "nsIDOMIccManager.h"
 #include "nsIDOMMobileConnection.h"
 #endif // MOZ_B2G_RIL
 
 #ifdef MOZ_B2G_FM
 #include "FMRadio.h"
 #endif
 
+#include "nsIDOMCameraManager.h"
 #include "nsIDOMGlobalObjectConstructor.h"
 #include "nsIDOMLockedFile.h"
 #include "nsDebug.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Likely.h"
 #include "WindowNamedPropertiesHandler.h"
 #include "nsIInterfaceInfoManager.h"
@@ -464,16 +465,19 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(CSSPageRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozIccManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
+  NS_DEFINE_CLASSINFO_DATA(CameraCapabilities, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(UserDataHandler, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(XPathNamespace, nsDOMGenericSH,
@@ -1167,16 +1171,20 @@ nsDOMClassInfo::Init()
 #ifdef MOZ_B2G_RIL
   DOM_CLASSINFO_MAP_BEGIN(MozIccManager, nsIDOMMozIccManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozIccManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
 #endif
 
+  DOM_CLASSINFO_MAP_BEGIN(CameraCapabilities, nsICameraCapabilities)
+    DOM_CLASSINFO_MAP_ENTRY(nsICameraCapabilities)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFeatureValuesRule, nsIDOMCSSFontFeatureValuesRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -98,16 +98,18 @@ DOMCI_CLASS(MozCSSKeyframeRule)
 DOMCI_CLASS(MozCSSKeyframesRule)
 
 DOMCI_CLASS(CSSPageRule)
 
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozIccManager)
 #endif
 
+DOMCI_CLASS(CameraCapabilities)
+
 DOMCI_CLASS(LockedFile)
 
 DOMCI_CLASS(CSSFontFeatureValuesRule)
 
 DOMCI_CLASS(UserDataHandler)
 DOMCI_CLASS(XPathNamespace)
 DOMCI_CLASS(XULControlElement)
 DOMCI_CLASS(XULLabeledControlElement)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -175,21 +175,16 @@ DOMInterfaces = {
     'headerFile': 'BluetoothDevice.h'
 },
 
 'BluetoothManager': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
     'headerFile': 'BluetoothManager.h'
 },
 
-'CameraCapabilities': {
-    'nativeType': 'mozilla::dom::CameraCapabilities',
-    'headerFile': 'DOMCameraCapabilities.h'
-},
-
 'CameraControl': {
     'nativeType': 'mozilla::nsDOMCameraControl',
     'headerFile': 'DOMCameraControl.h',
     'binaryNames': {
         "release": "ReleaseHardware"
     }
 },
 
@@ -1962,8 +1957,20 @@ addExternalIface('SVGNumber')
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
 addExternalIface('UserDataHandler')
 addExternalIface('XPathResult', nativeType='nsISupports')
 addExternalIface('XPathExpression')
 addExternalIface('XPathNSResolver')
 addExternalIface('XULCommandDispatcher')
 addExternalIface('DataTransfer', notflattened=True)
+addExternalIface('GetCameraCallback', nativeType='nsICameraGetCameraCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraErrorCallback', nativeType='nsICameraErrorCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraCapabilities', nativeType='nsICameraCapabilities', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraAutoFocusCallback', nativeType='nsICameraAutoFocusCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraShutterCallback', nativeType='nsICameraShutterCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraClosedCallback', nativeType='nsICameraClosedCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraTakePictureCallback', nativeType='nsICameraTakePictureCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraReleaseCallback', nativeType='nsICameraReleaseCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraStartRecordingCallback', nativeType='nsICameraStartRecordingCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraPreviewStateChange', nativeType='nsICameraPreviewStateChange', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraPreviewStreamCallback', nativeType='nsICameraPreviewStreamCallback', headerFile='nsIDOMCameraManager.h')
+addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
deleted file mode 100644
--- a/dom/camera/AutoRwLock.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* 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 RWLOCK_AUTO_ENTER_H
-#define RWLOCK_AUTO_ENTER_H
-
-#include "prrwlock.h"
-#include "mozilla/Assertions.h"
-
-class RwLockAutoEnterRead
-{
-public:
-  RwLockAutoEnterRead(PRRWLock* aRwLock)
-    : mRwLock(aRwLock)
-  {
-    MOZ_ASSERT(mRwLock);
-    PR_RWLock_Rlock(mRwLock);
-  }
-
-  ~RwLockAutoEnterRead()
-  {
-    PR_RWLock_Unlock(mRwLock);
-  }
-
-protected:
-  PRRWLock* mRwLock;
-};
-
-class RwLockAutoEnterWrite
-{
-public:
-  RwLockAutoEnterWrite(PRRWLock* aRwLock)
-    : mRwLock(aRwLock)
-  {
-    MOZ_ASSERT(mRwLock);
-    PR_RWLock_Wlock(mRwLock);
-  }
-
-  ~RwLockAutoEnterWrite()
-  {
-    PR_RWLock_Unlock(mRwLock);
-  }
-
-protected:
-  PRRWLock* mRwLock;
-};
-
-#endif // RWLOCK_AUTO_ENTER_H
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -14,16 +14,17 @@
 #define __func__ __FILE__
 #endif
 #endif
 
 #ifndef NAN
 #define NAN std::numeric_limits<double>::quiet_NaN()
 #endif
 
+#include "nsIDOMCameraManager.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetCameraLog();
 #define DOM_CAMERA_LOG( type, ... ) PR_LOG(GetCameraLog(), (PRLogModuleLevel)type, ( __VA_ARGS__ ))
 #else
 #define DOM_CAMERA_LOG( type, ... )
 #endif
@@ -56,16 +57,54 @@ enum {
 #else
 #define DOM_CAMERA_LOGR( ... )
 #endif
 #define DOM_CAMERA_LOGT( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_TRACE, __VA_ARGS__ )
 #define DOM_CAMERA_LOGI( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_INFO, __VA_ARGS__ )
 #define DOM_CAMERA_LOGW( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
 #define DOM_CAMERA_LOGE( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
 
+enum {
+  CAMERA_PARAM_EFFECT,
+  CAMERA_PARAM_WHITEBALANCE,
+  CAMERA_PARAM_SCENEMODE,
+  CAMERA_PARAM_FLASHMODE,
+  CAMERA_PARAM_FOCUSMODE,
+  CAMERA_PARAM_ZOOM,
+  CAMERA_PARAM_METERINGAREAS,
+  CAMERA_PARAM_FOCUSAREAS,
+  CAMERA_PARAM_FOCALLENGTH,
+  CAMERA_PARAM_FOCUSDISTANCENEAR,
+  CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
+  CAMERA_PARAM_FOCUSDISTANCEFAR,
+  CAMERA_PARAM_EXPOSURECOMPENSATION,
+  CAMERA_PARAM_PICTURESIZE,
+  CAMERA_PARAM_THUMBNAILSIZE,
+  CAMERA_PARAM_THUMBNAILQUALITY,
+  CAMERA_PARAM_SENSORANGLE,
+
+  CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
+  CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
+  CAMERA_PARAM_SUPPORTED_PICTURESIZES,
+  CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
+  CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
+  CAMERA_PARAM_SUPPORTED_SCENEMODES,
+  CAMERA_PARAM_SUPPORTED_EFFECTS,
+  CAMERA_PARAM_SUPPORTED_FLASHMODES,
+  CAMERA_PARAM_SUPPORTED_FOCUSMODES,
+  CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
+  CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
+  CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
+  CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
+  CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
+  CAMERA_PARAM_SUPPORTED_ZOOM,
+  CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
+  CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
+};
+
 #ifdef PR_LOGGING
 
 static inline void nsLogAddRefCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, uint32_t size)
 {
   if (count == 1) {
     DOM_CAMERA_LOGR("++++++++++++++++++++++++++++++++++++++++");
   }
   DOM_CAMERA_LOGR("%s:%d : CAMREF-ADD(%s): this=%p, mRefCnt=%d\n", file, line, clazz, p, count);
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -1,576 +1,523 @@
 /* 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 "CameraControlImpl.h"
 #include "base/basictypes.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/unused.h"
-#include "nsIWeakReferenceUtils.h"
+#include "DOMCameraPreview.h"
 #include "CameraRecorderProfiles.h"
+#include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
 #include "DeviceStorageFileDescriptor.h"
-#include "CameraControlListener.h"
 
 using namespace mozilla;
-
-nsWeakPtr CameraControlImpl::sCameraThread;
+using namespace mozilla::dom;
+using namespace mozilla::idl;
 
-CameraControlImpl::CameraControlImpl(uint32_t aCameraId)
+CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
   : mCameraId(aCameraId)
-  , mPreviewState(CameraControlListener::kPreviewStopped)
-  , mHardwareState(CameraControlListener::kHardwareClosed)
+  , mCameraThread(aCameraThread)
+  , mWindowId(aWindowId)
+  , mFileFormat()
+  , mMaxMeteringAreas(0)
+  , mMaxFocusAreas(0)
+  , mPreviewState(PREVIEW_STOPPED)
+  , mDOMPreview(nullptr)
+  , mAutoFocusOnSuccessCb(nullptr)
+  , mAutoFocusOnErrorCb(nullptr)
+  , mTakePictureOnSuccessCb(nullptr)
+  , mTakePictureOnErrorCb(nullptr)
+  , mOnShutterCb(nullptr)
+  , mOnClosedCb(nullptr)
+  , mOnRecorderStateChangeCb(nullptr)
+  , mOnPreviewStateChangeCb(nullptr)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  // reuse the same camera thread to conserve resources
-  nsCOMPtr<nsIThread> ct = do_QueryReferent(sCameraThread);
-  if (ct) {
-    mCameraThread = ct.forget();
-  } else {
-    nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
-    unused << rv; // swallow rv to suppress a compiler warning when the macro
-                  // is #defined to nothing (i.e. in non-DEBUG builds).
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    // keep a weak reference to the new thread
-    sCameraThread = do_GetWeakReference(mCameraThread);
-  }
-
-  mListenerLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock");
 }
 
 CameraControlImpl::~CameraControlImpl()
 {
-  if (mListenerLock) {
-    PR_DestroyRWLock(mListenerLock);
-    mListenerLock = nullptr;
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+// Helpers for string properties.
+nsresult
+CameraControlImpl::Set(uint32_t aKey, const nsAString& aValue)
+{
+  SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get());
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, nsAString& aValue)
+{
+  const char* value = GetParameterConstChar(aKey);
+  if (!value) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aValue.AssignASCII(value);
+  return NS_OK;
+}
+
+// Helpers for doubles.
+nsresult
+CameraControlImpl::Set(uint32_t aKey, double aValue)
+{
+  SetParameter(aKey, aValue);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, double* aValue)
+{
+  MOZ_ASSERT(aValue);
+  *aValue = GetParameterDouble(aKey);
+  return NS_OK;
+}
+
+// Helper for weighted regions.
+nsresult
+CameraControlImpl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
+{
+  if (aLimit == 0) {
+    DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
+    return NS_OK;
+  }
+
+  if (!aValue.isObject()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length = 0;
+
+  JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
+  if (!JS_GetArrayLength(aCx, regions, &length)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
+  if (length > aLimit) {
+    length = aLimit;
+  }
+
+  nsTArray<CameraRegion> regionArray;
+  regionArray.SetCapacity(length);
+
+  for (uint32_t i = 0; i < length; ++i) {
+    JS::Rooted<JS::Value> v(aCx);
+
+    if (!JS_GetElement(aCx, regions, i, &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    CameraRegion* r = regionArray.AppendElement();
+    /**
+     * These are the default values.  We can remove these when the xpidl
+     * dictionary parser gains the ability to grok default values.
+     */
+    r->top = -1000;
+    r->left = -1000;
+    r->bottom = 1000;
+    r->right = 1000;
+    r->weight = 1000;
+
+    nsresult rv = r->Init(aCx, v.address());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n",
+      i,
+      r->top,
+      r->left,
+      r->bottom,
+      r->right,
+      r->weight
+    );
+  }
+  SetParameter(aKey, regionArray);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
+{
+  nsTArray<CameraRegion> regionArray;
+
+  GetParameter(aKey, regionArray);
+
+  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
+
+  uint32_t length = regionArray.Length();
+  DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
+
+  for (uint32_t i = 0; i < length; ++i) {
+    CameraRegion* r = &regionArray[i];
+    JS::Rooted<JS::Value> v(aCx);
+
+    JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+    if (!o) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    DOM_CAMERA_LOGI("top=%d\n", r->top);
+    v = INT_TO_JSVAL(r->top);
+    if (!JS_SetProperty(aCx, o, "top", v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("left=%d\n", r->left);
+    v = INT_TO_JSVAL(r->left);
+    if (!JS_SetProperty(aCx, o, "left", v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
+    v = INT_TO_JSVAL(r->bottom);
+    if (!JS_SetProperty(aCx, o, "bottom", v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("right=%d\n", r->right);
+    v = INT_TO_JSVAL(r->right);
+    if (!JS_SetProperty(aCx, o, "right", v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("weight=%d\n", r->weight);
+    v = INT_TO_JSVAL(r->weight);
+    if (!JS_SetProperty(aCx, o, "weight", v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (!JS_SetElement(aCx, array, i, o)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aValue = JS::ObjectValue(*array);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(nsICameraShutterCallback* aOnShutter)
+{
+  mOnShutterCb = new nsMainThreadPtrHolder<nsICameraShutterCallback>(aOnShutter);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraShutterCallback** aOnShutter)
+{
+  *aOnShutter = mOnShutterCb;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(nsICameraClosedCallback* aOnClosed)
+{
+  mOnClosedCb = new nsMainThreadPtrHolder<nsICameraClosedCallback>(aOnClosed);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed)
+{
+  *aOnClosed = mOnClosedCb;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(nsICameraRecorderStateChange* aOnRecorderStateChange)
+{
+  mOnRecorderStateChangeCb = new nsMainThreadPtrHolder<nsICameraRecorderStateChange>(aOnRecorderStateChange);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange)
+{
+  *aOnRecorderStateChange = mOnRecorderStateChangeCb;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(nsICameraPreviewStateChange* aOnPreviewStateChange)
+{
+  mOnPreviewStateChangeCb = new nsMainThreadPtrHolder<nsICameraPreviewStateChange>(aOnPreviewStateChange);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraPreviewStateChange** aOnPreviewStateChange)
+{
+  *aOnPreviewStateChange = mOnPreviewStateChangeCb;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(uint32_t aKey, const idl::CameraSize& aSize)
+{
+  SetParameter(aKey, aSize);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, idl::CameraSize& aSize)
+{
+  GetParameter(aKey, aSize);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, int32_t* aValue)
+{
+  MOZ_ASSERT(aValue);
+  *aValue = GetParameterInt32(aKey);
+  return NS_OK;
 }
 
 already_AddRefed<RecorderProfileManager>
 CameraControlImpl::GetRecorderProfileManager()
 {
   return GetRecorderProfileManagerImpl();
 }
 
 void
 CameraControlImpl::Shutdown()
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-}
-
-void
-CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState)
-{
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread. On Gonk, it may be called from the camera's
-  //  local binder thread, should the mediaserver process die.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  if (aNewState == mHardwareState) {
-    DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
-    return;
-  }
-
-#ifdef PR_LOGGING
-  const char* state[] = { "open", "closed", "failed" };
-  MOZ_ASSERT(aNewState >= 0);
-  if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
-    DOM_CAMERA_LOGI("New hardware state is '%s'\n", state[aNewState]);
-  } else {
-    DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState);
-  }
-#endif
-
-  mHardwareState = aNewState;
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnHardwareStateChange(mHardwareState);
-  }
+  mAutoFocusOnSuccessCb = nullptr;
+  mAutoFocusOnErrorCb = nullptr;
+  mTakePictureOnSuccessCb = nullptr;
+  mTakePictureOnErrorCb = nullptr;
+  mOnShutterCb = nullptr;
+  mOnClosedCb = nullptr;
+  mOnRecorderStateChangeCb = nullptr;
+  mOnPreviewStateChangeCb = nullptr;
 }
 
 void
-CameraControlImpl::OnConfigurationChange()
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length());
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnConfigurationChange(mCurrentConfiguration);
-  }
-}
-
-void
-CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
+CameraControlImpl::OnShutterInternal()
 {
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread. On Gonk, it is called from the camera
-  //  library's auto focus thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnAutoFocusComplete(aAutoFocusSucceeded);
-  }
-}
-
-void
-CameraControlImpl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
-{
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread. On Gonk, it is called from the camera
-  //  library's snapshot thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnTakePictureComplete(aData, aLength, aMimeType);
+  DOM_CAMERA_LOGI("** SNAP **\n");
+  if (mOnShutterCb.get()) {
+    mOnShutterCb->HandleEvent();
   }
 }
 
 void
 CameraControlImpl::OnShutter()
 {
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread. On Gonk, it is called from the camera driver's
-  //  preview thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnShutter();
+  nsCOMPtr<nsIRunnable> onShutter = NS_NewRunnableMethod(this, &CameraControlImpl::OnShutterInternal);
+  nsresult rv = NS_DispatchToMainThread(onShutter);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGW("Failed to dispatch onShutter event to main thread (%d)\n", rv);
   }
 }
 
+class OnClosedTask : public nsRunnable
+{
+public:
+  OnClosedTask(nsMainThreadPtrHandle<nsICameraClosedCallback> onClosed, uint64_t aWindowId)
+    : mOnClosedCb(onClosed)
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~OnClosedTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnClosedCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnClosedCb->HandleEvent();
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraClosedCallback> mOnClosedCb;
+  uint64_t mWindowId;
+};
+
 void
 CameraControlImpl::OnClosed()
 {
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnHardwareStateChange(CameraControlListener::kHardwareClosed);
+  nsCOMPtr<nsIRunnable> onClosed = new OnClosedTask(mOnClosedCb, mWindowId);
+  nsresult rv = NS_DispatchToMainThread(onClosed);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGW("Failed to dispatch onClosed event to main thread (%d)\n", rv);
   }
 }
 
 void
-CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
-                                         int32_t aStatus, int32_t aTrackNumber)
+CameraControlImpl::OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber)
 {
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread. On Gonk, it is called from the media encoder
-  //  thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg).get());
 
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnRecorderStateChange(aState, aStatus, aTrackNumber);
+  nsCOMPtr<nsIRunnable> onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId);
+  nsresult rv = NS_DispatchToMainThread(onRecorderStateChange);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv);
   }
 }
 
 void
-CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState)
+CameraControlImpl::OnPreviewStateChange(PreviewState aNewState)
 {
-  // This callback runs on the Main Thread and the Camera Thread, and
-  //  may run on the local binder thread, should the mediaserver
-  //  process die.
-  RwLockAutoEnterRead lock(mListenerLock);
-
   if (aNewState == mPreviewState) {
     DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
     return;
   }
 
-#ifdef PR_LOGGING
-  const char* state[] = { "stopped", "paused", "started" };
-  MOZ_ASSERT(aNewState >= 0);
-  if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
-    DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]);
-  } else {
-    DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState);
+  nsString msg;
+  switch (aNewState) {
+    case PREVIEW_STOPPED:
+      msg = NS_LITERAL_STRING("stopped");
+      break;
+
+    case PREVIEW_STARTED:
+      msg = NS_LITERAL_STRING("started");
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Preview state can only be PREVIEW_STOPPED or _STARTED!");
   }
-#endif
 
+  // const nsString& aStateMsg)
+  DOM_CAMERA_LOGI("OnPreviewStateChange: '%s'\n", NS_ConvertUTF16toUTF8(msg).get());
   mPreviewState = aNewState;
 
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnPreviewStateChange(mPreviewState);
-  }
-}
-
-bool
-CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
-{
-  // This function runs on neither the Main Thread nor the Camera Thread.
-  //  On Gonk, it is called from the camera driver's preview thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-  DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n",
-    mListeners.Length());
-
-  bool consumed = false;
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    consumed = l->OnNewPreviewFrame(aImage, aWidth, aHeight) || consumed;
-  }
-  return consumed;
-}
-
-void
-CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext,
-                           CameraControlListener::CameraError aError)
-{
-  // This callback can run on threads other than the Main Thread and
-  //  the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
-
-#ifdef PR_LOGGING
-  const char* error[] = { "camera-service-failed", "unknown" };
-  if (static_cast<unsigned int>(aError) < sizeof(error) / sizeof(error[0])) {
-    DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext=%u, msg='%s'\n",
-      aContext, error[aError]);
-  } else {
-    DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, unknown error=%d\n",
-      aContext, aError);
-  }
-#endif
-
-  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
-    CameraControlListener* l = mListeners[i];
-    l->OnError(aContext, aError);
+  nsCOMPtr<nsIRunnable> onPreviewStateChange = new CameraPreviewStateChange(mOnPreviewStateChangeCb, msg, mWindowId);
+  nsresult rv = NS_DispatchToMainThread(onPreviewStateChange);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("Failed to dispatch onPreviewStateChange event to main thread (%d)\n", rv);
   }
 }
 
-// Camera control asynchronous message; these are dispatched from
-//  the Main Thread to the Camera Thread, where they are consumed.
-
-class CameraControlImpl::ControlMessage : public nsRunnable
+nsresult
+CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-public:
-  ControlMessage(CameraControlImpl* aCameraControl,
-                 CameraControlListener::CameraErrorContext aContext)
-    : mCameraControl(aCameraControl)
-    , mContext(aContext)
-  {
-    MOZ_COUNT_CTOR(CameraControlImpl::ControlMessage);
-  }
-
-  virtual ~ControlMessage()
-  {
-    MOZ_COUNT_DTOR(CameraControlImpl::ControlMessage);
-  }
-
-  virtual nsresult RunImpl() = 0;
-
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mCameraControl);
-    MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl->mCameraThread);
-
-    nsresult rv = RunImpl();
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGW("Camera control API failed at %d with 0x%x\n", mContext, rv);
-      // XXXmikeh - do we want to report a more specific error code?
-      mCameraControl->OnError(mContext, CameraControlListener::kErrorApiFailed);
-    }
-
-    return NS_OK;
-  }
-
-protected:
-  nsRefPtr<CameraControlImpl> mCameraControl;
-  CameraControlListener::CameraErrorContext mContext;
-};
-
-nsresult
-CameraControlImpl::SetConfiguration(const Configuration& aConfig)
-{
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext,
-            const Configuration& aConfig)
-      : ControlMessage(aCameraControl, aContext)
-      , mConfig(aConfig)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->SetConfigurationImpl(mConfig);
-    }
-
-  protected:
-    Configuration mConfig;
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInSetConfiguration, aConfig), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> getPreviewStreamTask = new GetPreviewStreamTask(this, aSize, onSuccess, onError);
+  return mCameraThread->Dispatch(getPreviewStreamTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::AutoFocus(bool aCancelExistingCall)
+CameraControlImpl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext,
-            bool aCancelExistingCall)
-      : ControlMessage(aCameraControl, aContext)
-      , mCancelExistingCall(aCancelExistingCall)
-    { }
+  MOZ_ASSERT(NS_IsMainThread());
+  bool cancel = false;
 
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->AutoFocusImpl(mCancelExistingCall);
-    }
+  nsCOMPtr<nsICameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.get();
+  if (cb) {
+    /**
+     * We already have a callback, so someone has already
+     * called autoFocus() -- cancel it.
+     */
+    mAutoFocusOnSuccessCb = nullptr;
+    mAutoFocusOnErrorCb = nullptr;
+    cancel = true;
+  }
 
-  protected:
-    bool mCancelExistingCall;
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInAutoFocus, aCancelExistingCall), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> autoFocusTask = new AutoFocusTask(this, cancel, onSuccess, onError);
+  return mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::TakePicture()
+CameraControlImpl::TakePicture(const CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext)
-      : ControlMessage(aCameraControl, aContext)
-    { }
+  MOZ_ASSERT(NS_IsMainThread());
+  bool cancel = false;
 
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->TakePictureImpl();
-    }
-  };
+  nsCOMPtr<nsICameraTakePictureCallback> cb = mTakePictureOnSuccessCb.get();
+  if (cb) {
+    /**
+     * We already have a callback, so someone has already
+     * called takePicture() -- cancel it.
+     */
+    mTakePictureOnSuccessCb = nullptr;
+    mTakePictureOnErrorCb = nullptr;
+    cancel = true;
+  }
 
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInTakePicture), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError);
+  return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
-                                  const StartRecordingOptions* aOptions)
+CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor* aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext,
-            const StartRecordingOptions* aOptions,
-            DeviceStorageFileDescriptor* aFileDescriptor)
-      : ControlMessage(aCameraControl, aContext)
-      , mOptionsPassed(false)
-      , mFileDescriptor(aFileDescriptor)
-    {
-      if (aOptions) {
-        mOptions = *aOptions;
-        mOptionsPassed = true;
-      }
-    }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->StartRecordingImpl(mFileDescriptor,
-        mOptionsPassed ? &mOptions : nullptr);
-    }
-
-  protected:
-    StartRecordingOptions mOptions;
-    bool mOptionsPassed;
-    nsRefPtr<DeviceStorageFileDescriptor> mFileDescriptor;
-  };
-
-
-  return mCameraThread->Dispatch(new Message(this, CameraControlListener::kInStartRecording,
-    aOptions, aFileDescriptor), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, aFileDescriptor, onSuccess, onError, mWindowId);
+  return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 CameraControlImpl::StopRecording()
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext)
-      : ControlMessage(aCameraControl, aContext)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->StopRecordingImpl();
-    }
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInStopRecording), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
+  return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StartPreview()
+CameraControlImpl::StartPreview(DOMCameraPreview* aDOMPreview)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext)
-      : ControlMessage(aCameraControl, aContext)
-    { }
+  nsCOMPtr<nsIRunnable> startPreviewTask = new StartPreviewTask(this, aDOMPreview);
+  return mCameraThread->Dispatch(startPreviewTask, NS_DISPATCH_NORMAL);
+}
 
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->StartPreviewImpl();
-    }
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInStartPreview), NS_DISPATCH_NORMAL);
+void
+CameraControlImpl::StopPreview()
+{
+  nsCOMPtr<nsIRunnable> stopPreviewTask = new StopPreviewTask(this);
+  mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StopPreview()
+CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext)
-      : ControlMessage(aCameraControl, aContext)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->StopPreviewImpl();
-    }
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInStopPreview), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError);
+  return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::ReleaseHardware()
+CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  class Message : public ControlMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener::CameraErrorContext aContext)
-      : ControlMessage(aCameraControl, aContext)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      return mCameraControl->ReleaseHardwareImpl();
-    }
-  };
-
-  return mCameraThread->Dispatch(
-    new Message(this, CameraControlListener::kInReleaseHardware), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> releaseHardwareTask = new ReleaseHardwareTask(this, onSuccess, onError);
+  return mCameraThread->Dispatch(releaseHardwareTask, NS_DISPATCH_NORMAL);
 }
 
-class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage
+bool
+CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
 {
-public:
-  ListenerMessage(CameraControlImpl* aCameraControl,
-                  CameraControlListener* aListener)
-    : ControlMessage(aCameraControl, CameraControlListener::kInUnspecified)
-    , mListener(aListener)
-  { }
+  if (!mDOMPreview) {
+    return false;
+  }
 
-protected:
-  nsRefPtr<CameraControlListener> mListener;
-};
-
-void
-CameraControlImpl::AddListenerImpl(already_AddRefed<CameraControlListener> aListener)
-{
-  RwLockAutoEnterWrite lock(mListenerLock);
-
-  CameraControlListener* l = *mListeners.AppendElement() = aListener;
-
-  // Update the newly-added listener's state
-  l->OnConfigurationChange(mCurrentConfiguration);
-  l->OnHardwareStateChange(mHardwareState);
-  l->OnPreviewStateChange(mPreviewState);
+  return mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder);
 }
 
-void
-CameraControlImpl::AddListener(CameraControlListener* aListener)
- {
-  class Message : public ListenerMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl,
-            CameraControlListener* aListener)
-      : ListenerMessage(aCameraControl, aListener)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      mCameraControl->AddListenerImpl(mListener.forget());
-      return NS_OK;
-    }
-  };
-
-  mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL);
-}
-
-void
-CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
+NS_IMETHODIMP
+GetPreviewStreamResult::Run()
 {
-  RwLockAutoEnterWrite lock(mListenerLock);
-
-  nsRefPtr<CameraControlListener> l(aListener);
-  mListeners.RemoveElement(l);
-  // XXXmikeh - do we want to notify the listener that it has been removed?
-}
+  /**
+   * The camera preview stream object is DOM-facing, and as such
+   * must be a cycle-collection participant created on the main
+   * thread.
+   */
+  MOZ_ASSERT(NS_IsMainThread());
 
-void
-CameraControlImpl::RemoveListener(CameraControlListener* aListener)
- {
-  class Message : public ListenerMessage
-  {
-  public:
-    Message(CameraControlImpl* aCameraControl, CameraControlListener* aListener)
-      : ListenerMessage(aCameraControl, aListener)
-    { }
-
-    nsresult
-    RunImpl() MOZ_OVERRIDE
-    {
-      mCameraControl->RemoveListenerImpl(mListener);
-      return NS_OK;
-    }
-  };
-
-  mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsICameraPreviewStreamCallback> onSuccess = mOnSuccessCb.get();
+  nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+  if (onSuccess && nsDOMCameraManager::IsWindowStillActive(mWindowId) && window) {
+    nsCOMPtr<nsIDOMMediaStream> stream =
+      new DOMCameraPreview(window, mCameraControl, mWidth, mHeight,
+	                         mFramesPerSecond);
+    onSuccess->HandleEvent(stream);
+  }
+  return NS_OK;
 }
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -1,123 +1,750 @@
 /* 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 DOM_CAMERA_CAMERACONTROLIMPL_H
 #define DOM_CAMERA_CAMERACONTROLIMPL_H
 
-#include "nsTArray.h"
-#include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "nsIFile.h"
+#include "nsDOMFile.h"
 #include "nsProxyRelease.h"
-#include "AutoRwLock.h"
+#include "DictionaryHelpers.h"
 #include "nsIDOMDeviceStorage.h"
+#include "DOMCameraManager.h"
+#include "DOMCameraPreview.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 #include "DeviceStorage.h"
 #include "DeviceStorageFileDescriptor.h"
-#include "CameraControlListener.h"
 
 class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 
-namespace layers {
-  class Image;
-}
+class GetPreviewStreamTask;
+class StartPreviewTask;
+class StopPreviewTask;
+class AutoFocusTask;
+class TakePictureTask;
+class StartRecordingTask;
+class StopRecordingTask;
+class SetParameterTask;
+class GetParameterTask;
+class GetPreviewStreamVideoModeTask;
+class ReleaseHardwareTask;
 
+class DOMCameraPreview;
 class RecorderProfileManager;
 
 class CameraControlImpl : public ICameraControl
 {
+  friend class GetPreviewStreamTask;
+  friend class StartPreviewTask;
+  friend class StopPreviewTask;
+  friend class AutoFocusTask;
+  friend class TakePictureTask;
+  friend class StartRecordingTask;
+  friend class StopRecordingTask;
+  friend class SetParameterTask;
+  friend class GetParameterTask;
+  friend class GetPreviewStreamVideoModeTask;
+  friend class ReleaseHardwareTask;
+
 public:
-  CameraControlImpl(uint32_t aCameraId);
-  virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE;
-  virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE;
+  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
+
+  nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartPreview(DOMCameraPreview* aDOMPreview);
+  void StopPreview();
+  nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aDSFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StopRecording();
+  nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError);
 
-  virtual nsresult SetConfiguration(const Configuration& aConfig) MOZ_OVERRIDE;
-  virtual nsresult StartPreview() MOZ_OVERRIDE;
-  virtual nsresult StopPreview() MOZ_OVERRIDE;
-  virtual nsresult AutoFocus(bool aCancelExistingCall) MOZ_OVERRIDE;
-  virtual nsresult TakePicture() MOZ_OVERRIDE;
-  virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
-                                  const StartRecordingOptions* aOptions) MOZ_OVERRIDE;
-  virtual nsresult StopRecording() MOZ_OVERRIDE;
-  virtual nsresult ReleaseHardware() MOZ_OVERRIDE;
+  nsresult Set(uint32_t aKey, const nsAString& aValue);
+  nsresult Get(uint32_t aKey, nsAString& aValue);
+  nsresult Set(uint32_t aKey, double aValue);
+  nsresult Get(uint32_t aKey, double* aValue);
+  nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
+  nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
+  nsresult Set(nsICameraShutterCallback* aOnShutter);
+  nsresult Get(nsICameraShutterCallback** aOnShutter);
+  nsresult Set(nsICameraClosedCallback* aOnClosed);
+  nsresult Get(nsICameraClosedCallback** aOnClosed);
+  nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange);
+  nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange);
+  nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange);
+  nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange);
+  nsresult Set(uint32_t aKey, const idl::CameraSize& aSize);
+  nsresult Get(uint32_t aKey, idl::CameraSize& aSize);
+  nsresult Get(uint32_t aKey, int32_t* aValue);
+
+  nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
+  {
+    return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
+  }
+
+  nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
+  {
+    return Set(aCx, CAMERA_PARAM_METERINGAREAS, aValue, mMaxMeteringAreas);
+  }
 
   already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
   uint32_t GetCameraId() { return mCameraId; }
 
-  virtual void Shutdown() MOZ_OVERRIDE;
+  virtual const char* GetParameter(const char* aKey) = 0;
+  virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
+  virtual double GetParameterDouble(uint32_t aKey) = 0;
+  virtual int32_t GetParameterInt32(uint32_t aKey) = 0;
+  virtual void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions) = 0;
+  virtual void GetParameter(uint32_t aKey, idl::CameraSize& aSize) = 0;
+  virtual void SetParameter(const char* aKey, const char* aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, double aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions) = 0;
+  virtual void SetParameter(uint32_t aKey, const idl::CameraSize& aSize) = 0;
+  virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
+  virtual nsresult PushParameters() = 0;
+  virtual void Shutdown();
 
+  bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
   void OnShutter();
   void OnClosed();
-  void OnError(CameraControlListener::CameraErrorContext aContext,
-               CameraControlListener::CameraError aError);
+  void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber);
+
+  enum PreviewState {
+    PREVIEW_STOPPED,
+    PREVIEW_STARTED
+  };
+  void OnPreviewStateChange(PreviewState aNewState);
+
+  uint64_t GetWindowId()
+  {
+    return mWindowId;
+  }
 
 protected:
-  // Event handlers.
-  void OnAutoFocusComplete(bool aAutoFocusSucceeded);
-  void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
-
-  bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
-  void OnRecorderStateChange(CameraControlListener::RecorderState aState,
-                             int32_t aStatus, int32_t aTrackNumber);
-  void OnPreviewStateChange(CameraControlListener::PreviewState aState);
-  void OnHardwareStateChange(CameraControlListener::HardwareState aState);
-  void OnConfigurationChange();
-
-  // When we create a new CameraThread, we keep a static reference to it so
-  // that multiple CameraControl instances can find and reuse it; but we
-  // don't want that reference to keep the thread object around unnecessarily,
-  // so we make it a weak reference. The strong dynamic references will keep
-  // the thread object alive as needed.
-  static nsWeakPtr sCameraThread;
-  nsCOMPtr<nsIThread> mCameraThread;
-
   virtual ~CameraControlImpl();
 
-  virtual void BeginBatchParameterSet() MOZ_OVERRIDE { }
-  virtual void EndBatchParameterSet() MOZ_OVERRIDE { }
-
-  // Manage camera event listeners.
-  void AddListenerImpl(already_AddRefed<CameraControlListener> aListener);
-  void RemoveListenerImpl(CameraControlListener* aListener);
-  nsTArray<nsRefPtr<CameraControlListener> > mListeners;
-  PRRWLock* mListenerLock;
-
-  class ControlMessage;
-  class ListenerMessage;
-
-  virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0;
-  virtual nsresult StartPreviewImpl() = 0;
-  virtual nsresult StopPreviewImpl() = 0;
-  virtual nsresult AutoFocusImpl(bool aCancelExistingCall) = 0;
-  virtual nsresult TakePictureImpl() = 0;
-  virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
-                                      const StartRecordingOptions* aOptions) = 0;
-  virtual nsresult StopRecordingImpl() = 0;
+  virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0;
+  virtual nsresult StartPreviewImpl(StartPreviewTask* aStartPreview) = 0;
+  virtual nsresult StopPreviewImpl(StopPreviewTask* aStopPreview) = 0;
+  virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0;
+  virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0;
+  virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0;
+  virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
   virtual nsresult PushParametersImpl() = 0;
   virtual nsresult PullParametersImpl() = 0;
-  virtual nsresult ReleaseHardwareImpl() = 0;
+  virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0;
+  virtual nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) = 0;
   virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
 
   void OnShutterInternal();
   void OnClosedInternal();
 
-  uint32_t mCameraId;
+  uint32_t            mCameraId;
+  nsCOMPtr<nsIThread> mCameraThread;
+  uint64_t            mWindowId;
+  nsString            mFileFormat;
+  uint32_t            mMaxMeteringAreas;
+  uint32_t            mMaxFocusAreas;
+  PreviewState        mPreviewState;
 
-  CameraControlListener::CameraListenerConfiguration mCurrentConfiguration;
+  /**
+   * 'mDOMPreview' is a raw pointer to the object that will receive incoming
+   * preview frames.  This is guaranteed to be valid, or null.
+   *
+   * It is set by a call to StartPreview(), and set to null on StopPreview().
+   * It is up to the caller to ensure that the object will not disappear
+   * out from under this pointer--usually by calling NS_ADDREF().
+   */
+  DOMCameraPreview*   mDOMPreview;
 
-  CameraControlListener::PreviewState   mPreviewState;
-  CameraControlListener::HardwareState  mHardwareState;
+  nsMainThreadPtrHandle<nsICameraAutoFocusCallback>   mAutoFocusOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback>       mAutoFocusOnErrorCb;
+  nsMainThreadPtrHandle<nsICameraTakePictureCallback> mTakePictureOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback>       mTakePictureOnErrorCb;
+  nsMainThreadPtrHandle<nsICameraShutterCallback>     mOnShutterCb;
+  nsMainThreadPtrHandle<nsICameraClosedCallback>      mOnClosedCb;
+  nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnRecorderStateChangeCb;
+  nsMainThreadPtrHandle<nsICameraPreviewStateChange>  mOnPreviewStateChangeCb;
 
 private:
   CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
   CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE;
 };
 
+// Error result runnable
+class CameraErrorResult : public nsRunnable
+{
+public:
+  CameraErrorResult(nsMainThreadPtrHandle<nsICameraErrorCallback> onError, const nsString& aErrorMsg, uint64_t aWindowId)
+    : mOnErrorCb(onError)
+    , mErrorMsg(aErrorMsg)
+    , mWindowId(aWindowId)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnErrorCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnErrorCb->HandleEvent(mErrorMsg);
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+  const nsString mErrorMsg;
+  uint64_t mWindowId;
+};
+
+// Return the resulting preview stream to JS.  Runs on the main thread.
+class GetPreviewStreamResult : public nsRunnable
+{
+public:
+  GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsMainThreadPtrHandle<nsICameraPreviewStreamCallback>& onSuccess, uint64_t aWindowId)
+    : mCameraControl(aCameraControl)
+    , mWidth(aWidth)
+    , mHeight(aHeight)
+    , mFramesPerSecond(aFramesPerSecond)
+    , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~GetPreviewStreamResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  // Run() method is implementation specific.
+  NS_IMETHOD Run() MOZ_OVERRIDE;
+
+protected:
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mFramesPerSecond;
+  nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Get the desired preview stream.
+class GetPreviewStreamTask : public nsRunnable
+{
+public:
+  GetPreviewStreamTask(CameraControlImpl* aCameraControl, idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mSize(aSize)
+    , mCameraControl(aCameraControl)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraPreviewStreamCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~GetPreviewStreamTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    nsresult rv = mCameraControl->GetPreviewStreamImpl(this);
+
+    if (NS_FAILED(rv)) {
+      nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
+      rv = NS_DispatchToMainThread(cameraErrorResult);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return rv;
+  }
+
+  idl::CameraSize mSize;
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Return the autofocus status to JS.  Runs on the main thread.
+class AutoFocusResult : public nsRunnable
+{
+public:
+  AutoFocusResult(bool aSuccess, nsMainThreadPtrHandle<nsICameraAutoFocusCallback> onSuccess, uint64_t aWindowId)
+    : mSuccess(aSuccess)
+    , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  { }
+
+  virtual ~AutoFocusResult() { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnSuccessCb->HandleEvent(mSuccess);
+    }
+    return NS_OK;
+  }
+
+protected:
+  bool mSuccess;
+  nsMainThreadPtrHandle<nsICameraAutoFocusCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Autofocus the camera.
+class AutoFocusTask : public nsRunnable
+{
+public:
+  AutoFocusTask(CameraControlImpl* aCameraControl, bool aCancel, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mCancel(aCancel)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraAutoFocusCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~AutoFocusTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->AutoFocusImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    if (NS_FAILED(rv)) {
+      nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
+      rv = NS_DispatchToMainThread(cameraErrorResult);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return rv;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  bool mCancel;
+  nsMainThreadPtrHandle<nsICameraAutoFocusCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Return the captured picture to JS.  Runs on the main thread.
+class TakePictureResult : public nsRunnable
+{
+public:
+  TakePictureResult(uint8_t* aData, uint64_t aLength, const nsAString& aMimeType, nsMainThreadPtrHandle<nsICameraTakePictureCallback> onSuccess, uint64_t aWindowId)
+    : mData(aData)
+    , mLength(aLength)
+    , mMimeType(aMimeType)
+    , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~TakePictureResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+    if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      nsCOMPtr<nsIDOMBlob> image = new nsDOMMemoryFile(static_cast<void*>(mData), static_cast<uint64_t>(mLength), mMimeType);
+      mOnSuccessCb->HandleEvent(image);
+    } else {
+      delete[] mData;
+    }
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+    return NS_OK;
+  }
+
+protected:
+  uint8_t* mData;
+  uint64_t mLength;
+  nsString mMimeType;
+  nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Capture a still image with the camera.
+class TakePictureTask : public nsRunnable
+{
+public:
+  TakePictureTask(CameraControlImpl* aCameraControl, bool aCancel, const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mCancel(aCancel)
+    , mSize(aSize)
+    , mRotation(aRotation)
+    , mFileFormat(aFileFormat)
+    , mPosition(aPosition)
+    , mDateTime(aDateTime)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraTakePictureCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~TakePictureTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->TakePictureImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    if (NS_FAILED(rv)) {
+      nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
+      rv = NS_DispatchToMainThread(cameraErrorResult);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return rv;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  bool mCancel;
+  idl::CameraSize mSize;
+  int32_t mRotation;
+  nsString mFileFormat;
+  idl::CameraPosition mPosition;
+  uint64_t mDateTime;
+  nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Return the result of starting recording.  Runs on the main thread.
+class StartRecordingResult : public nsRunnable
+{
+public:
+  StartRecordingResult(nsMainThreadPtrHandle<nsICameraStartRecordingCallback> onSuccess, uint64_t aWindowId)
+    : mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  { }
+
+  virtual ~StartRecordingResult() { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnSuccessCb->HandleEvent();
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Start video recording.
+class StartRecordingTask : public nsRunnable
+{
+public:
+  StartRecordingTask(CameraControlImpl* aCameraControl,
+                     idl::CameraStartRecordingOptions aOptions,
+                     DeviceStorageFileDescriptor *aDSFileDescriptor,
+                     nsICameraStartRecordingCallback* onSuccess,
+                     nsICameraErrorCallback* onError,
+                     uint64_t aWindowId)
+    : mCameraControl(aCameraControl)
+    , mOptions(aOptions)
+    , mDSFileDescriptor(aDSFileDescriptor)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraStartRecordingCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StartRecordingTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->StartRecordingImpl(this);
+    DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv);
+
+    // dispatch the callback
+    nsCOMPtr<nsIRunnable> startRecordingResult;
+    if (NS_SUCCEEDED(rv)) {
+      startRecordingResult = new StartRecordingResult(mOnSuccessCb, mWindowId);
+    } else {
+      startRecordingResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId);
+    }
+    rv = NS_DispatchToMainThread(startRecordingResult);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
+    }
+    return rv;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  idl::CameraStartRecordingOptions mOptions;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
+};
+
+// Stop video recording.
+class StopRecordingTask : public nsRunnable
+{
+public:
+  StopRecordingTask(CameraControlImpl* aCameraControl)
+    : mCameraControl(aCameraControl)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StopRecordingTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->StopRecordingImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    NS_ENSURE_SUCCESS(rv, rv);
+    return NS_OK;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+};
+
+// Start the preview.
+class StartPreviewTask : public nsRunnable
+{
+public:
+  StartPreviewTask(CameraControlImpl* aCameraControl, DOMCameraPreview* aDOMPreview)
+    : mCameraControl(aCameraControl)
+    , mDOMPreview(aDOMPreview)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StartPreviewTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->StartPreviewImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    NS_ENSURE_SUCCESS(rv, rv);
+    return NS_OK;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  DOMCameraPreview* mDOMPreview; // DOMCameraPreview NS_ADDREFs itself for us
+};
+
+// Stop the preview.
+class StopPreviewTask : public nsRunnable
+{
+public:
+  StopPreviewTask(CameraControlImpl* aCameraControl)
+    : mCameraControl(aCameraControl)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StopPreviewTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    mCameraControl->StopPreviewImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    return NS_OK;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+};
+
+// Get the video mode preview stream.
+class GetPreviewStreamVideoModeTask : public nsRunnable
+{
+public:
+  GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, idl::CameraRecorderOptions aOptions,  nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mOptions(aOptions)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraPreviewStreamCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this);
+    DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv);
+
+    if (NS_FAILED(rv)) {
+      nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
+      rv = NS_DispatchToMainThread(cameraErrorResult);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return NS_OK;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  idl::CameraRecorderOptions mOptions;
+  nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Return the result of releasing the camera hardware.  Runs on the main thread.
+class ReleaseHardwareResult : public nsRunnable
+{
+public:
+  ReleaseHardwareResult(nsMainThreadPtrHandle<nsICameraReleaseCallback> onSuccess, uint64_t aWindowId)
+    : mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~ReleaseHardwareResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnSuccessCb->HandleEvent();
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraReleaseCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Release the camera hardware.
+class ReleaseHardwareTask : public nsRunnable
+{
+public:
+  ReleaseHardwareTask(CameraControlImpl* aCameraControl, nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraReleaseCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~ReleaseHardwareTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->ReleaseHardwareImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    if (NS_FAILED(rv)) {
+      nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
+      rv = NS_DispatchToMainThread(cameraErrorResult);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return rv;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  nsMainThreadPtrHandle<nsICameraReleaseCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Report that the video recorder state has changed.
+class CameraRecorderStateChange : public nsRunnable
+{
+public:
+  CameraRecorderStateChange(nsMainThreadPtrHandle<nsICameraRecorderStateChange> onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId)
+    : mOnStateChangeCb(onStateChange)
+    , mStateMsg(aStateMsg)
+    , mStatus(aStatus)
+    , mTrackNumber(aTrackNumber)
+    , mWindowId(aWindowId)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      // For now, just pass the state message and swallow mStatus and mTrackNumber
+      mOnStateChangeCb->HandleStateChange(mStateMsg);
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnStateChangeCb;
+  const nsString mStateMsg;
+  int32_t mStatus;
+  int32_t mTrackNumber;
+  uint64_t mWindowId;
+};
+
+// Report that the preview stream state has changed.
+class CameraPreviewStateChange : public nsRunnable
+{
+public:
+  CameraPreviewStateChange(nsMainThreadPtrHandle<nsICameraPreviewStateChange> onStateChange, const nsString& aStateMsg, uint64_t aWindowId)
+    : mOnStateChangeCb(onStateChange)
+    , mStateMsg(aStateMsg)
+    , mWindowId(aWindowId)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnStateChangeCb->HandleStateChange(mStateMsg);
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraPreviewStateChange> mOnStateChangeCb;
+  const nsString mStateMsg;
+  uint64_t mWindowId;
+};
+
 } // namespace mozilla
 
 #endif // DOM_CAMERA_CAMERACONTROLIMPL_H
deleted file mode 100644
--- a/dom/camera/CameraControlListener.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* 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 DOM_CAMERA_CAMERACONTROLLISTENER_H
-#define DOM_CAMERA_CAMERACONTROLLISTENER_H
-
-#include <stdint.h>
-#include "ICameraControl.h"
-
-namespace mozilla {
-
-namespace layers {
-  class Image;
-}
-
-class CameraControlListener
-{
-public:
-  virtual ~CameraControlListener() { }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
-
-  enum HardwareState
-  {
-    kHardwareOpen,
-    kHardwareClosed
-  };
-  virtual void OnHardwareStateChange(HardwareState aState) { }
-
-  enum PreviewState
-  {
-    kPreviewStopped,
-    kPreviewPaused,
-    kPreviewStarted
-  };
-  virtual void OnPreviewStateChange(PreviewState aState) { }
-
-  enum RecorderState
-  {
-    kRecorderStopped,
-    kRecorderStarted,
-#ifdef MOZ_B2G_CAMERA
-    kFileSizeLimitReached,
-    kVideoLengthLimitReached,
-    kTrackCompleted,
-    kTrackFailed,
-    kMediaRecorderFailed,
-    kMediaServerFailed
-#endif
-  };
-  enum { kNoTrackNumber = -1 };
-  virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) { }
-
-  virtual void OnShutter() { }
-  virtual bool OnNewPreviewFrame(layers::Image* aFrame, uint32_t aWidth, uint32_t aHeight)
-  {
-    return false;
-  }
-
-  class CameraListenerConfiguration : public ICameraControl::Configuration
-  {
-  public:
-    uint32_t mMaxMeteringAreas;
-    uint32_t mMaxFocusAreas;
-  };
-  virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) { }
-
-  virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) { }
-  virtual void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
-
-  enum CameraErrorContext
-  {
-    kInGetCamera,
-    kInAutoFocus,
-    kInTakePicture,
-    kInStartRecording,
-    kInStopRecording,
-    kInSetConfiguration,
-    kInReleaseHardware,
-    kInStartPreview,
-    kInStopPreview,
-    kInUnspecified
-  };
-  enum CameraError
-  {
-    kErrorApiFailed,
-    kErrorInitFailed,
-    kErrorInvalidConfiguration,
-    kErrorServiceFailed,
-    kErrorSetPictureSizeFailed,
-    kErrorSetThumbnailSizeFailed,
-    kErrorUnknown
-  };
-  virtual void OnError(CameraErrorContext aContext, CameraError aError) { }
-};
-
-} // namespace mozilla
-
-#endif // DOM_CAMERA_CAMERACONTROLLISTENER_H
--- a/dom/camera/CameraRecorderProfiles.cpp
+++ b/dom/camera/CameraRecorderProfiles.cpp
@@ -1,14 +1,14 @@
 /* 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 "jsapi.h"
 #include "CameraRecorderProfiles.h"
-#include "jsapi.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
 /**
  * Video profile implementation.
  */
 RecorderVideoProfile::RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
--- a/dom/camera/CameraRecorderProfiles.h
+++ b/dom/camera/CameraRecorderProfiles.h
@@ -5,18 +5,20 @@
 #ifndef DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
 #define DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
 
 #include "nsISupportsImpl.h"
 #include "nsMimeTypes.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "jsapi.h"
+#include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
+
 namespace mozilla {
 
 class CameraControlImpl;
 
 class RecorderVideoProfile
 {
 public:
   RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -1,282 +1,424 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "DOMCameraCapabilities.h"
-#include "nsPIDOMWindow.h"
-#include "nsContentUtils.h"
-#include "mozilla/dom/CameraManagerBinding.h"
-#include "mozilla/dom/CameraCapabilitiesBinding.h"
-#include "CameraCommon.h"
-#include "ICameraControl.h"
-#include "CameraRecorderProfiles.h"
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities)
+ * 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/. */
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CameraCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-  tmp->mRecorderProfiles = JS::UndefinedValue();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CameraCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+#include <cstring>
+#include <cstdlib>
+#include "base/basictypes.h"
+#include "nsDOMClassInfo.h"
+#include "jsapi.h"
+#include "CameraRecorderProfiles.h"
+#include "DOMCameraControl.h"
+#include "DOMCameraCapabilities.h"
+#include "CameraCommon.h"
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CameraCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mRecorderProfiles)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
+using namespace mozilla;
+using namespace mozilla::dom;
+
+DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraCapabilities)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraCapabilities)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraCapabilities)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_BEGIN(DOMCameraCapabilities)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities)
 NS_INTERFACE_MAP_END
 
-CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow)
-  : mRecorderProfiles(JS::UndefinedValue())
-  , mWindow(aWindow)
+NS_IMPL_ADDREF(DOMCameraCapabilities)
+NS_IMPL_RELEASE(DOMCameraCapabilities)
+
+static nsresult
+ParseZoomRatioItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                         uint32_t aIndex, const char* aStart, char** aEnd)
 {
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  MOZ_COUNT_CTOR(CameraCapabilities);
-  mozilla::HoldJSObjects(this);
-  SetIsDOMBinding();
-}
+  if (!*aEnd) {
+    // make 'aEnd' follow the same semantics as strchr().
+    aEnd = nullptr;
+  }
 
-CameraCapabilities::~CameraCapabilities()
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  mRecorderProfiles = JS::UndefinedValue();
-  mozilla::DropJSObjects(this);
-  MOZ_COUNT_DTOR(CameraCapabilities);
-}
+  /**
+   * The by-100 divisor is Gonk-specific.  For now, assume other platforms
+   * return actual fractional multipliers.
+   */
+  double d = strtod(aStart, aEnd);
+#if MOZ_WIDGET_GONK
+  d /= 100;
+#endif
 
-JSObject*
-CameraCapabilities::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
-{
-  return CameraCapabilitiesBinding::Wrap(aCx, aScope, this);
+  if (!JS_SetElement(aCx, aArray, aIndex, d)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
 }
 
-#define LOG_IF_ERROR(rv, param)                               \
-  do {                                                        \
-    if (NS_FAILED(rv)) {                                      \
-      DOM_CAMERA_LOGW("Error %x trying to get " #param "\n",  \
-        (rv));                                                \
-    }                                                         \
-  } while(0)
+static nsresult
+ParseStringItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                      uint32_t aIndex, const char* aStart, char** aEnd)
+{
+  JS::Rooted<JSString*> s(aCx);
 
-nsresult
-CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl,
-                                          uint32_t aKey, nsTArray<CameraSize>& aSizes)
-{
-  nsresult rv;
-  nsTArray<ICameraControl::Size> sizes;
+  if (*aEnd) {
+    s = JS_NewStringCopyN(aCx, aStart, *aEnd - aStart);
+  } else {
+    s = JS_NewStringCopyZ(aCx, aStart);
+  }
+  if (!s) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  rv = aCameraControl->Get(aKey, sizes);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!JS_SetElement(aCx, aArray, aIndex, s)) {
+    return NS_ERROR_FAILURE;
   }
 
-  aSizes.Clear();
-  aSizes.SetCapacity(sizes.Length());
-  for (uint32_t i = 0; i < sizes.Length(); ++i) {
-    CameraSize* s = aSizes.AppendElement();
-    s->mWidth = sizes[i].width;
-    s->mHeight = sizes[i].height;
+  return NS_OK;
+}
+
+static nsresult
+ParseDimensionItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                         uint32_t aIndex, const char* aStart, char** aEnd)
+{
+  char* x;
+
+  if (!*aEnd) {
+    // make 'aEnd' follow the same semantics as strchr().
+    aEnd = nullptr;
+  }
+
+  JS::Rooted<JS::Value> w(aCx, INT_TO_JSVAL(strtol(aStart, &x, 10)));
+  JS::Rooted<JS::Value> h(aCx, INT_TO_JSVAL(strtol(x + 1, aEnd, 10)));
+
+  JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+  if (!o) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!JS_SetProperty(aCx, o, "width", w)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!JS_SetProperty(aCx, o, "height", h)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!JS_SetElement(aCx, aArray, aIndex, o)) {
+    return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-CameraCapabilities::Populate(ICameraControl* aCameraControl)
+DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx,
+                                               JS::MutableHandle<JSObject*> aArray,
+                                               uint32_t aKey,
+                                               ParseItemAndAddFunc aParseItemAndAdd)
 {
-  NS_ENSURE_TRUE(aCameraControl, NS_ERROR_INVALID_ARG);
-
-  nsresult rv;
-
-  rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, mPreviewSizes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
-
-  rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PICTURESIZES, mPictureSizes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
-
-  rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, mThumbnailSizes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
-
-  rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_VIDEOSIZES, mVideoSizes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, mFileFormats);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, mWhiteBalanceModes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, mEffects);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, mFlashModes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
-
-  int32_t areas;
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
-  mMaxFocusAreas = areas < 0 ? 0 : areas;
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
-  mMaxMeteringAreas = areas < 0 ? 0 : areas;
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mMinExposureCompensation);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
-
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, mMaxExposureCompensation);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
+  const char* value = mCamera->GetParameterConstChar(aKey);
+  if (!value) {
+    // in case we get nonsense data back
+    aArray.set(nullptr);
+    return NS_OK;
+  }
 
-  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep);
-  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
-
-  mRecorderProfileManager = aCameraControl->GetRecorderProfileManager();
-  if (!mRecorderProfileManager) {
-    DOM_CAMERA_LOGW("Unable to get recorder profile manager\n");
-  } else {
-    AutoJSContext js;
-
-    JS::Rooted<JSObject*> o(js);
-    nsresult rv = mRecorderProfileManager->GetJsObject(js, o.address());
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to JS-objectify profile manager (%d)\n", rv);
-      return rv;
-    }
-
-    mRecorderProfiles = JS::ObjectValue(*o);
+  aArray.set(JS_NewArrayObject(aCx, 0));
+  if (!aArray) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  // For now, always return success, since the presence or absence of capabilities
-  // indicates whether or not they are supported.
+  const char* p = value;
+  uint32_t index = 0;
+  nsresult rv;
+  char* q;
+
+  while (p) {
+    /**
+     * In C's string.h, strchr() is declared as returning 'char*'; in C++'s
+     * cstring, it is declared as returning 'const char*', _except_ in MSVC,
+     * where the C version is declared to return const like the C++ version.
+     *
+     * Unfortunately, for both cases, strtod() and strtol() take a 'char**' as
+     * the end-of-conversion pointer, so we need to cast away strchr()'s
+     * const-ness here to make the MSVC build everything happy.
+     */
+    q = const_cast<char*>(strchr(p, ','));
+    if (q != p) { // skip consecutive delimiters, just in case
+      rv = aParseItemAndAdd(aCx, aArray, index, p, &q);
+      NS_ENSURE_SUCCESS(rv, rv);
+      ++index;
+    }
+    p = q;
+    if (p) {
+      ++p;
+    }
+  }
+
+  return JS_FreezeObject(aCx, aArray) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult
+DOMCameraCapabilities::StringListToNewObject(JSContext* aCx,
+                                             JS::MutableHandle<JS::Value> aArray,
+                                             uint32_t aKey)
+{
+  JS::Rooted<JSObject*> array(aCx);
+
+  nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseStringItemAndAdd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aArray.setObjectOrNull(array);
+  return NS_OK;
+}
+
+nsresult
+DOMCameraCapabilities::DimensionListToNewObject(JSContext* aCx,
+                                                JS::MutableHandle<JS::Value> aArray,
+                                                uint32_t aKey)
+{
+  JS::Rooted<JSObject*> array(aCx);
+
+  nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseDimensionItemAndAdd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aArray.setObjectOrNull(array);
   return NS_OK;
 }
 
-void
-CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval) const
+/* readonly attribute jsval previewSizes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetPreviewSizes(JSContext* cx,
+                                       JS::MutableHandle<JS::Value> aPreviewSizes)
 {
-  retval = mPreviewSizes;
+  return DimensionListToNewObject(cx, aPreviewSizes, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
+}
+
+/* readonly attribute jsval pictureSizes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetPictureSizes(JSContext* cx,
+                                       JS::MutableHandle<JS::Value> aPictureSizes)
+{
+  return DimensionListToNewObject(cx, aPictureSizes, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
 }
 
-void
-CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval) const
+/* readonly attribute jsval thumbnailSizes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetThumbnailSizes(JSContext* cx,
+                                         JS::MutableHandle<JS::Value> aThumbnailSizes)
 {
-  retval = mPictureSizes;
+  return DimensionListToNewObject(cx, aThumbnailSizes, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
 }
 
-void
-CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval) const
+/* readonly attribute jsval fileFormats; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetFileFormats(JSContext* cx,
+                                      JS::MutableHandle<JS::Value> aFileFormats)
 {
-  retval = mThumbnailSizes;
+  return StringListToNewObject(cx, aFileFormats, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
+}
+
+/* readonly attribute jsval whiteBalanceModes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetWhiteBalanceModes(JSContext* cx,
+                                            JS::MutableHandle<JS::Value> aWhiteBalanceModes)
+{
+  return StringListToNewObject(cx, aWhiteBalanceModes, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
+}
+
+/* readonly attribute jsval sceneModes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetSceneModes(JSContext* cx,
+                                     JS::MutableHandle<JS::Value> aSceneModes)
+{
+  return StringListToNewObject(cx, aSceneModes, CAMERA_PARAM_SUPPORTED_SCENEMODES);
 }
 
-void
-CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval) const
+/* readonly attribute jsval effects; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetEffects(JSContext* cx,
+                                  JS::MutableHandle<JS::Value> aEffects)
 {
-  retval = mVideoSizes;
+  return StringListToNewObject(cx, aEffects, CAMERA_PARAM_SUPPORTED_EFFECTS);
 }
 
-void
-CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval) const
+/* readonly attribute jsval flashModes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetFlashModes(JSContext* cx,
+                                     JS::MutableHandle<JS::Value> aFlashModes)
 {
-  retval = mFileFormats;
+  return StringListToNewObject(cx, aFlashModes, CAMERA_PARAM_SUPPORTED_FLASHMODES);
+}
+
+/* readonly attribute jsval focusModes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetFocusModes(JSContext* cx,
+                                     JS::MutableHandle<JS::Value> aFocusModes)
+{
+  return StringListToNewObject(cx, aFocusModes, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
 }
 
-void
-CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval) const
+/* readonly attribute long maxFocusAreas; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
 {
-  retval = mWhiteBalanceModes;
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
+  if (!value) {
+    // in case we get nonsense data back
+    *aMaxFocusAreas = 0;
+    return NS_OK;
+  }
+
+  *aMaxFocusAreas = atoi(value);
+  return NS_OK;
 }
 
-void
-CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval) const
+/* readonly attribute double minExposureCompensation; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
 {
-  retval = mSceneModes;
-}
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-void
-CameraCapabilities::GetEffects(nsTArray<nsString>& retval) const
-{
-  retval = mEffects;
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
+  if (!value) {
+    // in case we get nonsense data back
+    *aMinExposureCompensation = 0;
+    return NS_OK;
+  }
+
+  *aMinExposureCompensation = atof(value);
+  return NS_OK;
 }
 
-void
-CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval) const
+/* readonly attribute double maxExposureCompensation; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
 {
-  retval = mFlashModes;
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
+  if (!value) {
+    // in case we get nonsense data back
+    *aMaxExposureCompensation = 0;
+    return NS_OK;
+  }
+
+  *aMaxExposureCompensation = atof(value);
+  return NS_OK;
 }
 
-void
-CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval) const
+/* readonly attribute double stepExposureCompensation; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
 {
-  retval = mFocusModes;
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
+  if (!value) {
+    // in case we get nonsense data back
+    *aStepExposureCompensation = 0;
+    return NS_OK;
+  }
+
+  *aStepExposureCompensation = atof(value);
+  return NS_OK;
 }
 
-void
-CameraCapabilities::GetZoomRatios(nsTArray<double>& retval) const
+/* readonly attribute long maxMeteringAreas; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
 {
-  retval = mZoomRatios;
-}
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-uint32_t
-CameraCapabilities::MaxFocusAreas() const
-{
-  return mMaxFocusAreas;
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
+  if (!value) {
+    // in case we get nonsense data back
+    *aMaxMeteringAreas = 0;
+    return NS_OK;
+  }
+
+  *aMaxMeteringAreas = atoi(value);
+  return NS_OK;
 }
 
-uint32_t
-CameraCapabilities::MaxMeteringAreas() const
+/* readonly attribute jsval zoomRatios; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetZoomRatios(JSContext* cx, JS::MutableHandle<JS::Value> aZoomRatios)
 {
-  return mMaxMeteringAreas;
-}
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-double
-CameraCapabilities::MinExposureCompensation() const
-{
-  return mMinExposureCompensation;
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_ZOOM);
+  if (!value || strcmp(value, "true") != 0) {
+    // if zoom is not supported, return a null object
+    aZoomRatios.setNull();
+    return NS_OK;
+  }
+
+  JS::Rooted<JSObject*> array(cx);
+
+  nsresult rv = ParameterListToNewArray(cx, &array, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aZoomRatios.setObjectOrNull(array);
+  return NS_OK;
 }
 
-double
-CameraCapabilities::MaxExposureCompensation() const
+/* readonly attribute jsval videoSizes; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::MutableHandle<JS::Value> aVideoSizes)
 {
-  return mMaxExposureCompensation;
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  nsTArray<mozilla::idl::CameraSize> sizes;
+  nsresult rv = mCamera->GetVideoSizes(sizes);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (sizes.Length() == 0) {
+    // video recording not supported, return null
+    aVideoSizes.setNull();
+    return NS_OK;
+  }
+
+  JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, 0));
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (uint32_t i = 0; i < sizes.Length(); ++i) {
+    JS::Rooted<JSObject*> o(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
+    JS::Rooted<JS::Value> v(cx, INT_TO_JSVAL(sizes[i].width));
+    if (!JS_SetProperty(cx, o, "width", v)) {
+      return NS_ERROR_FAILURE;
+    }
+    v = INT_TO_JSVAL(sizes[i].height);
+    if (!JS_SetProperty(cx, o, "height", v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (!JS_SetElement(cx, array, i, o)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  aVideoSizes.setObject(*array);
+  return NS_OK;
 }
 
-double
-CameraCapabilities::ExposureCompensationStep() const
+/* readonly attribute jsval recorderProfiles; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetRecorderProfiles(JSContext* cx, JS::MutableHandle<JS::Value> aRecorderProfiles)
 {
-  return mExposureCompensationStep;
-}
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-JS::Value
-CameraCapabilities::RecorderProfiles(JSContext* aCx) const
-{
-  return mRecorderProfiles;
+  nsRefPtr<RecorderProfileManager> profileMgr = mCamera->GetRecorderProfileManager();
+  if (!profileMgr) {
+    aRecorderProfiles.setNull();
+    return NS_OK;
+  }
+
+  JS::Rooted<JSObject*> o(cx);
+  nsresult rv = profileMgr->GetJsObject(cx, o.address());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aRecorderProfiles.setObject(*o);
+  return NS_OK;
 }
-
-} // namespace dom
-} // namespace mozilla
--- a/dom/camera/DOMCameraCapabilities.h
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -1,96 +1,59 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 mozilla_dom_CameraCapabilities_h__
-#define mozilla_dom_CameraCapabilities_h__
+ * 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 "nsString.h"
+#ifndef DOM_CAMERA_DOMCAMERACAPABILITIES_H
+#define DOM_CAMERA_DOMCAMERACAPABILITIES_H
+
+#include "ICameraControl.h"
 #include "nsAutoPtr.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/CameraManagerBinding.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
-
-struct JSContext;
-class nsPIDOMWindow;
+#include "CameraCommon.h"
 
 namespace mozilla {
 
-class ICameraControl;
-class RecorderProfileManager;
+typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                                        uint32_t aIndex, const char* aStart, char** aEnd);
 
-namespace dom {
-
-class CameraCapabilities MOZ_FINAL : public nsISupports
-                                   , public nsWrapperCache
+class DOMCameraCapabilities MOZ_FINAL : public nsICameraCapabilities
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraCapabilities)
-
-  CameraCapabilities(nsPIDOMWindow* aWindow);
-  ~CameraCapabilities();
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICAMERACAPABILITIES
 
-  nsresult Populate(ICameraControl* aCameraControl);
-
-  nsPIDOMWindow* GetParentObject() const { return mWindow; }
-
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  DOMCameraCapabilities(ICameraControl* aCamera)
+    : mCamera(aCamera)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
-  void GetPreviewSizes(nsTArray<CameraSize>& aRetVal) const;
-  void GetPictureSizes(nsTArray<CameraSize>& aRetVal) const;
-  void GetThumbnailSizes(nsTArray<CameraSize>& aRetVal) const;
-  void GetVideoSizes(nsTArray<CameraSize>& aRetVal) const;
-  void GetFileFormats(nsTArray<nsString>& aRetVal) const;
-  void GetWhiteBalanceModes(nsTArray<nsString>& aRetVal) const;
-  void GetSceneModes(nsTArray<nsString>& aRetVal) const;
-  void GetEffects(nsTArray<nsString>& aRetVal) const;
-  void GetFlashModes(nsTArray<nsString>& aRetVal) const;
-  void GetFocusModes(nsTArray<nsString>& aRetVal) const;
-  void GetZoomRatios(nsTArray<double>& aRetVal) const;
-  uint32_t MaxFocusAreas() const;
-  uint32_t MaxMeteringAreas() const;
-  double MinExposureCompensation() const;
-  double MaxExposureCompensation() const;
-  double ExposureCompensationStep() const;
-  JS::Value RecorderProfiles(JSContext* cx) const;
+  nsresult ParameterListToNewArray(
+    JSContext* cx,
+    JS::MutableHandle<JSObject*> aArray,
+    uint32_t aKey,
+    ParseItemAndAddFunc aParseItemAndAdd
+  );
+  nsresult StringListToNewObject(JSContext* aCx,
+                                 JS::MutableHandle<JS::Value> aArray,
+                                 uint32_t aKey);
+  nsresult DimensionListToNewObject(JSContext* aCx,
+                                    JS::MutableHandle<JS::Value> aArray,
+                                    uint32_t aKey);
+
+private:
+  DOMCameraCapabilities(const DOMCameraCapabilities&) MOZ_DELETE;
+  DOMCameraCapabilities& operator=(const DOMCameraCapabilities&) MOZ_DELETE;
 
 protected:
-  nsresult TranslateToDictionary(ICameraControl* aCameraControl,
-                                 uint32_t aKey, nsTArray<CameraSize>& aSizes);
-
-  nsTArray<CameraSize> mPreviewSizes;
-  nsTArray<CameraSize> mPictureSizes;
-  nsTArray<CameraSize> mThumbnailSizes;
-  nsTArray<CameraSize> mVideoSizes;
+  /* additional members */
+  ~DOMCameraCapabilities()
+  {
+    // destructor code
+    DOM_CAMERA_LOGT("%s:%d : this=%p, mCamera=%p\n", __func__, __LINE__, this, mCamera.get());
+  }
 
-  nsTArray<nsString> mFileFormats;
-  nsTArray<nsString> mWhiteBalanceModes;
-  nsTArray<nsString> mSceneModes;
-  nsTArray<nsString> mEffects;
-  nsTArray<nsString> mFlashModes;
-  nsTArray<nsString> mFocusModes;
-
-  nsTArray<double> mZoomRatios;
-
-  uint32_t mMaxFocusAreas;
-  uint32_t mMaxMeteringAreas;
-
-  double mMinExposureCompensation;
-  double mMaxExposureCompensation;
-  double mExposureCompensationStep;
-
-  nsRefPtr<RecorderProfileManager> mRecorderProfileManager;
-  JS::Heap<JS::Value> mRecorderProfiles;
-
-  nsRefPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<ICameraControl> mCamera;
 };
 
-} // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_CameraCapabilities_h__
+#endif // DOM_CAMERA_DOMCAMERACAPABILITIES_H
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1,478 +1,170 @@
 /* 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 "DOMCameraControl.h"
 #include "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "nsHashPropertyBag.h"
 #include "nsThread.h"
 #include "DeviceStorage.h"
 #include "DeviceStorageFileDescriptor.h"
+#include "mozilla/dom/CameraControlBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsIAppsService.h"
 #include "nsIObserverService.h"
 #include "nsIDOMDeviceStorage.h"
-#include "nsIDOMEventListener.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsXULAppAPI.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraCapabilities.h"
+#include "DOMCameraControl.h"
 #include "CameraCommon.h"
-#include "DictionaryHelpers.h"
-#include "nsGlobalWindow.h"
-#include "CameraPreviewMediaStream.h"
-#include "mozilla/dom/CameraControlBinding.h"
 #include "mozilla/dom/CameraManagerBinding.h"
-#include "mozilla/dom/CameraCapabilitiesBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::idl;
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsDOMCameraControl, mDOMCapabilities, mWindow)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraControl)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
-NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
-
-NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream)
-NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMCameraControl, DOMMediaStream)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGetCameraOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGetCameraOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAutoFocusOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAutoFocusOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTakePictureOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTakePictureOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartRecordingOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartRecordingOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReleaseOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReleaseOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetConfigurationOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetConfigurationOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnShutterCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnClosedCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnRecorderStateChangeCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnPreviewStateChangeCb)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMCameraControl, DOMMediaStream)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGetCameraOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGetCameraOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAutoFocusOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAutoFocusOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTakePictureOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTakePictureOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartRecordingOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartRecordingOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReleaseOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReleaseOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetConfigurationOnSuccessCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetConfigurationOnErrorCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnShutterCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnClosedCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnRecorderStateChangeCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnPreviewStateChangeCb)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMCameraControl)
-
-class mozilla::StartRecordingHelper : public nsIDOMEventListener
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTLISTENER
-
-  StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
-    : mDOMCameraControl(aDOMCameraControl)
-  {
-    MOZ_COUNT_CTOR(StartRecordingHelper);
-  }
-
-protected:
-  virtual ~StartRecordingHelper()
-  {
-    MOZ_COUNT_DTOR(StartRecordingHelper);
-  }
-
-protected:
-  nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
-};
-
-NS_IMETHODIMP
-StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
-{
-  nsString eventType;
-  aEvent->GetType(eventType);
-
-  mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS0(mozilla::StartRecordingHelper)
+NS_INTERFACE_MAP_END
 
-nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
-  : CameraConfiguration()
-  , mMaxFocusAreas(0)
-  , mMaxMeteringAreas(0)
-{
-  MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
-}
-
-nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration)
-  : CameraConfiguration(aConfiguration)
-  , mMaxFocusAreas(0)
-  , mMaxMeteringAreas(0)
-{
-  MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
-}
-
-nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration()
-{
-  MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration);
-}
-
-nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
-                                       const CameraConfiguration& aInitialConfig,
-                                       GetCameraCallback* aOnSuccess,
-                                       CameraErrorCallback* aOnError,
-                                       nsPIDOMWindow* aWindow)
-  : DOMMediaStream()
-  , mCameraControl(nullptr)
-  , mAudioChannelAgent(nullptr)
-  , mGetCameraOnSuccessCb(aOnSuccess)
-  , mGetCameraOnErrorCb(aOnError)
-  , mAutoFocusOnSuccessCb(nullptr)
-  , mAutoFocusOnErrorCb(nullptr)
-  , mTakePictureOnSuccessCb(nullptr)
-  , mTakePictureOnErrorCb(nullptr)
-  , mStartRecordingOnSuccessCb(nullptr)
-  , mStartRecordingOnErrorCb(nullptr)
-  , mReleaseOnSuccessCb(nullptr)
-  , mReleaseOnErrorCb(nullptr)
-  , mSetConfigurationOnSuccessCb(nullptr)
-  , mSetConfigurationOnErrorCb(nullptr)
-  , mOnShutterCb(nullptr)
-  , mOnClosedCb(nullptr)
-  , mOnRecorderStateChangeCb(nullptr)
-  , mOnPreviewStateChangeCb(nullptr)
-  , mWindow(aWindow)
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  mInput = new CameraPreviewMediaStream(this);
-
-  SetIsDOMBinding();
-
-  nsRefPtr<DOMCameraConfiguration> initialConfig =
-    new DOMCameraConfiguration(aInitialConfig);
-
-  // Create and initialize the underlying camera.
-  ICameraControl::Configuration config;
-
-  switch (aInitialConfig.mMode) {
-    case CameraMode::Picture:
-      config.mMode = ICameraControl::kPictureMode;
-      break;
-
-    case CameraMode::Video:
-      config.mMode = ICameraControl::kVideoMode;
-      break;
-
-    default:
-      MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
-  }
-
-  config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth;
-  config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
-  config.mRecorderProfile = aInitialConfig.mRecorderProfile;
-
-  mCameraControl = ICameraControl::Create(aCameraId, &config);
-  mCurrentConfiguration = initialConfig.forget();
-
-  // Attach our DOM-facing media stream to our viewfinder stream.
-  mStream = mInput;
-  MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!");
-  if (mWindow->GetExtantDoc()) {
-    CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal());
-  }
-
-  // Register a listener for camera events.
-  mListener = new DOMCameraControlListener(this, mInput);
-  mCameraControl->AddListener(mListener);
-}
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraControl)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraControl)
 
 nsDOMCameraControl::~nsDOMCameraControl()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 JSObject*
 nsDOMCameraControl::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return CameraControlBinding::Wrap(aCx, aScope, this);
 }
 
-bool
-nsDOMCameraControl::IsWindowStillActive()
-{
-  return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
-}
-
-// JS-to-native helpers
-// Setter for weighted regions: { top, bottom, left, right, weight }
-nsresult
-nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
+nsICameraCapabilities*
+nsDOMCameraControl::Capabilities()
 {
-  if (aLimit == 0) {
-    DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
-    return NS_OK;
-  }
-
-  if (!aValue.isObject()) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  uint32_t length = 0;
-
-  JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
-  if (!JS_GetArrayLength(aCx, regions, &length)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
-  if (length > aLimit) {
-    length = aLimit;
+  if (!mDOMCapabilities) {
+    mDOMCapabilities = new DOMCameraCapabilities(mCameraControl);
   }
 
-  nsTArray<ICameraControl::Region> regionArray;
-  regionArray.SetCapacity(length);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    JS::Rooted<JS::Value> v(aCx);
-
-    if (!JS_GetElement(aCx, regions, i, &v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    CameraRegion region;
-    if (!region.Init(aCx, v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    ICameraControl::Region* r = regionArray.AppendElement();
-    r->top = region.mTop;
-    r->left = region.mLeft;
-    r->bottom = region.mBottom;
-    r->right = region.mRight;
-    r->weight = region.mWeight;
-
-    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
-      i,
-      r->top,
-      r->left,
-      r->bottom,
-      r->right,
-      r->weight
-    );
-  }
-  return mCameraControl->Set(aKey, regionArray);
-}
-
-// Getter for weighted regions: { top, bottom, left, right, weight }
-nsresult
-nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
-{
-  nsTArray<ICameraControl::Region> regionArray;
-
-  nsresult rv = mCameraControl->Get(aKey, regionArray);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
-  if (!array) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  uint32_t length = regionArray.Length();
-  DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    ICameraControl::Region* r = &regionArray[i];
-    JS::Rooted<JS::Value> v(aCx);
-
-    JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
-    if (!o) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    DOM_CAMERA_LOGI("top=%d\n", r->top);
-    v = INT_TO_JSVAL(r->top);
-    if (!JS_SetProperty(aCx, o, "top", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("left=%d\n", r->left);
-    v = INT_TO_JSVAL(r->left);
-    if (!JS_SetProperty(aCx, o, "left", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
-    v = INT_TO_JSVAL(r->bottom);
-    if (!JS_SetProperty(aCx, o, "bottom", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("right=%d\n", r->right);
-    v = INT_TO_JSVAL(r->right);
-    if (!JS_SetProperty(aCx, o, "right", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("weight=%d\n", r->weight);
-    v = INT_TO_JSVAL(r->weight);
-    if (!JS_SetProperty(aCx, o, "weight", v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (!JS_SetElement(aCx, array, i, o)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  *aValue = JS::ObjectValue(*array);
-  return NS_OK;
+  return mDOMCapabilities;
 }
 
 void
 nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
 }
 void
 nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect);
 }
 
 void
 nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
 }
 void
 nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
 }
 
 void
 nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode);
 }
 void
 nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode);
 }
 
 void
 nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode);
 }
 void
 nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode);
 }
 
 void
 nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode);
 }
 void
 nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
 }
 
 double
 nsDOMCameraControl::GetZoom(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double zoom;
-  aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom);
+  aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, &zoom);
   return zoom;
 }
 
 void
 nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
 }
 
 /* attribute jsval meteringAreas; */
 JS::Value
 nsDOMCameraControl::GetMeteringAreas(JSContext* cx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> areas(cx);
-  aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, areas.address());
+  aRv = mCameraControl->Get(cx, CAMERA_PARAM_METERINGAREAS, areas.address());
   return areas;
 }
 
 void
 nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle<JS::Value> aMeteringAreas, ErrorResult& aRv)
 {
-  aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
-            mCurrentConfiguration->mMaxMeteringAreas);
+  aRv = mCameraControl->SetMeteringAreas(cx, aMeteringAreas);
 }
 
 JS::Value
 nsDOMCameraControl::GetFocusAreas(JSContext* cx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> value(cx);
-  aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, value.address());
+  aRv = mCameraControl->Get(cx, CAMERA_PARAM_FOCUSAREAS, value.address());
   return value;
 }
 void
 nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle<JS::Value> aFocusAreas, ErrorResult& aRv)
 {
-  aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
-            mCurrentConfiguration->mMaxFocusAreas);
+  aRv = mCameraControl->SetFocusAreas(cx, aFocusAreas);
 }
 
 static nsresult
-GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize)
+GetSize(JSContext* aCx, JS::Value* aValue, const CameraSize& aSize)
 {
   JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
   if (!o) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   JS::Rooted<JS::Value> v(aCx);
 
@@ -489,464 +181,448 @@ GetSize(JSContext* aCx, JS::Value* aValu
   return NS_OK;
 }
 
 /* attribute any pictureSize */
 JS::Value
 nsDOMCameraControl::GetPictureSize(JSContext* cx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> value(cx);
-
-  ICameraControl::Size size;
+  
+  CameraSize size;
   aRv = mCameraControl->Get(CAMERA_PARAM_PICTURESIZE, size);
   if (aRv.Failed()) {
     return value;
   }
 
   aRv = GetSize(cx, value.address(), size);
   return value;
 }
 void
-nsDOMCameraControl::SetPictureSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
+nsDOMCameraControl::SetPictureSize(JSContext* cx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
 {
   CameraSize size;
-  if (!size.Init(aCx, aSize)) {
-    aRv = NS_ERROR_FAILURE;
+  aRv = size.Init(cx, aSize.address());
+  if (aRv.Failed()) {
     return;
   }
 
-  ICameraControl::Size s = { size.mWidth, size.mHeight };
-  aRv = mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, s);
+  aRv = mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, size);
 }
 
 /* attribute any thumbnailSize */
 JS::Value
-nsDOMCameraControl::GetThumbnailSize(JSContext* aCx, ErrorResult& aRv)
+nsDOMCameraControl::GetThumbnailSize(JSContext* cx, ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> value(aCx);
-
-  ICameraControl::Size size;
+  JS::Rooted<JS::Value> value(cx);
+  
+  CameraSize size;
   aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
   if (aRv.Failed()) {
     return value;
   }
 
-  aRv = GetSize(aCx, value.address(), size);
+  aRv = GetSize(cx, value.address(), size);
   return value;
 }
 void
-nsDOMCameraControl::SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
+nsDOMCameraControl::SetThumbnailSize(JSContext* cx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
 {
   CameraSize size;
-  if (!size.Init(aCx, aSize)) {
-    aRv = NS_ERROR_FAILURE;
+  aRv = size.Init(cx, aSize.address());
+  if (aRv.Failed()) {
     return;
   }
 
-  ICameraControl::Size s = { size.mWidth, size.mHeight };
-  aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
+  aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, size);
 }
 
 double
 nsDOMCameraControl::GetFocalLength(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double focalLength;
-  aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength);
+  aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, &focalLength);
   return focalLength;
 }
 
 double
 nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double distance;
-  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance);
+  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, &distance);
   return distance;
 }
 
 double
 nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double distance;
-  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance);
+  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, &distance);
   return distance;
 }
 
 double
 nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double distance;
-  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance);
+  aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, &distance);
   return distance;
 }
 
 void
 nsDOMCameraControl::SetExposureCompensation(const Optional<double>& aCompensation, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   if (!aCompensation.WasPassed()) {
     // use NaN to switch the camera back into auto mode
     aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
-    return;
   }
 
   aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value());
 }
 
 double
 nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
   double compensation;
-  aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
+  aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, &compensation);
   return compensation;
 }
 
 int32_t
 nsDOMCameraControl::SensorAngle()
 {
-  MOZ_ASSERT(mCameraControl);
-
   int32_t angle;
-  mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle);
+  mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, &angle);
   return angle;
 }
 
-already_AddRefed<CameraShutterCallback>
-nsDOMCameraControl::GetOnShutter()
-{
-  nsCOMPtr<CameraShutterCallback> cb = mOnShutterCb;
-  return cb.forget();
-}
-
-void
-nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb)
+already_AddRefed<nsICameraShutterCallback>
+nsDOMCameraControl::GetOnShutter(ErrorResult& aRv)
 {
-  mOnShutterCb = aCb;
-}
-
-/* attribute CameraClosedCallback onClosed; */
-already_AddRefed<CameraClosedCallback>
-nsDOMCameraControl::GetOnClosed()
-{
-  nsCOMPtr<CameraClosedCallback> onClosed = mOnClosedCb;
-  return onClosed.forget();
-}
-
-void
-nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb)
-{
-  mOnClosedCb = aCb;
-}
-
-already_AddRefed<CameraRecorderStateChange>
-nsDOMCameraControl::GetOnRecorderStateChange()
-{
-  nsCOMPtr<CameraRecorderStateChange> cb = mOnRecorderStateChangeCb;
+  nsCOMPtr<nsICameraShutterCallback> cb;
+  aRv = mCameraControl->Get(getter_AddRefs(cb));
   return cb.forget();
 }
 
 void
-nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb)
+nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter,
+                                 ErrorResult& aRv)
 {
-  mOnRecorderStateChangeCb = aCb;
+  aRv = mCameraControl->Set(aOnShutter);
 }
 
-/* attribute CameraPreviewStateChange onPreviewStateChange; */
-already_AddRefed<CameraPreviewStateChange>
-nsDOMCameraControl::GetOnPreviewStateChange()
+/* attribute nsICameraClosedCallback onClosed; */
+already_AddRefed<nsICameraClosedCallback>
+nsDOMCameraControl::GetOnClosed(ErrorResult& aRv)
 {
-  nsCOMPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
+  nsCOMPtr<nsICameraClosedCallback> onClosed;
+  aRv = mCameraControl->Get(getter_AddRefs(onClosed));
+  return onClosed.forget();
+}
+
+void
+nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed,
+                                ErrorResult& aRv)
+{
+  aRv = mCameraControl->Set(aOnClosed);
+}
+
+already_AddRefed<nsICameraRecorderStateChange>
+nsDOMCameraControl::GetOnRecorderStateChange(ErrorResult& aRv)
+{
+  nsCOMPtr<nsICameraRecorderStateChange> cb;
+  aRv = mCameraControl->Get(getter_AddRefs(cb));
   return cb.forget();
 }
-void
-nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb)
-{
-  mOnPreviewStateChangeCb = aCb;
-}
 
-already_AddRefed<dom::CameraCapabilities>
-nsDOMCameraControl::Capabilities()
+void
+nsDOMCameraControl::SetOnRecorderStateChange(nsICameraRecorderStateChange* aOnRecorderStateChange,
+                                             ErrorResult& aRv)
 {
-  nsRefPtr<CameraCapabilities> caps = mCapabilities;
+  aRv = mCameraControl->Set(aOnRecorderStateChange);
+}
 
-  if (!caps) {
-    caps = new CameraCapabilities(mWindow);
-    nsresult rv = caps->Populate(mCameraControl);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv);
-      return nullptr;
-    }
-    mCapabilities = caps;
+void
+nsDOMCameraControl::StartRecording(JSContext* aCx,
+                                   JS::Handle<JS::Value> aOptions,
+                                   nsDOMDeviceStorage& storageArea,
+                                   const nsAString& filename,
+                                   nsICameraStartRecordingCallback* onSuccess,
+                                   const Optional<nsICameraErrorCallback*>& onError,
+                                   ErrorResult& aRv)
+{
+  MOZ_ASSERT(onSuccess, "no onSuccess handler passed");
+
+  // Default values, until the dictionary parser can handle them.
+  mOptions.rotation = 0;
+  mOptions.maxFileSizeBytes = 0;
+  mOptions.maxVideoLengthMs = 0;
+  aRv = mOptions.Init(aCx, aOptions.address());
+  if (aRv.Failed()) {
+    return;
   }
 
-  return caps.forget();
-}
+  aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
 
-// Methods.
-void
-nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
-                                   nsDOMDeviceStorage& aStorageArea,
-                                   const nsAString& aFilename,
-                                   CameraStartRecordingCallback& aOnSuccess,
-                                   const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
-                                   ErrorResult& aRv)
-{
-  MOZ_ASSERT(mCameraControl);
-
-  NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
-
-#ifdef MOZ_B2G
+  #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (mAudioChannelAgent) {
       // Camera app will stop recording when it falls to the background, so no callback is necessary.
       mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr);
       // Video recording doesn't output any sound, so it's not necessary to check canPlay.
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
     }
   }
-#endif
+  #endif
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   mDSFileDescriptor = new DeviceStorageFileDescriptor();
-  aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(),
+  aRv = storageArea.CreateFileDescriptor(filename, mDSFileDescriptor.get(),
                                          getter_AddRefs(request));
   if (aRv.Failed()) {
     return;
   }
 
-  mOptions = aOptions;
-  mStartRecordingOnSuccessCb = &aOnSuccess;
-  mStartRecordingOnErrorCb = nullptr;
-  if (aOnError.WasPassed()) {
-    mStartRecordingOnErrorCb = &aOnError.Value();
-  }
+  mOnSuccessCb = onSuccess;
+  mOnErrorCb = onError.WasPassed() ? onError.Value() : nullptr;
 
-  nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
-  request->AddEventListener(NS_LITERAL_STRING("success"), listener, false);
-  request->AddEventListener(NS_LITERAL_STRING("error"), listener, false);
+  request->AddEventListener(NS_LITERAL_STRING("success"), this, false);
+  request->AddEventListener(NS_LITERAL_STRING("error"), this, false);
 }
 
-void
-nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
+NS_IMETHODIMP
+nsDOMCameraControl::HandleEvent(nsIDOMEvent* aEvent)
 {
-  if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
-    ICameraControl::StartRecordingOptions o;
+  nsString  eventType;
+  aEvent->GetType(eventType);
+  ErrorResult rv;
+
+  if ((eventType.EqualsLiteral("success")) &&
+      mDSFileDescriptor->mFileDescriptor.IsValid()) {
 
-    o.rotation = mOptions.mRotation;
-    o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
-    o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
-    nsresult rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
-    if (NS_SUCCEEDED(rv)) {
-      return;
+    rv = mCameraControl->StartRecording(&mOptions,
+                                        mDSFileDescriptor.get(),
+                                        mOnSuccessCb.get(),
+                                        mOnErrorCb.get());
+    if (!rv.Failed()) {
+      return rv.ErrorCode();
     }
+
+    // An error happened. Fall through and call the error callback.
   }
 
-  OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE"));
+  // We're already be on the main thread, so go ahead and call the
+  // error callback directly.
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mOnErrorCb &&
+      nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID())) {
+    mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+  }
+
+  return NS_OK;
 }
 
 void
 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
+  aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
 
-#ifdef MOZ_B2G
+  #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
   }
-#endif
+  #endif
 
   aRv = mCameraControl->StopRecording();
 }
 
 void
+nsDOMCameraControl::GetPreviewStream(JSContext* aCx,
+                                     JS::Handle<JS::Value> aOptions,
+                                     nsICameraPreviewStreamCallback* onSuccess,
+                                     const Optional<nsICameraErrorCallback*>& onError,
+                                     ErrorResult& aRv)
+{
+  mozilla::idl::CameraSize size;
+  aRv = size.Init(aCx, aOptions.address());
+  if (aRv.Failed()) {
+    return;
+  }
+
+  aRv = mCameraControl->GetPreviewStream(size, onSuccess,
+                                         onError.WasPassed()
+                                         ? onError.Value() : nullptr);
+}
+
+void
 nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-  aRv = mCameraControl->StartPreview();
+  aRv = mCameraControl->StartPreview(nullptr);
+}
+
+already_AddRefed<nsICameraPreviewStateChange>
+nsDOMCameraControl::GetOnPreviewStateChange() const
+{
+  nsCOMPtr<nsICameraPreviewStateChange> cb;
+  mCameraControl->Get(getter_AddRefs(cb));
+  return cb.forget();
+}
+
+void
+nsDOMCameraControl::SetOnPreviewStateChange(nsICameraPreviewStateChange* aCb)
+{
+  mCameraControl->Set(aCb);
+}
+
+void
+nsDOMCameraControl::AutoFocus(nsICameraAutoFocusCallback* onSuccess,
+                              const Optional<nsICameraErrorCallback*>& onError,
+                              ErrorResult& aRv)
+{
+  aRv = mCameraControl->AutoFocus(onSuccess,
+                                  onError.WasPassed() ? onError.Value() : nullptr);
 }
 
 void
-nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
-                                     const Optional<OwningNonNull<CameraSetConfigurationCallback> >& aOnSuccess,
-                                     const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
-                                     ErrorResult& aRv)
+nsDOMCameraControl::TakePicture(JSContext* aCx,
+                                const CameraPictureOptions& aOptions,
+                                nsICameraTakePictureCallback* onSuccess,
+                                const Optional<nsICameraErrorCallback*>& aOnError,
+                                ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
+  mozilla::idl::CameraSize           size;
+  mozilla::idl::CameraPosition       pos;
 
-  nsCOMPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
-  if (cb) {
-    // We're busy taking a picture, can't change modes right now.
-    if (aOnError.WasPassed()) {
-      ErrorResult ignored;
-      aOnError.Value().Call(NS_LITERAL_STRING("Busy"), ignored);
-    }
-    aRv = NS_ERROR_FAILURE;
+  aRv = size.Init(aCx, &aOptions.mPictureSize);
+  if (aRv.Failed()) {
     return;
   }
 
-  ICameraControl::Configuration config;
-  config.mRecorderProfile = aConfiguration.mRecorderProfile;
-  config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth;
-  config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight;
-  config.mMode = ICameraControl::kPictureMode;
-  if (aConfiguration.mMode == CameraMode::Video) {
-    config.mMode = ICameraControl::kVideoMode;
+  /**
+   * Default values, until the dictionary parser can handle them.
+   * NaN indicates no value provided.
+   */
+  pos.latitude = NAN;
+  pos.longitude = NAN;
+  pos.altitude = NAN;
+  pos.timestamp = NAN;
+  aRv = pos.Init(aCx, &aOptions.mPosition);
+  if (aRv.Failed()) {
+    return;
   }
 
-  mSetConfigurationOnSuccessCb = nullptr;
-  if (aOnSuccess.WasPassed()) {
-    mSetConfigurationOnSuccessCb = &aOnSuccess.Value();
-  }
-  mSetConfigurationOnErrorCb = nullptr;
-  if (aOnError.WasPassed()) {
-    mSetConfigurationOnErrorCb = &aOnError.Value();
-  }
-
-  aRv = mCameraControl->SetConfiguration(config);
+  nsICameraErrorCallback* onError =
+    aOnError.WasPassed() ? aOnError.Value() : nullptr;
+  aRv = mCameraControl->TakePicture(size, aOptions.mRotation,
+                                    aOptions.mFileFormat, pos,
+                                    aOptions.mDateTime, onSuccess, onError);
 }
 
 void
-nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess,
-                              const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
-                              ErrorResult& aRv)
+nsDOMCameraControl::GetPreviewStreamVideoMode(JSContext* aCx,
+                                              JS::Handle<JS::Value> aOptions,
+                                              nsICameraPreviewStreamCallback* onSuccess,
+                                              const Optional<nsICameraErrorCallback*>& onError,
+                                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
-
-  nsCOMPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
-  bool cancel = false;
-  if (cb) {
-    // we have a callback, which means we're already in the process of
-    // auto-focusing--cancel the old callback
-    nsCOMPtr<CameraErrorCallback> ecb = mAutoFocusOnErrorCb.forget();
-    ErrorResult ignored;
-    ecb->Call(NS_LITERAL_STRING("Interrupted"), ignored);
-    cancel = true;
-  }
-
-  mAutoFocusOnSuccessCb = &aOnSuccess;
-  mAutoFocusOnErrorCb = nullptr;
-  if (aOnError.WasPassed()) {
-    mAutoFocusOnErrorCb = &aOnError.Value();
-  }
-
-  aRv = mCameraControl->AutoFocus(cancel);
-}
-
-void
-nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
-                                CameraTakePictureCallback& aOnSuccess,
-                                const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
-                                ErrorResult& aRv)
-{
-  MOZ_ASSERT(mCameraControl);
-
-  nsCOMPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
-  if (cb) {
-    // There is already a call to TakePicture() in progress, abort this one and
-    //  invoke the error callback (if one was passed in).
-    if (aOnError.WasPassed()) {
-      ErrorResult ignored;
-      aOnError.Value().Call(NS_LITERAL_STRING("TakePictureAlreadyInProgress"), ignored);
-    }
-    aRv = NS_ERROR_FAILURE;
+  mozilla::idl::CameraRecorderOptions options;
+  aRv = options.Init(aCx, aOptions.address());
+  if (aRv.Failed()) {
     return;
   }
 
-  {
-    ICameraControlParameterSetAutoEnter batch(mCameraControl);
-
-    // XXXmikeh - remove this: see bug 931155
-    ICameraControl::Size s;
-    s.width = aOptions.mPictureSize.mWidth;
-    s.height = aOptions.mPictureSize.mHeight;
-
-    ICameraControl::Position p;
-    p.latitude = aOptions.mPosition.mLatitude;
-    p.longitude = aOptions.mPosition.mLongitude;
-    p.altitude = aOptions.mPosition.mAltitude;
-    p.timestamp = aOptions.mPosition.mTimestamp;
-
-    if (s.width && s.height) {
-      mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
-    }
-    mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation);
-    mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat);
-    mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime);
-    mCameraControl->SetLocation(p);
-  }
-
-  mTakePictureOnSuccessCb = &aOnSuccess;
-  mTakePictureOnErrorCb = nullptr;
-  if (aOnError.WasPassed()) {
-    mTakePictureOnErrorCb = &aOnError.Value();
-  }
-
-  aRv = mCameraControl->TakePicture();
+  aRv = mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess,
+                                                  onError.WasPassed()
+                                                  ? onError.Value() : nullptr);
 }
 
 void
-nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCallback> >& aOnSuccess,
-                                    const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
+nsDOMCameraControl::ReleaseHardware(const Optional<nsICameraReleaseCallback*>& onSuccess,
+                                    const Optional<nsICameraErrorCallback*>& onError,
                                     ErrorResult& aRv)
 {
-  MOZ_ASSERT(mCameraControl);
+  aRv =
+    mCameraControl->ReleaseHardware(
+        onSuccess.WasPassed() ? onSuccess.Value() : nullptr,
+        onError.WasPassed() ? onError.Value() : nullptr);
+}
+
+class GetCameraResult : public nsRunnable
+{
+public:
+  GetCameraResult(nsDOMCameraControl* aDOMCameraControl,
+    nsresult aResult,
+    const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
+    const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
+    uint64_t aWindowId)
+    : mDOMCameraControl(aDOMCameraControl)
+    , mResult(aResult)
+    , mOnSuccessCb(onSuccess)
+    , mOnErrorCb(onError)
+    , mWindowId(aWindowId)
+  { }
 
-  mReleaseOnSuccessCb = nullptr;
-  if (aOnSuccess.WasPassed()) {
-    mReleaseOnSuccessCb = &aOnSuccess.Value();
-  }
-  mReleaseOnErrorCb = nullptr;
-  if (aOnError.WasPassed()) {
-    mReleaseOnErrorCb = &aOnError.Value();
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      DOM_CAMERA_LOGT("%s : this=%p -- BEFORE CALLBACK\n", __func__, this);
+      if (NS_FAILED(mResult)) {
+        if (mOnErrorCb.get()) {
+          mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+        }
+      } else {
+        if (mOnSuccessCb.get()) {
+          mOnSuccessCb->HandleEvent(mDOMCameraControl);
+        }
+      }
+      DOM_CAMERA_LOGT("%s : this=%p -- AFTER CALLBACK\n", __func__, this);
+    }
+
+    /**
+     * Finally, release the extra reference to the DOM-facing camera control.
+     * See the nsDOMCameraControl constructor for the corresponding call to
+     * NS_ADDREF_THIS().
+     */
+    NS_RELEASE(mDOMCameraControl);
+    return NS_OK;
   }
 
-  aRv = mCameraControl->ReleaseHardware();
+protected:
+  /**
+   * 'mDOMCameraControl' is a raw pointer to a previously ADDREF()ed object,
+   * which is released in Run().
+   */
+  nsDOMCameraControl* mDOMCameraControl;
+  nsresult mResult;
+  nsMainThreadPtrHandle<nsICameraGetCameraCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
+};
+
+nsresult
+nsDOMCameraControl::Result(nsresult aResult,
+                           const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
+                           const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
+                           uint64_t aWindowId)
+{
+  nsCOMPtr<GetCameraResult> getCameraResult = new GetCameraResult(this, aResult, onSuccess, onError, aWindowId);
+  return NS_DispatchToMainThread(getCameraResult);
 }
 
 void
 nsDOMCameraControl::Shutdown()
 {
   DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
-  MOZ_ASSERT(mCameraControl);
-
-  // Remove any pending solicited event handlers; these
-  // reference our window object, which in turn references
-  // us. If we don't remove them, we can leak DOM objects.
-  mGetCameraOnSuccessCb = nullptr;
-  mGetCameraOnErrorCb = nullptr;
-  mAutoFocusOnSuccessCb = nullptr;
-  mAutoFocusOnErrorCb = nullptr;
-  mTakePictureOnSuccessCb = nullptr;
-  mTakePictureOnErrorCb = nullptr;
-  mStartRecordingOnSuccessCb = nullptr;
-  mStartRecordingOnErrorCb = nullptr;
-  mReleaseOnSuccessCb = nullptr;
-  mReleaseOnErrorCb = nullptr;
-  mSetConfigurationOnSuccessCb = nullptr;
-  mSetConfigurationOnErrorCb = nullptr;
-
-  // Remove all of the unsolicited event handlers too.
-  mOnShutterCb = nullptr;
-  mOnClosedCb = nullptr;
-  mOnRecorderStateChangeCb = nullptr;
-  mOnPreviewStateChangeCb = nullptr;
-
   mCameraControl->Shutdown();
 }
 
 nsRefPtr<ICameraControl>
 nsDOMCameraControl::GetNativeCameraControl()
 {
   return mCameraControl;
 }
@@ -957,270 +633,8 @@ nsDOMCameraControl::NotifyRecordingStatu
   NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
 
   return MediaManager::NotifyRecordingStatusChange(mWindow,
                                                    aMsg,
                                                    true /* aIsAudio */,
                                                    true /* aIsVideo */);
 }
 
-// Camera Control event handlers--must only be called from the Main Thread!
-void
-nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult ignored;
-
-  DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState);
-
-  switch (aState) {
-    case CameraControlListener::kHardwareOpen:
-      // The hardware is open, so we can return a camera to JS, even if
-      // the preview hasn't started yet.
-      if (mGetCameraOnSuccessCb) {
-        nsCOMPtr<GetCameraCallback> cb = mGetCameraOnSuccessCb.forget();
-        ErrorResult ignored;
-        mGetCameraOnErrorCb = nullptr;
-        cb->Call(*this, *mCurrentConfiguration, ignored);
-      }
-      break;
-
-    case CameraControlListener::kHardwareClosed:
-      if (mReleaseOnSuccessCb) {
-        // If we have this event handler, this was a solicited hardware close.
-        nsCOMPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
-        mReleaseOnErrorCb = nullptr;
-        cb->Call(ignored);
-      } else if(mOnClosedCb) {
-        // If not, something else closed the hardware.
-        nsCOMPtr<CameraClosedCallback> cb = mOnClosedCb;
-        cb->Call(ignored);
-      }
-      break;
-
-    default:
-      MOZ_ASSUME_UNREACHABLE("Unanticipated camera hardware state");
-  }
-}
-
-void
-nsDOMCameraControl::OnShutter()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  DOM_CAMERA_LOGI("DOM ** SNAP **\n");
-
-  nsCOMPtr<CameraShutterCallback> cb = mOnShutterCb;
-  if (cb) {
-    ErrorResult ignored;
-    cb->Call(ignored);
-  }
-}
-
-void
-nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!mOnPreviewStateChangeCb) {
-    return;
-  }
-
-  nsString state;
-  switch (aState) {
-    case CameraControlListener::kPreviewStarted:
-      state = NS_LITERAL_STRING("started");
-      break;
-
-    default:
-      state = NS_LITERAL_STRING("stopped");
-      break;
-  }
-
-  nsCOMPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
-  ErrorResult ignored;
-  cb->Call(state, ignored);
-}
-
-void
-nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
-                                          int32_t aArg, int32_t aTrackNum)
-{
-  // For now, we do nothing with 'aStatus' and 'aTrackNum'.
-  MOZ_ASSERT(NS_IsMainThread());
-
-  ErrorResult ignored;
-  nsString state;
-
-  switch (aState) {
-    case CameraControlListener::kRecorderStarted:
-      if (mStartRecordingOnSuccessCb) {
-        nsCOMPtr<CameraStartRecordingCallback> cb = mStartRecordingOnSuccessCb.forget();
-        mStartRecordingOnErrorCb = nullptr;
-        cb->Call(ignored);
-      }
-      return;
-
-    case CameraControlListener::kRecorderStopped:
-      NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
-      return;
-
-#ifdef MOZ_B2G_CAMERA
-    case CameraControlListener::kFileSizeLimitReached:
-      state = NS_LITERAL_STRING("FileSizeLimitReached");
-      break;
-
-    case CameraControlListener::kVideoLengthLimitReached:
-      state = NS_LITERAL_STRING("VideoLengthLimitReached");
-      break;
-
-    case CameraControlListener::kTrackCompleted:
-      state = NS_LITERAL_STRING("TrackCompleted");
-      break;
-
-    case CameraControlListener::kTrackFailed:
-      state = NS_LITERAL_STRING("TrackFailed");
-      break;
-
-    case CameraControlListener::kMediaRecorderFailed:
-      state = NS_LITERAL_STRING("MediaRecorderFailed");
-      break;
-
-    case CameraControlListener::kMediaServerFailed:
-      state = NS_LITERAL_STRING("MediaServerFailed");
-      break;
-#endif
-
-    default:
-      MOZ_ASSUME_UNREACHABLE("Unanticipated video recorder error");
-      return;
-  }
-
-  nsCOMPtr<CameraRecorderStateChange> cb = mOnRecorderStateChangeCb;
-  if (cb) {
-    cb->Call(state, ignored);
-  }
-}
-
-void
-nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Update our record of the current camera configuration
-  mCurrentConfiguration = aConfiguration;
-
-  DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this);
-  DOM_CAMERA_LOGI("    mode                   : %s\n",
-    mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture");
-  DOM_CAMERA_LOGI("    maximum focus areas    : %d\n",
-    mCurrentConfiguration->mMaxFocusAreas);
-  DOM_CAMERA_LOGI("    maximum metering areas : %d\n",
-    mCurrentConfiguration->mMaxMeteringAreas);
-  DOM_CAMERA_LOGI("    preview size (w x h)   : %d x %d\n",
-    mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
-  DOM_CAMERA_LOGI("    recorder profile       : %s\n",
-    NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
-
-  nsCOMPtr<CameraSetConfigurationCallback> cb = mSetConfigurationOnSuccessCb.forget();
-  mSetConfigurationOnErrorCb = nullptr;
-  if (cb) {
-    ErrorResult ignored;
-    cb->Call(*mCurrentConfiguration, ignored);
-  }
-}
-
-void
-nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult ignored;
-
-  nsCOMPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
-  mAutoFocusOnErrorCb = nullptr;
-  cb->Call(aAutoFocusSucceeded, ignored);
-}
-
-void
-nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult ignored;
-
-  nsCOMPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
-  mTakePictureOnErrorCb = nullptr;
-  cb->Call(aPicture, ignored);
-}
-
-void
-nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsCOMPtr<CameraErrorCallback>* errorCb;
-  switch (aContext) {
-    case CameraControlListener::kInGetCamera:
-      mGetCameraOnSuccessCb = nullptr;
-      errorCb = &mGetCameraOnErrorCb;
-      break;
-
-    case CameraControlListener::kInSetConfiguration:
-      mSetConfigurationOnSuccessCb = nullptr;
-      errorCb = &mSetConfigurationOnErrorCb;
-      break;
-
-    case CameraControlListener::kInAutoFocus:
-      mAutoFocusOnSuccessCb = nullptr;
-      errorCb = &mAutoFocusOnErrorCb;
-      break;
-
-    case CameraControlListener::kInTakePicture:
-      mTakePictureOnSuccessCb = nullptr;
-      errorCb = &mTakePictureOnErrorCb;
-      break;
-
-    case CameraControlListener::kInStartRecording:
-      mStartRecordingOnSuccessCb = nullptr;
-      errorCb = &mStartRecordingOnErrorCb;
-      break;
-
-    case CameraControlListener::kInReleaseHardware:
-      mReleaseOnSuccessCb = nullptr;
-      errorCb = &mReleaseOnErrorCb;
-      break;
-
-    case CameraControlListener::kInStopRecording:
-      NS_WARNING("Failed to stop recording (which shouldn't happen)!");
-      MOZ_CRASH();
-      break;
-
-    case CameraControlListener::kInStartPreview:
-      NS_WARNING("Failed to (re)start preview!");
-      MOZ_CRASH();
-      break;
-
-    case CameraControlListener::kInUnspecified:
-      if (aError.EqualsASCII("ErrorServiceFailed")) {
-        // If the camera service fails, we will get preview-stopped and
-        //  hardware-closed events, so nothing to do here.
-        return;
-      }
-      // fallthrough
-
-    default:
-      MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state");
-      return;
-  }
-
-  MOZ_ASSERT(errorCb);
-
-  if (!*errorCb) {
-    DOM_CAMERA_LOGW("DOM No error handler for error '%s' at %d\n",
-      NS_LossyConvertUTF16toASCII(aError).get(), aContext);
-    return;
-  }
-
-  // kung-fu death grip
-  nsCOMPtr<CameraErrorCallback> cb = (*errorCb).forget();
-  ErrorResult ignored;
-  cb->Call(aError, ignored);
-}
-
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -3,60 +3,61 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_DOMCAMERACONTROL_H
 #define DOM_CAMERA_DOMCAMERACONTROL_H
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "mozilla/dom/CameraControlBinding.h"
+#include "nsIDOMEventListener.h"
+#include "DictionaryHelpers.h"
 #include "ICameraControl.h"
+#include "DOMCameraPreview.h"
+#include "nsIDOMCameraManager.h"
 #include "CameraCommon.h"
-#include "DOMMediaStream.h"
 #include "AudioChannelAgent.h"
 #include "nsProxyRelease.h"
 #include "nsHashPropertyBag.h"
 #include "DeviceStorage.h"
-#include "DOMCameraControlListener.h"
 
 class nsDOMDeviceStorage;
 class nsPIDOMWindow;
-class nsIDOMBlob;
 
 namespace mozilla {
-
 namespace dom {
-  class CameraCapabilities;
-  class CameraPictureOptions;
-  class CameraStartRecordingOptions;
-  template<typename T> class Optional;
+class CameraPictureOptions;
+template<typename T> class Optional;
 }
 class ErrorResult;
-class StartRecordingHelper;
 
 // Main camera control.
-class nsDOMCameraControl MOZ_FINAL : public DOMMediaStream
+class nsDOMCameraControl MOZ_FINAL : public nsIDOMEventListener,
+                                     public nsWrapperCache
 {
 public:
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMCameraControl, DOMMediaStream)
-  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCameraControl)
 
-  nsDOMCameraControl(uint32_t aCameraId,
-                     const dom::CameraConfiguration& aInitialConfig,
-                     dom::GetCameraCallback* aOnSuccess,
-                     dom::CameraErrorCallback* aOnError,
-                     nsPIDOMWindow* aWindow);
+  nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread,
+                     nsICameraGetCameraCallback* onSuccess,
+                     nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow);
+  nsresult Result(nsresult aResult,
+                  const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
+                  const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
+                  uint64_t aWindowId);
   nsRefPtr<ICameraControl> GetNativeCameraControl();
 
   void Shutdown();
 
   nsPIDOMWindow* GetParentObject() const { return mWindow; }
 
-  // Attributes.
+  // WebIDL
+  nsICameraCapabilities* Capabilities();
   void GetEffect(nsString& aEffect, ErrorResult& aRv);
   void SetEffect(const nsAString& aEffect, ErrorResult& aRv);
   void GetWhiteBalanceMode(nsString& aMode, ErrorResult& aRv);
   void SetWhiteBalanceMode(const nsAString& aMode, ErrorResult& aRv);
   void GetSceneMode(nsString& aMode, ErrorResult& aRv);
   void SetSceneMode(const nsAString& aMode, ErrorResult& aRv);
   void GetFlashMode(nsString& aMode, ErrorResult& aRv);
   void SetFlashMode(const nsAString& aMode, ErrorResult& aRv);
@@ -74,137 +75,55 @@ public:
   void SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv);
   double GetFocalLength(ErrorResult& aRv);
   double GetFocusDistanceNear(ErrorResult& aRv);
   double GetFocusDistanceOptimum(ErrorResult& aRv);
   double GetFocusDistanceFar(ErrorResult& aRv);
   void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
   double GetExposureCompensation(ErrorResult& aRv);
   int32_t SensorAngle();
-  already_AddRefed<dom::CameraCapabilities> Capabilities();
-
-  // Unsolicited event handlers.
-  already_AddRefed<dom::CameraShutterCallback> GetOnShutter();
-  void SetOnShutter(dom::CameraShutterCallback* aCb);
-  already_AddRefed<dom::CameraClosedCallback> GetOnClosed();
-  void SetOnClosed(dom::CameraClosedCallback* aCb);
-  already_AddRefed<dom::CameraRecorderStateChange> GetOnRecorderStateChange();
-  void SetOnRecorderStateChange(dom::CameraRecorderStateChange* aCb);
-  already_AddRefed<dom::CameraPreviewStateChange> GetOnPreviewStateChange();
-  void SetOnPreviewStateChange(dom::CameraPreviewStateChange* aCb);
-
-  // Methods.
-  void SetConfiguration(const dom::CameraConfiguration& aConfiguration,
-                        const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
-                        const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
-                        ErrorResult& aRv);
-  void AutoFocus(dom::CameraAutoFocusCallback& aOnSuccess,
-                 const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
-                 ErrorResult& aRv);
-  void TakePicture(const dom::CameraPictureOptions& aOptions,
-                   dom::CameraTakePictureCallback& aOnSuccess,
-                   const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
+  already_AddRefed<nsICameraShutterCallback> GetOnShutter(ErrorResult& aRv);
+  void SetOnShutter(nsICameraShutterCallback* aCb, ErrorResult& aRv);
+  already_AddRefed<nsICameraClosedCallback> GetOnClosed(ErrorResult& aRv);
+  void SetOnClosed(nsICameraClosedCallback* aCb, ErrorResult& aRv);
+  already_AddRefed<nsICameraRecorderStateChange> GetOnRecorderStateChange(ErrorResult& aRv);
+  void SetOnRecorderStateChange(nsICameraRecorderStateChange* aCb, ErrorResult& aRv);
+  void AutoFocus(nsICameraAutoFocusCallback* aOnSuccess, const dom::Optional<nsICameraErrorCallback*>& aOnErro, ErrorResult& aRvr);
+  void TakePicture(JSContext* aCx, const dom::CameraPictureOptions& aOptions,
+                   nsICameraTakePictureCallback* onSuccess,
+                   const dom::Optional<nsICameraErrorCallback* >& onError,
                    ErrorResult& aRv);
-  void StartRecording(const dom::CameraStartRecordingOptions& aOptions,
-                      nsDOMDeviceStorage& storageArea,
-                      const nsAString& filename,
-                      dom::CameraStartRecordingCallback& aOnSuccess,
-                      const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
-                      ErrorResult& aRv);
+  already_AddRefed<nsICameraPreviewStateChange> GetOnPreviewStateChange() const;
+  void SetOnPreviewStateChange(nsICameraPreviewStateChange* aOnStateChange);
+  void GetPreviewStreamVideoMode(JSContext* cx, JS::Handle<JS::Value> aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
+  void StartRecording(JSContext* cx, JS::Handle<JS::Value> aOptions, nsDOMDeviceStorage& storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
   void StopRecording(ErrorResult& aRv);
+  void GetPreviewStream(JSContext* cx, JS::Handle<JS::Value> aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
   void ResumePreview(ErrorResult& aRv);
-  void ReleaseHardware(const dom::Optional<dom::OwningNonNull<dom::CameraReleaseCallback> >& aOnSuccess,
-                       const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
-                       ErrorResult& aRv);
-
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  void ReleaseHardware(const dom::Optional<nsICameraReleaseCallback* >& onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
 
 protected:
   virtual ~nsDOMCameraControl();
 
-  class DOMCameraConfiguration : public dom::CameraConfiguration
-  {
-  public:
-    NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration)
-
-    DOMCameraConfiguration();
-    DOMCameraConfiguration(const dom::CameraConfiguration& aConfiguration);
-
-    // Additional configuration options that aren't exposed to the DOM
-    uint32_t mMaxFocusAreas;
-    uint32_t mMaxMeteringAreas;
-
-  protected:
-    ~DOMCameraConfiguration();
-  };
-
-  friend class DOMCameraControlListener;
-  friend class mozilla::StartRecordingHelper;
-
-  void OnCreatedFileDescriptor(bool aSucceeded);
-
-  void OnAutoFocusComplete(bool aAutoFocusSucceeded);
-  void OnTakePictureComplete(nsIDOMBlob* aPicture);
-
-  void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState);
-  void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState);
-  void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum);
-  void OnConfigurationChange(DOMCameraConfiguration* aConfiguration);
-  void OnShutter();
-  void OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& mError);
-
-  bool IsWindowStillActive();
-
-  nsresult NotifyRecordingStatusChange(const nsString& aMsg);
-
-  nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control
-
-  // An agent used to join audio channel service.
-  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
-
-  nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
-  nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
-
-  nsRefPtr<DOMCameraConfiguration>              mCurrentConfiguration;
-  nsRefPtr<dom::CameraCapabilities>             mCapabilities;
-
-  // solicited camera control event handlers
-  nsCOMPtr<dom::GetCameraCallback>              mGetCameraOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mGetCameraOnErrorCb;
-  nsCOMPtr<dom::CameraAutoFocusCallback>        mAutoFocusOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mAutoFocusOnErrorCb;
-  nsCOMPtr<dom::CameraTakePictureCallback>      mTakePictureOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mTakePictureOnErrorCb;
-  nsCOMPtr<dom::CameraStartRecordingCallback>   mStartRecordingOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mStartRecordingOnErrorCb;
-  nsCOMPtr<dom::CameraReleaseCallback>          mReleaseOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mReleaseOnErrorCb;
-  nsCOMPtr<dom::CameraSetConfigurationCallback> mSetConfigurationOnSuccessCb;
-  nsCOMPtr<dom::CameraErrorCallback>            mSetConfigurationOnErrorCb;
-
-  // unsolicited event handlers
-  nsCOMPtr<dom::CameraShutterCallback>          mOnShutterCb;
-  nsCOMPtr<dom::CameraClosedCallback>           mOnClosedCb;
-  nsCOMPtr<dom::CameraRecorderStateChange>      mOnRecorderStateChangeCb;
-  nsCOMPtr<dom::CameraPreviewStateChange>       mOnPreviewStateChangeCb;
-
-  // Camera event listener; we only need this weak reference so that
-  //  we can remove the listener from the camera when we're done
-  //  with it.
-  DOMCameraControlListener* mListener;
-
-  // our viewfinder stream
-  CameraPreviewMediaStream* mInput;
-
-  // set once when this object is created
-  nsCOMPtr<nsPIDOMWindow>   mWindow;
-
-  dom::CameraStartRecordingOptions mOptions;
-  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
-
 private:
   nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
   nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  nsresult NotifyRecordingStatusChange(const nsString& aMsg);
+
+  mozilla::idl::CameraStartRecordingOptions mOptions;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+
+protected:
+  /* additional members */
+  nsRefPtr<ICameraControl>        mCameraControl; // non-DOM camera control
+  nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
+  // An agent used to join audio channel service.
+  nsCOMPtr<nsIAudioChannelAgent>  mAudioChannelAgent;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
 };
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_DOMCAMERACONTROL_H
deleted file mode 100644
--- a/dom/camera/DOMCameraControlListener.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/* 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 "DOMCameraControlListener.h"
-#include "nsThreadUtils.h"
-#include "nsDOMFile.h"
-#include "CameraCommon.h"
-#include "DOMCameraControl.h"
-#include "CameraPreviewMediaStream.h"
-#include "mozilla/dom/CameraManagerBinding.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-// Boilerplate callback runnable
-class DOMCameraControlListener::DOMCallback : public nsRunnable
-{
-public:
-  DOMCallback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl)
-    : mDOMCameraControl(aDOMCameraControl)
-  { }
-  virtual ~DOMCallback() { }
-
-  virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0;
-
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    nsRefPtr<nsDOMCameraControl> camera = mDOMCameraControl.get();
-    if (camera) {
-      RunCallback(camera);
-    }
-    return NS_OK;
-  }
-
-protected:
-  nsMainThreadPtrHandle<nsDOMCameraControl> mDOMCameraControl;
-};
-
-// Specific callback handlers
-void
-DOMCameraControlListener::OnHardwareStateChange(HardwareState aState)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             HardwareState aState)
-      : DOMCallback(aDOMCameraControl)
-      , mState(aState)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      aDOMCameraControl->OnHardwareStateChange(mState);
-    }
-
-  protected:
-    HardwareState mState;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
-}
-
-void
-DOMCameraControlListener::OnPreviewStateChange(PreviewState aState)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             PreviewState aState)
-      : DOMCallback(aDOMCameraControl)
-      , mState(aState)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      aDOMCameraControl->OnPreviewStateChange(mState);
-    }
-
-  protected:
-    PreviewState mState;
-  };
-
-  switch (aState) {
-    case kPreviewStopped:
-      // Clear the current frame right away, without dispatching a
-      //  runnable. This is an ugly coupling between the camera's
-      //  SurfaceTextureClient and the MediaStream/ImageContainer,
-      //  but without it, the preview can fail to start.
-      DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n");
-      mStream->ClearCurrentFrame();
-      break;
-
-    case kPreviewPaused:
-      // In the paused state, we still want to reflect the change
-      //  in preview state, but we don't want to clear the current
-      //  frame as above, since doing so seems to cause genlock
-      //  problems when we restart the preview. See bug 957749.
-      DOM_CAMERA_LOGI("Preview paused\n");
-      break;
-
-    case kPreviewStarted:
-      DOM_CAMERA_LOGI("Preview started\n");
-      break;
-
-    default:
-      DOM_CAMERA_LOGE("Unknown preview state %d\n", aState);
-      MOZ_ASSUME_UNREACHABLE("Invalid preview state");
-      return;
-  }
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
-}
-
-void
-DOMCameraControlListener::OnRecorderStateChange(RecorderState aState,
-                                                int32_t aStatus, int32_t aTrackNum)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             RecorderState aState,
-             int32_t aStatus,
-             int32_t aTrackNum)
-      : DOMCallback(aDOMCameraControl)
-      , mState(aState)
-      , mStatus(aStatus)
-      , mTrackNum(aTrackNum)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum);
-    }
-
-  protected:
-    RecorderState mState;
-    int32_t mStatus;
-    int32_t mTrackNum;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum));
-}
-
-void
-DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             const CameraListenerConfiguration& aConfiguration)
-      : DOMCallback(aDOMCameraControl)
-      , mConfiguration(aConfiguration)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      nsRefPtr<nsDOMCameraControl::DOMCameraConfiguration> config =
-        new nsDOMCameraControl::DOMCameraConfiguration();
-
-      switch (mConfiguration.mMode) {
-        case ICameraControl::kVideoMode:
-          config->mMode = CameraMode::Video;
-          break;
-
-        case ICameraControl::kPictureMode:
-          config->mMode = CameraMode::Picture;
-          break;
-
-        default:
-          MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
-      }
-
-      // Map CameraControl parameters to their DOM-facing equivalents
-      config->mRecorderProfile = mConfiguration.mRecorderProfile;
-      config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
-      config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
-      config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
-      config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;
-
-      aDOMCameraControl->OnConfigurationChange(config);
-    }
-
-  protected:
-    const CameraListenerConfiguration mConfiguration;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration));
-}
-
-void
-DOMCameraControlListener::OnShutter()
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl)
-      : DOMCallback(aDOMCameraControl)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      aDOMCameraControl->OnShutter();
-    }
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl));
-}
-
-bool
-DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
-{
-  DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight);
-
-  mStream->SetCurrentFrame(gfxIntSize(aWidth, aHeight), aImage);
-  return true;
-}
-
-void
-DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             bool aAutoFocusSucceeded)
-      : DOMCallback(aDOMCameraControl)
-      , mAutoFocusSucceeded(aAutoFocusSucceeded)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded);
-    }
-
-  protected:
-    bool mAutoFocusSucceeded;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded));
-}
-
-void
-DOMCameraControlListener::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
-      : DOMCallback(aDOMCameraControl)
-      , mData(aData)
-      , mLength(aLength)
-      , mMimeType(aMimeType)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      nsCOMPtr<nsIDOMBlob> picture = new nsDOMMemoryFile(static_cast<void*>(mData),
-                                                         static_cast<uint64_t>(mLength),
-                                                         mMimeType);
-      aDOMCameraControl->OnTakePictureComplete(picture);
-    }
-
-  protected:
-    uint8_t* mData;
-    uint32_t mLength;
-    nsString mMimeType;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType));
-}
-
-void
-DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aError)
-{
-  class Callback : public DOMCallback
-  {
-  public:
-    Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
-             CameraErrorContext aContext,
-             CameraError aError)
-      : DOMCallback(aDOMCameraControl)
-      , mContext(aContext)
-      , mError(aError)
-    { }
-
-    void
-    RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
-    {
-      nsString error;
-
-      switch (mError) {
-        case kErrorServiceFailed:
-          error = NS_LITERAL_STRING("ErrorServiceFailed");
-          break;
-
-        case kErrorApiFailed:
-          // XXXmikeh legacy error placeholder
-          error = NS_LITERAL_STRING("FAILURE");
-          break;
-
-        default:
-          error = NS_LITERAL_STRING("ErrorUnknown");
-          break;
-      }
-      aDOMCameraControl->OnError(mContext, error);
-    }
-
-  protected:
-    CameraErrorContext mContext;
-    CameraError mError;
-  };
-
-  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError));
-}
deleted file mode 100644
--- a/dom/camera/DOMCameraControlListener.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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 DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
-#define DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
-
-#include "nsProxyRelease.h"
-#include "CameraControlListener.h"
-
-namespace mozilla {
-
-class nsDOMCameraControl;
-class CameraPreviewMediaStream;
-
-class DOMCameraControlListener : public CameraControlListener
-{
-protected:
-  nsMainThreadPtrHandle<nsDOMCameraControl> mDOMCameraControl;
-  CameraPreviewMediaStream* mStream;
-
-  class DOMCallback;
-
-public:
-  DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream)
-    : mDOMCameraControl(new nsMainThreadPtrHolder<nsDOMCameraControl>(aDOMCameraControl))
-    , mStream(aStream)
-  { }
-
-  void OnAutoFocusComplete(bool aAutoFocusSucceeded);
-  void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
-
-  void OnHardwareStateChange(HardwareState aState);
-  void OnPreviewStateChange(PreviewState aState);
-  void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum);
-  void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration);
-  void OnShutter();
-  bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
-  void OnError(CameraErrorContext aContext, CameraError aError);
-
-private:
-  DOMCameraControlListener(const DOMCameraControlListener&) MOZ_DELETE;
-  DOMCameraControlListener& operator=(const DOMCameraControlListener&) MOZ_DELETE;
-};
-
-} // namespace mozilla
-
-#endif // DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -1,80 +1,79 @@
 /* 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 "DOMCameraManager.h"
 #include "nsDebug.h"
-#include "jsapi.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/Services.h"
 #include "nsObserverService.h"
 #include "nsIPermissionManager.h"
 #include "DOMCameraControl.h"
+#include "DOMCameraManager.h"
 #include "nsDOMClassInfo.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
-#include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/CameraManagerBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager)
 
 /**
  * Global camera logging object
  *
  * Set the NSPR_LOG_MODULES environment variable to enable logging
  * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
  */
 PRLogModuleInfo*
 GetCameraLog()
 {
   static PRLogModuleInfo *sLog;
-  if (!sLog) {
+  if (!sLog)
     sLog = PR_NewLogModule("Camera");
-  }
   return sLog;
 }
 
+/**
+ * nsDOMCameraManager::GetListOfCameras
+ * is implementation-specific, and can be found in (e.g.)
+ * GonkCameraManager.cpp and FallbackCameraManager.cpp.
+ */
+
 WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
 
 nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
   : mWindowId(aWindow->WindowID())
+  , mCameraThread(nullptr)
   , mWindow(aWindow)
 {
   /* member initializers and constructor code */
   DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
-  MOZ_COUNT_CTOR(nsDOMCameraManager);
   SetIsDOMBinding();
 }
 
 nsDOMCameraManager::~nsDOMCameraManager()
 {
   /* destructor code */
-  MOZ_COUNT_DTOR(nsDOMCameraManager);
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-void
-nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
-{
-  aRv = ICameraControl::GetListOfCameras(aList);
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(this, "xpcom-shutdown");
+  }
 }
 
 bool
 nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, false);
@@ -102,38 +101,40 @@ nsDOMCameraManager::CreateInstance(nsPID
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   obs->AddObserver(cameraManager, "xpcom-shutdown", true);
 
   return cameraManager.forget();
 }
 
 void
-nsDOMCameraManager::GetCamera(const nsAString& aCamera,
-                              const CameraConfiguration& aInitialConfig,
-                              GetCameraCallback& aOnSuccess,
-                              const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
+nsDOMCameraManager::GetCamera(const CameraSelector& aOptions,
+                              nsICameraGetCameraCallback* onSuccess,
+                              const Optional<nsICameraErrorCallback*>& onError,
                               ErrorResult& aRv)
 {
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-
   uint32_t cameraId = 0;  // back (or forward-facing) camera by default
-  if (aCamera.EqualsLiteral("front")) {
+  if (aOptions.mCamera.EqualsLiteral("front")) {
     cameraId = 1;
   }
 
-  nsCOMPtr<CameraErrorCallback> errorCallback = nullptr;
-  if (aOnError.WasPassed()) {
-    errorCallback = &aOnError.Value();
+  // reuse the same camera thread to conserve resources
+  if (!mCameraThread) {
+    aRv = NS_NewThread(getter_AddRefs(mCameraThread));
+    if (aRv.Failed()) {
+      return;
+    }
   }
 
-  // Creating this object will trigger the aOnSuccess callback
-  //  (or the aOnError one, if it fails).
+  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+  // Creating this object will trigger the onSuccess handler
   nsRefPtr<nsDOMCameraControl> cameraControl =
-    new nsDOMCameraControl(cameraId, aInitialConfig, &aOnSuccess, errorCallback, mWindow);
+    new nsDOMCameraControl(cameraId, mCameraThread,
+                           onSuccess, onError.WasPassed() ? onError.Value() : nullptr, mWindow);
 
   Register(cameraControl);
 }
 
 void
 nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
 {
   DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId);
--- a/dom/camera/DOMCameraManager.h
+++ b/dom/camera/DOMCameraManager.h
@@ -5,34 +5,34 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_DOMCAMERAMANAGER_H
 #define DOM_CAMERA_DOMCAMERAMANAGER_H
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
+#include "nsIThread.h"
 #include "nsIObserver.h"
+#include "nsThreadUtils.h"
 #include "nsHashKeys.h"
 #include "nsWrapperCache.h"
 #include "nsWeakReference.h"
 #include "nsClassHashtable.h"
+#include "nsIDOMCameraManager.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
-
-class nsPIDOMWindow;
+#include "nsPIDOMWindow.h"
 
 namespace mozilla {
   class ErrorResult;
-  class nsDOMCameraControl;
-  namespace dom {
-    class CameraConfiguration;
-    class GetCameraCallback;
-    class CameraErrorCallback;
-  }
+class nsDOMCameraControl;
+namespace dom {
+class CameraSelector;
+}
 }
 
 typedef nsTArray<nsRefPtr<mozilla::nsDOMCameraControl> > CameraControls;
 typedef nsClassHashtable<nsUint64HashKey, CameraControls> WindowTable;
 
 class nsDOMCameraManager MOZ_FINAL
   : public nsIObserver
   , public nsSupportsWeakReference
@@ -47,21 +47,23 @@ public:
   static bool CheckPermission(nsPIDOMWindow* aWindow);
   static already_AddRefed<nsDOMCameraManager>
     CreateInstance(nsPIDOMWindow* aWindow);
   static bool IsWindowStillActive(uint64_t aWindowId);
 
   void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
   void OnNavigation(uint64_t aWindowId);
 
+  nsresult GetNumberOfCameras(int32_t& aDeviceCount);
+  nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName);
+
   // WebIDL
-  void GetCamera(const nsAString& aCamera,
-                 const mozilla::dom::CameraConfiguration& aOptions,
-                 mozilla::dom::GetCameraCallback& aOnSuccess,
-                 const mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::CameraErrorCallback> >& aOnError,
+  void GetCamera(const mozilla::dom::CameraSelector& aOptions,
+                 nsICameraGetCameraCallback* aCallback,
+                 const mozilla::dom::Optional<nsICameraErrorCallback*>& ErrorCallback,
                  mozilla::ErrorResult& aRv);
   void GetListOfCameras(nsTArray<nsString>& aList, mozilla::ErrorResult& aRv);
 
   nsPIDOMWindow* GetParentObject() const { return mWindow; }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
     MOZ_OVERRIDE;
 
 protected:
@@ -72,17 +74,37 @@ protected:
 private:
   nsDOMCameraManager() MOZ_DELETE;
   nsDOMCameraManager(nsPIDOMWindow* aWindow);
   nsDOMCameraManager(const nsDOMCameraManager&) MOZ_DELETE;
   nsDOMCameraManager& operator=(const nsDOMCameraManager&) MOZ_DELETE;
 
 protected:
   uint64_t mWindowId;
+  nsCOMPtr<nsIThread> mCameraThread;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   /**
-   * 'sActiveWindows' is only ever accessed while in the Main Thread,
+   * 'mActiveWindows' is only ever accessed while in the main thread,
    * so it is not otherwise protected.
    */
   static ::WindowTable* sActiveWindows;
 };
 
+class GetCameraTask : public nsRunnable
+{
+public:
+  GetCameraTask(uint32_t aCameraId, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsIThread* aCameraThread)
+    : mCameraId(aCameraId)
+    , mOnSuccessCb(onSuccess)
+    , mOnErrorCb(onError)
+    , mCameraThread(aCameraThread)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE;
+
+protected:
+  uint32_t mCameraId;
+  nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+  nsCOMPtr<nsIThread> mCameraThread;
+};
+
 #endif // DOM_CAMERA_DOMCAMERAMANAGER_H
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraPreview.cpp
@@ -0,0 +1,304 @@
+/* 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 "Layers.h"
+#include "VideoUtils.h"
+#include "DOMCameraPreview.h"
+#include "CameraCommon.h"
+#include "nsGlobalWindow.h"
+#include "nsIDocument.h"
+#include "nsPIDOMWindow.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+
+/**
+ * 'PreviewControl' is a helper class that dispatches preview control
+ * events from the main thread.
+ *
+ * NS_NewRunnableMethod() can't be used because it AddRef()s the method's
+ * object, which can't be done off the main thread for cycle collection
+ * participants.
+ *
+ * Before using this class, 'aDOMPreview' must be appropriately AddRef()ed.
+ */
+class PreviewControl : public nsRunnable
+{
+public:
+  enum {
+    START,
+    STOP,
+    STARTED,
+    STOPPED
+  };
+  PreviewControl(DOMCameraPreview* aDOMPreview, uint32_t aControl)
+    : mDOMPreview(aDOMPreview)
+    , mControl(aControl)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "PreviewControl not run on main thread");
+
+    switch (mControl) {
+      case START:
+        mDOMPreview->Start();
+        break;
+
+      case STOP:
+        mDOMPreview->StopPreview();
+        break;
+
+      case STARTED:
+        mDOMPreview->SetStateStarted();
+        break;
+
+      case STOPPED:
+        mDOMPreview->SetStateStopped();
+        break;
+
+      default:
+        DOM_CAMERA_LOGE("PreviewControl: invalid control %d\n", mControl);
+        break;
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  /**
+   * This must be a raw pointer because this class is not created on the
+   * main thread, and DOMCameraPreview is not threadsafe.  Prior to
+   * issuing a preview control event, the caller must ensure that
+   * mDOMPreview will not disappear.
+   */
+  DOMCameraPreview* mDOMPreview;
+  uint32_t mControl;
+};
+
+class DOMCameraPreviewListener : public MediaStreamListener
+{
+public:
+  DOMCameraPreviewListener(DOMCameraPreview* aDOMPreview) :
+    mDOMPreview(aDOMPreview)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  ~DOMCameraPreviewListener()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+
+#ifdef PR_LOGGING
+    const char* state;
+
+    switch (aConsuming) {
+      case NOT_CONSUMED:
+        state = "not consuming";
+        break;
+
+      case CONSUMED:
+        state = "consuming";
+        break;
+
+      default:
+        state = "unknown";
+        break;
+    }
+
+    DOM_CAMERA_LOGA("camera viewfinder is %s\n", state);
+#endif
+    nsCOMPtr<nsIRunnable> previewControl;
+
+    switch (aConsuming) {
+      case NOT_CONSUMED:
+        previewControl = new PreviewControl(mDOMPreview, PreviewControl::STOP);
+        break;
+
+      case CONSUMED:
+        previewControl = new PreviewControl(mDOMPreview, PreviewControl::START);
+        break;
+
+      default:
+        return;
+    }
+
+    nsresult rv = NS_DispatchToMainThread(previewControl);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGE("Failed to dispatch preview control (%d)!\n", rv);
+    }
+  }
+
+protected:
+  // Raw pointer; if we exist, 'mDOMPreview' exists as well
+  DOMCameraPreview* mDOMPreview;
+};
+
+DOMCameraPreview::DOMCameraPreview(nsGlobalWindow* aWindow,
+                                   ICameraControl* aCameraControl,
+                                   uint32_t aWidth, uint32_t aHeight,
+                                   uint32_t aFrameRate)
+  : DOMMediaStream()
+  , mState(STOPPED)
+  , mWidth(aWidth)
+  , mHeight(aHeight)
+  , mFramesPerSecond(aFrameRate)
+  , mFrameCount(0)
+  , mCameraControl(aCameraControl)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p : mWidth=%d, mHeight=%d, mFramesPerSecond=%d\n", __func__, __LINE__, this, mWidth, mHeight, mFramesPerSecond);
+
+  mImageContainer = LayerManager::CreateImageContainer();
+  mWindow = aWindow;
+  mInput = new CameraPreviewMediaStream(this);
+  mStream = mInput;
+
+  mListener = new DOMCameraPreviewListener(this);
+  mInput->AddListener(mListener);
+
+  if (aWindow->GetExtantDoc()) {
+    CombineWithPrincipal(aWindow->GetExtantDoc()->NodePrincipal());
+  }
+}
+
+DOMCameraPreview::~DOMCameraPreview()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mListener=%p\n", __func__, __LINE__, this, mListener);
+  mInput->RemoveListener(mListener);
+}
+
+bool
+DOMCameraPreview::HaveEnoughBuffered()
+{
+  return true;
+}
+
+bool
+DOMCameraPreview::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  if (!aBuffer || !aBuilder) {
+    return false;
+  }
+  if (mState != STARTED) {
+    return false;
+  }
+
+  nsRefPtr<Image> image = mImageContainer->CreateImage(aFormat);
+  aBuilder(image, aBuffer, mWidth, mHeight);
+
+  mInput->SetCurrentFrame(gfxIntSize(mWidth, mHeight), image);
+  return true;
+}
+
+void
+DOMCameraPreview::Start()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Start() not called from main thread");
+  if (mState != STOPPED) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Starting preview stream\n");
+
+  /**
+   * Add a reference to ourselves to make sure we stay alive while
+   * the preview is running, as the CameraControlImpl object holds a
+   * weak reference to us.
+   *
+   * This reference is removed in SetStateStopped().
+   */
+  NS_ADDREF_THIS();
+  DOM_CAMERA_SETSTATE(STARTING);
+  mCameraControl->StartPreview(this);
+}
+
+void
+DOMCameraPreview::SetStateStarted()
+{
+  NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread");
+
+  DOM_CAMERA_SETSTATE(STARTED);
+  DOM_CAMERA_LOGI("Preview stream started\n");
+}
+
+void
+DOMCameraPreview::Started()
+{
+  if (mState != STARTING) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Dispatching preview stream started\n");
+  nsCOMPtr<nsIRunnable> started = new PreviewControl(this, PreviewControl::STARTED);
+  nsresult rv = NS_DispatchToMainThread(started);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("failed to set statrted state (%d), POTENTIAL MEMORY LEAK!\n", rv);
+  }
+}
+
+void
+DOMCameraPreview::StopPreview()
+{
+  NS_ASSERTION(NS_IsMainThread(), "StopPreview() not called from main thread");
+  if (mState != STARTED) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Stopping preview stream\n");
+  DOM_CAMERA_SETSTATE(STOPPING);
+  mCameraControl->StopPreview();
+  //mInput->EndTrack(TRACK_VIDEO);
+  //mInput->Finish();
+}
+
+void
+DOMCameraPreview::SetStateStopped()
+{
+  NS_ASSERTION(NS_IsMainThread(), "SetStateStopped() not called from main thread");
+
+  // see bug 809259 and bug 817367.
+  if (mState != STOPPING) {
+    //mInput->EndTrack(TRACK_VIDEO);
+    //mInput->Finish();
+  }
+  DOM_CAMERA_SETSTATE(STOPPED);
+  DOM_CAMERA_LOGI("Preview stream stopped\n");
+
+  /**
+   * Only remove the reference added in Start() once the preview
+   * has stopped completely.
+   */
+  NS_RELEASE_THIS();
+}
+
+void
+DOMCameraPreview::Stopped(bool aForced)
+{
+  if (mState != STOPPING && !aForced) {
+    return;
+  }
+
+  mInput->ClearCurrentFrame();
+
+  DOM_CAMERA_LOGI("Dispatching preview stream stopped\n");
+  nsCOMPtr<nsIRunnable> stopped = new PreviewControl(this, PreviewControl::STOPPED);
+  nsresult rv = NS_DispatchToMainThread(stopped);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("failed to decrement reference count (%d), MEMORY LEAK!\n", rv);
+  }
+}
+
+void
+DOMCameraPreview::Error()
+{
+  DOM_CAMERA_LOGE("Error occurred changing preview state!\n");
+  Stopped(true);
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraPreview.h
@@ -0,0 +1,99 @@
+/* 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 DOM_CAMERA_DOMCAMERAPREVIEW_H
+#define DOM_CAMERA_DOMCAMERAPREVIEW_H
+
+#include "nsCycleCollectionParticipant.h"
+#include "MediaStreamGraph.h"
+#include "StreamBuffer.h"
+#include "ICameraControl.h"
+#include "DOMMediaStream.h"
+#include "CameraPreviewMediaStream.h"
+#include "CameraCommon.h"
+
+class nsGlobalWindow;
+
+namespace mozilla {
+
+typedef void (*FrameBuilder)(mozilla::layers::Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight);
+
+/**
+ * DOMCameraPreview is only exposed to the DOM as an nsDOMMediaStream,
+ * which is a cycle-collection participant already, and we don't
+ * add any traceable fields here, so we don't need to declare any
+ * more cycle-collection goop.
+ */
+class DOMCameraPreview : public DOMMediaStream
+{
+protected:
+  enum { TRACK_VIDEO = 1 };
+
+public:
+  DOMCameraPreview(nsGlobalWindow* aWindow, ICameraControl* aCameraControl,
+                   uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond = 30);
+
+  bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, mozilla::FrameBuilder aBuilder);
+  bool HaveEnoughBuffered();
+
+  void Start();   // called by the MediaStreamListener to start preview
+  void Started(); // called by the CameraControl when preview is started
+  void StopPreview(); // called by the MediaStreamListener to stop preview
+  void Stopped(bool aForced = false);
+                  // called by the CameraControl when preview is stopped
+  void Error();   // something went wrong, NS_RELEASE needed
+
+  void SetStateStarted();
+  void SetStateStopped();
+
+protected:
+  virtual ~DOMCameraPreview();
+
+  enum {
+    STOPPED,
+    STARTING,
+    STARTED,
+    STOPPING
+  };
+  uint32_t mState;
+
+  // Helper function, used in conjunction with the macro below, to make
+  //  it easy to track state changes, which must happen only on the main
+  //  thread.
+  void
+  SetState(uint32_t aNewState, const char* aFileOrFunc, int aLine)
+  {
+#ifdef PR_LOGGING
+    const char* states[] = { "stopped", "starting", "started", "stopping" };
+    MOZ_ASSERT(mState < sizeof(states) / sizeof(states[0]));
+    MOZ_ASSERT(aNewState < sizeof(states) / sizeof(states[0]));
+    DOM_CAMERA_LOGI("SetState: (this=%p) '%s' --> '%s' : %s:%d\n", this, states[mState], states[aNewState], aFileOrFunc, aLine);
+#endif
+
+    NS_ASSERTION(NS_IsMainThread(), "Preview state set OFF OF main thread!");
+    mState = aNewState;
+  }
+
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mFramesPerSecond;
+  CameraPreviewMediaStream* mInput;
+  nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
+  VideoSegment mVideoSegment;
+  uint32_t mFrameCount;
+  nsRefPtr<ICameraControl> mCameraControl;
+
+  // Raw pointer; AddListener() keeps the reference for us
+  MediaStreamListener* mListener;
+
+private:
+  DOMCameraPreview(const DOMCameraPreview&) MOZ_DELETE;
+  DOMCameraPreview& operator=(const DOMCameraPreview&) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#define DOM_CAMERA_SETSTATE(newState)   SetState((newState), __func__, __LINE__)
+
+#endif // DOM_CAMERA_DOMCAMERAPREVIEW_H
--- a/dom/camera/FallbackCameraCapabilities.cpp
+++ b/dom/camera/FallbackCameraCapabilities.cpp
@@ -1,14 +1,15 @@
 /* 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 "nsDOMClassInfoID.h"
+#include "DOMCameraControl.h"
 #include "DOMCameraCapabilities.h"
-#include "DOMCameraControl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
 DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
 
 NS_INTERFACE_MAP_BEGIN(DOMCameraCapabilities)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -1,75 +1,216 @@
 /* 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 "DOMCameraControl.h"
 #include "CameraControlImpl.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
-  class RecorderProfileManager;
+class RecorderProfileManager;
+}
+
+/**
+ * Fallback camera control subclass.  Can be used as a template for the
+ * definition of new camera support classes.
+ */
+class nsFallbackCameraControl : public CameraControlImpl
+{
+public:
+  nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
+
+  const char* GetParameter(const char* aKey);
+  const char* GetParameterConstChar(uint32_t aKey);
+  double GetParameterDouble(uint32_t aKey);
+  int32_t GetParameterInt32(uint32_t aKey);
+  void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
+  void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
+  void SetParameter(const char* aKey, const char* aValue);
+  void SetParameter(uint32_t aKey, const char* aValue);
+  void SetParameter(uint32_t aKey, double aValue);
+  void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
+  void SetParameter(uint32_t aKey, const idl::CameraSize& aSize);
+  nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes);
+  nsresult PushParameters();
+
+protected:
+  ~nsFallbackCameraControl();
 
-  namespace layers {
-    class GraphicBufferLocked;
-  }
+  nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
+  nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
+  nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
+  nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
+  nsresult TakePictureImpl(TakePictureTask* aTakePicture);
+  nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
+  nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
+  nsresult PushParametersImpl();
+  nsresult PullParametersImpl();
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
+
+private:
+  nsFallbackCameraControl(const nsFallbackCameraControl&) MOZ_DELETE;
+  nsFallbackCameraControl& operator=(const nsFallbackCameraControl&) MOZ_DELETE;
+};
+
+/**
+ * Stub implementation of the DOM-facing camera control constructor.
+ *
+ * This should never get called--it exists to keep the linker happy; if
+ * implemented, it should construct (e.g.) nsFallbackCameraControl and
+ * store a reference in the 'mCameraControl' member (which is why it is
+ * defined here).
+ */
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow) :
+  mWindow(aWindow)
+{
+  MOZ_ASSERT(aWindow, "shouldn't be created with null window!");
+  SetIsDOMBinding();
 }
 
 /**
- * Fallback camera control subclass. Can be used as a template for the
- * definition of new camera support classes.
+ * Stub implemetations of the fallback camera control.
+ *
+ * None of these should ever get called--they exist to keep the linker happy,
+ * and may be used as templates for new camera support classes.
  */
-class FallbackCameraControl : public CameraControlImpl
+nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
+{
+}
+
+nsFallbackCameraControl::~nsFallbackCameraControl()
+{
+}
+
+const char*
+nsFallbackCameraControl::GetParameter(const char* aKey)
 {
-public:
-  FallbackCameraControl(uint32_t aCameraId) : CameraControlImpl(aCameraId) { }
+  return nullptr;
+}
+
+const char*
+nsFallbackCameraControl::GetParameterConstChar(uint32_t aKey)
+{
+  return nullptr;
+}
+
+double
+nsFallbackCameraControl::GetParameterDouble(uint32_t aKey)
+{
+  return NAN;
+}
 
-  void OnAutoFocusComplete(bool aSuccess);
-  void OnTakePictureComplete(uint8_t* aData, uint32_t aLength) { }
-  void OnTakePictureError() { }
-  void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer) { }
-  void OnRecorderEvent(int msg, int ext1, int ext2) { }
-  void OnError(CameraControlListener::CameraErrorContext aWhere,
-               CameraControlListener::CameraError aError) { }
+int32_t
+nsFallbackCameraControl::GetParameterInt32(uint32_t aKey)
+{
+  return 0;
+}
+
+void
+nsFallbackCameraControl::GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions)
+{
+}
+
+void
+nsFallbackCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize)
+{
+}
 
-  virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
+void
+nsFallbackCameraControl::SetParameter(const char* aKey, const char* aValue)
+{
+}
+
+void
+nsFallbackCameraControl::SetParameter(uint32_t aKey, const char* aValue)
+{
+}
+
+void
+nsFallbackCameraControl::SetParameter(uint32_t aKey, double aValue)
+{
+}
+
+void
+nsFallbackCameraControl::SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions)
+{
+}
 
-  virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
+void
+nsFallbackCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize)
+{
+}
 
-  virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
-  virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
+nsresult
+nsFallbackCameraControl::PushParameters()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
-  nsresult PushParameters() { return NS_ERROR_FAILURE; }
-  nsresult PullParameters() { return NS_ERROR_FAILURE; }
+nsresult
+nsFallbackCameraControl::StartPreviewImpl(StartPreviewTask* aGetPreviewStream)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
-protected:
-  ~FallbackCameraControl();
+nsresult
+nsFallbackCameraControl::StopPreviewImpl(StopPreviewTask* aGetPreviewStream)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
-  virtual nsresult StartPreviewImpl() { return NS_ERROR_FAILURE; }
-  virtual nsresult StopPreviewImpl() { return NS_ERROR_FAILURE; }
-  virtual nsresult AutoFocusImpl(bool aCancelExistingCall) { return NS_ERROR_FAILURE; }
-  virtual nsresult TakePictureImpl() { return NS_ERROR_FAILURE; }
-  virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
-                                      const StartRecordingOptions* aOptions = nullptr)
-                                        { return NS_ERROR_FAILURE; }
-  virtual nsresult StopRecordingImpl() { return NS_ERROR_FAILURE; }
-  virtual nsresult PushParametersImpl() { return NS_ERROR_FAILURE; }
-  virtual nsresult PullParametersImpl() { return NS_ERROR_FAILURE; }
-  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() { return nullptr; }
+nsresult
+nsFallbackCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
-private:
-  FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE;
-  FallbackCameraControl& operator=(const FallbackCameraControl&) MOZ_DELETE;
-};
+nsresult
+nsFallbackCameraControl::PushParametersImpl()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::PullParametersImpl()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+already_AddRefed<RecorderProfileManager> 
+nsFallbackCameraControl::GetRecorderProfileManagerImpl()
+{
+  return nullptr;
+}
--- a/dom/camera/FallbackCameraManager.cpp
+++ b/dom/camera/FallbackCameraManager.cpp
@@ -1,32 +1,28 @@
 /* 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 "ICameraControl.h"
+#include "DOMCameraManager.h"
+
+#include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
 
-// From ICameraControl.
+// From nsDOMCameraManager.
 nsresult
-ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
+nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 };
 
 nsresult
-ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
+nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-nsresult
-ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
+void
+nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
 }
-
-already_AddRefed<ICameraControl>
-ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
-{
-  return nullptr;
-}
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1,755 +1,976 @@
 /*
- * Copyright (C) 2012-2014 Mozilla Foundation
+ * Copyright (C) 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include "GonkCameraControl.h"
 #include <time.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <libgen.h>
 #include "base/basictypes.h"
 #include "camera/CameraParameters.h"
 #include "nsCOMPtr.h"
+#include "nsDOMClassInfo.h"
 #include "nsMemory.h"
 #include "nsThread.h"
 #include <media/MediaProfiles.h>
 #include "mozilla/FileUtils.h"
 #include "mozilla/Services.h"
-#include "mozilla/unused.h"
 #include "nsAlgorithm.h"
 #include <media/mediaplayer.h>
 #include "nsPrintfCString.h"
 #include "nsIObserverService.h"
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
-#include "AutoRwLock.h"
+#include "DOMCameraManager.h"
 #include "GonkCameraHwMgr.h"
+#include "DOMCameraCapabilities.h"
+#include "DOMCameraControl.h"
 #include "GonkRecorderProfiles.h"
+#include "GonkCameraControl.h"
 #include "CameraCommon.h"
-#include "GonkCameraParameters.h"
 #include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 using namespace mozilla::layers;
-using namespace mozilla::gfx;
 using namespace android;
+using mozilla::gfx::IntSize;
+
+/**
+ * See bug 783682.  Most camera implementations, despite claiming they
+ * support 'yuv420p' as a preview format, actually ignore this setting
+ * and return 'yuv420sp' data anyway.  We have come across a new implementation
+ * that, while reporting that 'yuv420p' is supported *and* has been accepted,
+ * still returns the frame data in 'yuv420sp' anyway.  So for now, since
+ * everyone seems to return this format, we just force it.
+ */
+#define FORCE_PREVIEW_FORMAT_YUV420SP   1
 
 #define RETURN_IF_NO_CAMERA_HW()                                          \
   do {                                                                    \
     if (!mCameraHw.get()) {                                               \
       DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
       return NS_ERROR_NOT_AVAILABLE;                                      \
     }                                                                     \
   } while(0)
 
+static const char* getKeyText(uint32_t aKey)
+{
+  switch (aKey) {
+    case CAMERA_PARAM_EFFECT:
+      return CameraParameters::KEY_EFFECT;
+    case CAMERA_PARAM_WHITEBALANCE:
+      return CameraParameters::KEY_WHITE_BALANCE;
+    case CAMERA_PARAM_SCENEMODE:
+      return CameraParameters::KEY_SCENE_MODE;
+    case CAMERA_PARAM_FLASHMODE:
+      return CameraParameters::KEY_FLASH_MODE;
+    case CAMERA_PARAM_FOCUSMODE:
+      return CameraParameters::KEY_FOCUS_MODE;
+    case CAMERA_PARAM_ZOOM:
+      return CameraParameters::KEY_ZOOM;
+    case CAMERA_PARAM_METERINGAREAS:
+      return CameraParameters::KEY_METERING_AREAS;
+    case CAMERA_PARAM_FOCUSAREAS:
+      return CameraParameters::KEY_FOCUS_AREAS;
+    case CAMERA_PARAM_FOCALLENGTH:
+      return CameraParameters::KEY_FOCAL_LENGTH;
+    case CAMERA_PARAM_FOCUSDISTANCENEAR:
+      return CameraParameters::KEY_FOCUS_DISTANCES;
+    case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
+      return CameraParameters::KEY_FOCUS_DISTANCES;
+    case CAMERA_PARAM_FOCUSDISTANCEFAR:
+      return CameraParameters::KEY_FOCUS_DISTANCES;
+    case CAMERA_PARAM_EXPOSURECOMPENSATION:
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION;
+    case CAMERA_PARAM_PICTURESIZE:
+      return CameraParameters::KEY_PICTURE_SIZE;
+    case CAMERA_PARAM_THUMBNAILQUALITY:
+      return CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY;
+
+    case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
+      return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES;
+    case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
+      return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES;
+    case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
+      return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES;
+    case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
+      return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS;
+    case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
+      return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE;
+    case CAMERA_PARAM_SUPPORTED_SCENEMODES:
+      return CameraParameters::KEY_SUPPORTED_SCENE_MODES;
+    case CAMERA_PARAM_SUPPORTED_EFFECTS:
+      return CameraParameters::KEY_SUPPORTED_EFFECTS;
+    case CAMERA_PARAM_SUPPORTED_FLASHMODES:
+      return CameraParameters::KEY_SUPPORTED_FLASH_MODES;
+    case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
+      return CameraParameters::KEY_SUPPORTED_FOCUS_MODES;
+    case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
+      return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS;
+    case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
+      return CameraParameters::KEY_MAX_NUM_METERING_AREAS;
+    case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
+      return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION;
+    case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
+      return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION;
+    case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP;
+    case CAMERA_PARAM_SUPPORTED_ZOOM:
+      return CameraParameters::KEY_ZOOM_SUPPORTED;
+    case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
+      return CameraParameters::KEY_ZOOM_RATIOS;
+    case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
+      return CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
+    default:
+      return nullptr;
+  }
+}
+
+// nsDOMCameraControl implementation-specific constructor
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow)
+  : mDOMCapabilities(nullptr), mWindow(aWindow)
+{
+  MOZ_ASSERT(aWindow, "shouldn't be created with null window!");
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  SetIsDOMBinding();
+
+  /**
+   * nsDOMCameraControl is a cycle-collection participant, which means it is
+   * not threadsafe--so we need to bump up its reference count here to make
+   * sure that it exists long enough to be initialized.
+   *
+   * Once it is initialized, the GetCameraResult main-thread runnable will
+   * decrement it again to make sure it can be cleaned up.
+   *
+   * nsGonkCameraControl MUST NOT hold a strong reference to this
+   * nsDOMCameraControl or memory will leak!
+   */
+  NS_ADDREF_THIS();
+  nsRefPtr<nsGonkCameraControl> control = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError, aWindow->WindowID());
+  control->DispatchInit(this, onSuccess, onError, aWindow->WindowID());
+  mCameraControl = control;
+}
+
+// Gonk-specific CameraControl implementation.
+
+// Initialize nsGonkCameraControl instance--runs on camera thread.
+class InitGonkCameraControl : public nsRunnable
+{
+public:
+  InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+    : mCameraControl(aCameraControl)
+    , mDOMCameraControl(aDOMCameraControl)
+    , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraGetCameraCallback>(onSuccess))
+    , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  ~InitGonkCameraControl()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsresult rv = mCameraControl->Init();
+    return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb, mWindowId);
+  }
+
+  nsRefPtr<nsGonkCameraControl> mCameraControl;
+  // Raw pointer to DOM-facing camera control--it must NS_ADDREF itself for us
+  nsDOMCameraControl* mDOMCameraControl;
+  nsMainThreadPtrHandle<nsICameraGetCameraCallback> mOnSuccessCb;
+  nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
+};
+
 // Construct nsGonkCameraControl on the main thread.
-nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
-  : CameraControlImpl(aCameraId)
-  , mLastPictureSize({0, 0})
-  , mLastThumbnailSize({0, 0})
-  , mPreviewFps(30)
-  , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
-  , mDeferConfigUpdate(0)
+nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
+  , mExposureCompensationMin(0.0)
+  , mExposureCompensationStep(0.0)
+  , mDeferConfigUpdate(false)
+  , mWidth(0)
+  , mHeight(0)
+  , mLastPictureWidth(0)
+  , mLastPictureHeight(0)
+  , mLastThumbnailWidth(0)
+  , mLastThumbnailHeight(0)
+#if !FORCE_PREVIEW_FORMAT_YUV420SP
+  , mFormat(PREVIEW_FORMAT_UNKNOWN)
+#else
+  , mFormat(PREVIEW_FORMAT_YUV420SP)
+#endif
+  , mFps(30)
+  , mDiscardedFrameCount(0)
   , mMediaProfiles(nullptr)
   , mRecorder(nullptr)
   , mProfileManager(nullptr)
   , mRecorderProfile(nullptr)
   , mVideoFile(nullptr)
-  , mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor")
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  mImageContainer = LayerManager::CreateImageContainer();
+  mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
+}
+
+void nsGonkCameraControl::DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+{
+  // ...but initialization is carried out on the camera thread.
+  nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError, aWindowId);
+  mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-nsGonkCameraControl::Init(const Configuration* aInitialConfig)
+nsGonkCameraControl::Init()
 {
-  class InitGonkCameraControl : public nsRunnable
-  {
-  public:
-    InitGonkCameraControl(nsGonkCameraControl* aCameraControl,
-                          const Configuration* aConfig)
-      : mCameraControl(aCameraControl)
-      , mHaveInitialConfig(false)
-    {
-      DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-      if (aConfig) {
-        mConfig = *aConfig;
-        mHaveInitialConfig = true;
-      }
-    }
-
-    ~InitGonkCameraControl()
-    {
-      DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-    }
-
-    /**
-     * For initialization, we try to return the camera control to the upper
-     * upper layer (i.e. the DOM) as quickly as possible. To do this, the
-     * camera is initialized in the following stages:
-     *
-     *  0. InitImpl() initializes the hardware;
-     *  1. SetConfigurationInternal() does the minimal configuration
-     *     required so that we can start the preview -and- report a valid
-     *     configuration to the upper layer;
-     *  2. OnHardwareStateChange() reports that the hardware is ready,
-     *     which the upper layer can (and does) use to return the camera
-     *     control object;
-     *  3. StartPreviewImpl() starts the flow of preview frames from the
-     *     camera hardware.
-     *
-     * The intent of the above flow is to let the Main Thread do as much work
-     * up-front as possible without waiting for blocking Camera Thread calls
-     * to complete.
-     */
-    NS_IMETHODIMP
-    Run() MOZ_OVERRIDE
-    {
-      nsresult rv = mCameraControl->InitImpl();
-      if (NS_FAILED(rv)) {
-        mCameraControl->OnError(CameraControlListener::kInGetCamera,
-                                CameraControlListener::kErrorInitFailed);
-        // The hardware failed to initialize, so close it up
-        mCameraControl->ReleaseHardware();
-        return rv;
-      }
-
-      if (mHaveInitialConfig) {
-        rv = mCameraControl->SetConfigurationInternal(mConfig);
-        if (NS_FAILED(rv)) {
-          mCameraControl->OnError(CameraControlListener::kInGetCamera,
-                                  CameraControlListener::kErrorInvalidConfiguration);
-          // The initial configuration failed, close up the hardware
-          mCameraControl->ReleaseHardware();
-          return rv;
-        }
-      }
-
-      mCameraControl->OnHardwareStateChange(CameraControlListener::kHardwareOpen);
-      return mCameraControl->StartPreviewImpl();
-    }
-
-  protected:
-    nsRefPtr<nsGonkCameraControl> mCameraControl;
-    Configuration mConfig;
-    bool mHaveInitialConfig;
-  };
-
-  // Initialization is carried out on the camera thread.
-  return mCameraThread->Dispatch(
-    new InitGonkCameraControl(this, aInitialConfig), NS_DISPATCH_NORMAL);
-}
-
-nsresult
-nsGonkCameraControl::InitImpl()
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
   mCameraHw = GonkCameraHardware::Connect(this, mCameraId);
   if (!mCameraHw.get()) {
     DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this);
     return NS_ERROR_FAILURE;
   }
 
   DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get());
 
   // Initialize our camera configuration database.
   PullParametersImpl();
 
-  // Set preferred preview frame format.
-  mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp"));
+  // Try to set preferred image format and frame rate
+#if !FORCE_PREVIEW_FORMAT_YUV420SP
+  DOM_CAMERA_LOGI("Camera preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS));
+  const char* const PREVIEW_FORMAT = "yuv420p";
+  const char* const BAD_PREVIEW_FORMAT = "yuv420sp";
+  mParams.setPreviewFormat(PREVIEW_FORMAT);
+  mParams.setPreviewFrameRate(mFps);
+#else
+  mParams.setPreviewFormat("yuv420sp");
+  mParams.setPreviewFrameRate(mFps);
+#endif
   PushParametersImpl();
 
+  // Check that our settings stuck
+  PullParametersImpl();
+#if !FORCE_PREVIEW_FORMAT_YUV420SP
+  const char* format = mParams.getPreviewFormat();
+  if (strcmp(format, PREVIEW_FORMAT) == 0) {
+    mFormat = PREVIEW_FORMAT_YUV420P;  /* \o/ */
+  } else if (strcmp(format, BAD_PREVIEW_FORMAT) == 0) {
+    mFormat = PREVIEW_FORMAT_YUV420SP;
+    DOM_CAMERA_LOGA("Camera ignored our request for '%s' preview, will have to convert (from %d)\n", PREVIEW_FORMAT, mFormat);
+  } else {
+    mFormat = PREVIEW_FORMAT_UNKNOWN;
+    DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT, format);
+  }
+#endif
+
+  // Check the frame rate and log if the camera ignored our setting
+  uint32_t fps = mParams.getPreviewFrameRate();
+  if (fps != mFps) {
+    DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using that", mFps, fps);
+    mFps = fps;
+  }
+
   // Grab any other settings we'll need later.
-  mParams.Get(CAMERA_PARAM_PICTURE_FILEFORMAT, mFileFormat);
-  mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize);
-
-  // The emulator's camera returns -1 for these values; bump them up to 0
-  int areas;
-  mParams.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas);
-  mCurrentConfiguration.mMaxMeteringAreas = areas != -1 ? areas : 0;
-  mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
-  mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0;
+  mExposureCompensationMin = mParams.getFloat(mParams.KEY_MIN_EXPOSURE_COMPENSATION);
+  mExposureCompensationStep = mParams.getFloat(mParams.KEY_EXPOSURE_COMPENSATION_STEP);
+  mMaxMeteringAreas = mParams.getInt(mParams.KEY_MAX_NUM_METERING_AREAS);
+  mMaxFocusAreas = mParams.getInt(mParams.KEY_MAX_NUM_FOCUS_AREAS);
+  mLastThumbnailWidth = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_WIDTH);
+  mLastThumbnailHeight = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_HEIGHT);
 
-  mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize);
-  mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize);
-  mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize);
+  int w;
+  int h;
+  mParams.getPictureSize(&w, &h);
+  MOZ_ASSERT(w > 0 && h > 0); // make sure the driver returns sane values
+  mLastPictureWidth = static_cast<uint32_t>(w);
+  mLastPictureHeight = static_cast<uint32_t>(h);
 
-  DOM_CAMERA_LOGI(" - maximum metering areas:        %u\n", mCurrentConfiguration.mMaxMeteringAreas);
-  DOM_CAMERA_LOGI(" - maximum focus areas:           %u\n", mCurrentConfiguration.mMaxFocusAreas);
-  DOM_CAMERA_LOGI(" - default picture size:          %u x %u\n",
-    mLastPictureSize.width, mLastPictureSize.height);
-  DOM_CAMERA_LOGI(" - default thumbnail size:        %u x %u\n",
-    mLastThumbnailSize.width, mLastThumbnailSize.height);
-  DOM_CAMERA_LOGI(" - default preview size:          %u x %u\n",
-    mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height);
-  DOM_CAMERA_LOGI(" - default video recorder size:   %u x %u\n",
-    mLastRecorderSize.width, mLastRecorderSize.height);
-  DOM_CAMERA_LOGI(" - default picture file format:   %s\n",
-    NS_ConvertUTF16toUTF8(mFileFormat).get());
+  DOM_CAMERA_LOGI(" - minimum exposure compensation: %f\n", mExposureCompensationMin);
+  DOM_CAMERA_LOGI(" - exposure compensation step:    %f\n", mExposureCompensationStep);
+  DOM_CAMERA_LOGI(" - maximum metering areas:        %d\n", mMaxMeteringAreas);
+  DOM_CAMERA_LOGI(" - maximum focus areas:           %d\n", mMaxFocusAreas);
+  DOM_CAMERA_LOGI(" - default picture size:          %u x %u\n", mLastPictureWidth, mLastPictureHeight);
+  DOM_CAMERA_LOGI(" - default thumbnail size:        %u x %u\n", mLastThumbnailWidth, mLastThumbnailHeight);
 
   return NS_OK;
 }
 
 nsGonkCameraControl::~nsGonkCameraControl()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get());
 
-  ReleaseHardwareImpl();
+  ReleaseHardwareImpl(nullptr);
+  if (mRwLock) {
+    PRRWLock* lock = mRwLock;
+    mRwLock = nullptr;
+    PR_DestroyRWLock(lock);
+  }
+
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 }
 
-nsresult
-nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
+class RwAutoLockRead
+{
+public:
+  RwAutoLockRead(PRRWLock* aRwLock)
+    : mRwLock(aRwLock)
+  {
+    PR_RWLock_Rlock(mRwLock);
+  }
+
+  ~RwAutoLockRead()
+  {
+    PR_RWLock_Unlock(mRwLock);
+  }
+
+protected:
+  PRRWLock* mRwLock;
+};
+
+class RwAutoLockWrite
 {
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+public:
+  RwAutoLockWrite(PRRWLock* aRwLock)
+    : mRwLock(aRwLock)
+  {
+    PR_RWLock_Wlock(mRwLock);
+  }
+
+  ~RwAutoLockWrite()
+  {
+    PR_RWLock_Unlock(mRwLock);
+  }
 
-  nsresult rv;
+protected:
+  PRRWLock* mRwLock;
+};
+
+const char*
+nsGonkCameraControl::GetParameter(const char* aKey)
+{
+  RwAutoLockRead lock(mRwLock);
+  return mParams.get(aKey);
+}
+
+const char*
+nsGonkCameraControl::GetParameterConstChar(uint32_t aKey)
+{
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return nullptr;
+  }
 
-  switch (aConfig.mMode) {
-    case kPictureMode:
-      rv = SetPictureConfiguration(aConfig);
-      break;
+  RwAutoLockRead lock(mRwLock);
+  return mParams.get(key);
+}
+
+double
+nsGonkCameraControl::GetParameterDouble(uint32_t aKey)
+{
+  double val;
+  int index = 0;
+  double focusDistance[3];
+  const char* s;
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    // return 1x when zooming is not supported
+    return aKey == CAMERA_PARAM_ZOOM ? 1.0 : 0.0;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+  switch (aKey) {
+    case CAMERA_PARAM_ZOOM:
+      val = mParams.getInt(key);
+      return val / 100;
 
-    case kVideoMode:
-      rv = SetVideoConfiguration(aConfig);
-      break;
+    /**
+     * The gonk camera parameters API only exposes one focus distance property
+     * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may
+     * be 'Infinity'.
+     */
+    case CAMERA_PARAM_FOCUSDISTANCEFAR:
+      ++index;
+      // intentional fallthrough
+
+    case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
+      ++index;
+      // intentional fallthrough
+
+    case CAMERA_PARAM_FOCUSDISTANCENEAR:
+      s = mParams.get(key);
+      if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) {
+        return focusDistance[index];
+      }
+      return 0.0;
+
+    case CAMERA_PARAM_EXPOSURECOMPENSATION:
+      index = mParams.getInt(key);
+      if (!index) {
+        // NaN indicates automatic exposure compensation
+        return NAN;
+      }
+      val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin;
+      DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val);
+      return val;
 
     default:
-      MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
+      return mParams.getFloat(key);
+  }
+}
+
+int32_t
+nsGonkCameraControl::GetParameterInt32(uint32_t aKey)
+{
+  if (aKey == CAMERA_PARAM_SENSORANGLE) {
+    if (!mCameraHw.get()) {
+      return 0;
+    }
+    return mCameraHw->GetSensorOrientation();
+  }
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return 0;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+  return mParams.getInt(key);
+}
+
+void
+nsGonkCameraControl::GetParameter(uint32_t aKey,
+                                  nsTArray<idl::CameraRegion>& aRegions)
+{
+  aRegions.Clear();
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+
+  const char* value = mParams.get(key);
+  DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
+  if (!value) {
+    return;
+  }
+
+  const char* p = value;
+  uint32_t count = 1;
+
+  // count the number of regions in the string
+  while ((p = strstr(p, "),("))) {
+    ++count;
+    p += 3;
+  }
+
+  aRegions.SetCapacity(count);
+  idl::CameraRegion* r;
+
+  // parse all of the region sets
+  uint32_t i;
+  for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) {
+    r = aRegions.AppendElement();
+    if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) {
+      DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p);
+      aRegions.Clear();
+      return;
+    }
   }
 
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-  NS_ENSURE_SUCCESS(rv, rv);
+  return;
+}
 
-  mCurrentConfiguration.mMode = aConfig.mMode;
-  mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
-  if (aConfig.mMode == kVideoMode) {
-    mCurrentConfiguration.mPreviewSize = mLastRecorderSize;
+void
+nsGonkCameraControl::GetParameter(uint32_t aKey,
+                                  nsTArray<idl::CameraSize>& aSizes)
+{
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+
+  const char* value = mParams.get(key);
+  DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
+  if (!value) {
+    return;
   }
 
-  OnConfigurationChange();
-  return NS_OK;
+  const char* p = value;
+  idl::CameraSize* s;
+
+  // The 'value' string is in the format "w1xh1,w2xh2,w3xh3,..."
+  while (p) {
+    s = aSizes.AppendElement();
+    if (sscanf(p, "%dx%d", &s->width, &s->height) != 2) {
+      DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, p);
+      aSizes.Clear();
+      return;
+    }
+    // Look for the next record...
+    p = strchr(p, ',');
+    if (p) {
+      // ...skip the comma too
+      ++p;
+    }
+  }
+
+  return;
+}
+
+void
+nsGonkCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize)
+{
+  if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
+    // This is a special case--for some reason the thumbnail size
+    // is accessed as two separate values instead of a tuple.
+    RwAutoLockRead lock(mRwLock);
+
+    aSize.width = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
+    aSize.height = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
+    DOM_CAMERA_LOGI("thumbnail size --> value='%ux%u'\n", aSize.width, aSize.height);
+    return;
+  }
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+
+  const char* value = mParams.get(key);
+  DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
+  if (!value) {
+    return;
+  }
+
+  if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) {
+    DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, value);
+    aSize.width = 0;
+    aSize.height = 0;
+  }
 }
 
 nsresult
-nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
-{
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
-  // Stop any currently running preview
-  StopPreviewImpl();
-
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-  nsresult rv = SetConfigurationInternal(aConfig);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Restart the preview
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-  return StartPreviewImpl();
-}
-
-nsresult
-nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
-{
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-
-  // remove any existing recorder profile
-  mRecorderProfile = nullptr;
-
-  nsresult rv = SetPreviewSize(aConfig.mPreviewSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
-
-  DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
-    aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
-    mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height,
-    mPreviewFps);
-
-  return NS_OK;
-}
-
-nsresult
-nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
-{
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-
-  nsresult rv = SetupVideoMode(aConfig.mRecorderProfile);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  DOM_CAMERA_LOGI("video mode preview: profile '%s', got %ux%u (%u fps)\n",
-    NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get(),
-    mLastRecorderSize.width, mLastRecorderSize.height,
-    mPreviewFps);
-
-  return rv;
-}
-
-// Parameter management.
-nsresult
 nsGonkCameraControl::PushParameters()
 {
-  uint32_t dcu = mDeferConfigUpdate;
-  if (dcu > 0) {
-    DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu);
+  if (mDeferConfigUpdate) {
+    DOM_CAMERA_LOGT("%s:%d - defering config update\n", __func__, __LINE__);
     return NS_OK;
   }
 
   /**
    * If we're already on the camera thread, call PushParametersImpl()
    * directly, so that it executes synchronously.  Some callers
    * require this so that changes take effect immediately before
    * we can proceed.
    */
-  if (NS_GetCurrentThread() != mCameraThread) {
-    DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__, __LINE__);
-    nsCOMPtr<nsIRunnable> pushParametersTask =
-      NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl);
+  if (NS_IsMainThread()) {
+    DOM_CAMERA_LOGT("%s:%d - dispatching to camera thread\n", __func__, __LINE__);
+    nsCOMPtr<nsIRunnable> pushParametersTask = NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl);
     return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL);
   }
 
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   return PushParametersImpl();
 }
 
 void
-nsGonkCameraControl::BeginBatchParameterSet()
+nsGonkCameraControl::SetParameter(const char* aKey, const char* aValue)
+{
+  {
+    RwAutoLockWrite lock(mRwLock);
+    mParams.set(aKey, aValue);
+  }
+  PushParameters();
+}
+
+void
+nsGonkCameraControl::SetParameter(uint32_t aKey, const char* aValue)
 {
-  uint32_t dcu = ++mDeferConfigUpdate;
-  if (dcu == 0) {
-    NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!");
-    MOZ_CRASH();
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
+  }
+
+  {
+    RwAutoLockWrite lock(mRwLock);
+    mParams.set(key, aValue);
   }
-  DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu);
+  PushParameters();
+}
+
+void
+nsGonkCameraControl::SetParameter(uint32_t aKey, double aValue)
+{
+  uint32_t index;
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
+  }
+
+  {
+    RwAutoLockWrite lock(mRwLock);
+    if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) {
+      /**
+       * Convert from real value to a Gonk index, round
+       * to the nearest step; index is 1-based.
+       */
+      index = (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / mExposureCompensationStep + 1;
+      DOM_CAMERA_LOGI("compensation = %f --> index = %d\n", aValue, index);
+      mParams.set(key, index);
+    } else {
+      mParams.setFloat(key, aValue);
+    }
+  }
+  PushParameters();
 }
 
 void
-nsGonkCameraControl::EndBatchParameterSet()
-{
-  uint32_t dcu = mDeferConfigUpdate--;
-  if (dcu == 0) {
-    NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!");
-    MOZ_CRASH();
-  }
-  DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu);
-
-  if (dcu == 1) {
-    PushParameters();
-  }
-}
-
-template<class T> nsresult
-nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue)
+nsGonkCameraControl::SetParameter(uint32_t aKey,
+                                  const nsTArray<idl::CameraRegion>& aRegions)
 {
-  nsresult rv = mParams.Set(aKey, aValue);
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv);
-    return rv;
-  }
-  return PushParameters();
-}
-
-// Array-of-Size parameter accessor.
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Size>& aSizes)
-{
-  if (aKey == CAMERA_PARAM_SUPPORTED_VIDEOSIZES) {
-    nsresult rv = mParams.Get(aKey, aSizes);
-    if (aSizes.Length() != 0) {
-      return rv;
-    }
-    DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n");
-    aKey = CAMERA_PARAM_SUPPORTED_PREVIEWSIZES;
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
   }
 
-  return mParams.Get(aKey, aSizes);
-}
+  uint32_t length = aRegions.Length();
+
+  if (!length) {
+    // This tells the camera driver to revert to automatic regioning.
+    {
+      RwAutoLockWrite lock(mRwLock);
+      mParams.set(key, "(0,0,0,0,0)");
+    }
+    PushParameters();
+    return;
+  }
+
+  nsCString s;
 
-// Array-of-doubles parameter accessor.
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, nsTArray<double>& aValues)
-{
-  return mParams.Get(aKey, aValues);
-}
+  for (uint32_t i = 0; i < length; ++i) {
+    const idl::CameraRegion* r = &aRegions[i];
+    s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight);
+  }
+
+  // remove the trailing comma
+  s.Trim(",", false, true, true);
 
-// Array-of-nsString parameter accessor.
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, nsTArray<nsString>& aValues)
-{
-  return mParams.Get(aKey, aValues);
+  DOM_CAMERA_LOGI("camera region string '%s'\n", s.get());
+
+  {
+    RwAutoLockWrite lock(mRwLock);
+    mParams.set(key, s.get());
+  }
+  PushParameters();
 }
 
-// nsString-valued parameter accessors
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue)
+void
+nsGonkCameraControl::SetParameter(uint32_t aKey, int aValue)
 {
-  nsresult rv = mParams.Set(aKey, aValue);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (aKey == CAMERA_PARAM_PICTURE_FILEFORMAT) {
-    // Picture format -- need to keep it for the TakePicture() callback.
-    mFileFormat = aValue;
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return;
   }
-
-  return PushParameters();
-}
-
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet)
-{
-  return mParams.Get(aKey, aRet);
-}
-
-// Double-valued parameter accessors
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, double aValue)
-{
-  return SetAndPush(aKey, aValue);
+  {
+    RwAutoLockWrite lock(mRwLock);
+    mParams.set(key, aValue);
+  }
+  PushParameters();
 }
 
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, double& aRet)
-{
-  return mParams.Get(aKey, aRet);
-}
-
-// Signed-64-bit parameter accessors.
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue)
-{
-  return SetAndPush(aKey, aValue);
-}
-
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet)
-{
-  return mParams.Get(aKey, aRet);
-}
-
-// Weighted-region parameter accessors.
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, const nsTArray<Region>& aRegions)
-{
-  return SetAndPush(aKey, aRegions);
-}
-
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Region>& aRegions)
-{
-  return mParams.Get(aKey, aRegions);
-}
-
-// Singleton-size parameter accessors.
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize)
+void
+nsGonkCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize)
 {
   switch (aKey) {
     case CAMERA_PARAM_PICTURESIZE:
       DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height);
-      return SetPictureSize(aSize);
+      SetPictureSize(aSize.width, aSize.height);
+      break;
 
     case CAMERA_PARAM_THUMBNAILSIZE:
       DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height);
-      return SetThumbnailSize(aSize);
+      SetThumbnailSize(aSize.width, aSize.height);
+      break;
 
     default:
-      return SetAndPush(aKey, aSize);
-  }
-}
+      {
+        const char* key = getKeyText(aKey);
+        if (!key) {
+          return;
+        }
 
-nsresult
-nsGonkCameraControl::Get(uint32_t aKey, Size& aSize)
-{
-  return mParams.Get(aKey, aSize);
-}
+        nsCString s;
+        s.AppendPrintf("%ux%u", aSize.width, aSize.height);
+        DOM_CAMERA_LOGI("setting '%s' to %s\n", key, s.get());
 
-// Signed int parameter accessors.
-nsresult
-nsGonkCameraControl::Set(uint32_t aKey, int aValue)
-{
-  if (aKey == CAMERA_PARAM_PICTURE_ROTATION) {
-    aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation());
+        RwAutoLockWrite lock(mRwLock);
+        mParams.set(key, s.get());
+      }
+      break;
   }
-  return SetAndPush(aKey, aValue);
+  PushParameters();
 }
 
 nsresult
-nsGonkCameraControl::Get(uint32_t aKey, int& aRet)
+nsGonkCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
 {
-  if (aKey == CAMERA_PARAM_SENSORANGLE) {
-    if (!mCameraHw.get()) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-    aRet = mCameraHw->GetSensorOrientation();
-    return NS_OK;
-  }
+  // stop any currently running preview
+  StopPreviewInternal(true /* forced */);
+
+  // remove any existing recorder profile
+  mRecorderProfile = nullptr;
 
-  return mParams.Get(aKey, aRet);
-}
+  SetPreviewSize(aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height);
+  DOM_CAMERA_LOGI("picture preview: wanted %d x %d, got %d x %d (%d fps, format %d)\n", aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height, mWidth, mHeight, mFps, mFormat);
 
-// GPS location parameter accessors.
-nsresult
-nsGonkCameraControl::SetLocation(const Position& aLocation)
-{
-  return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation);
+  nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> onSuccess = aGetPreviewStream->mOnSuccessCb;
+  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, onSuccess, mWindowId);
+  return NS_DispatchToMainThread(getPreviewStreamResult);
 }
 
 nsresult
-nsGonkCameraControl::StartPreviewImpl()
+nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview)
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-  RETURN_IF_NO_CAMERA_HW();
-
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-  if (mPreviewState == CameraControlListener::kPreviewStarted) {
-    DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n");
-    return NS_OK;
+  /**
+   * If 'aStartPreview->mDOMPreview' is null, we are just restarting
+   * the preview after taking a picture.  No need to monkey with the
+   * currently set DOM-facing preview object.
+   */
+  if (aStartPreview->mDOMPreview) {
+    StopPreviewInternal(true /* forced */);
+    mDOMPreview = aStartPreview->mDOMPreview;
+  } else if (!mDOMPreview) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this);
+  DOM_CAMERA_LOGI("%s: starting preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
 
+  RETURN_IF_NO_CAMERA_HW();
   if (mCameraHw->StartPreview() != OK) {
-    DOM_CAMERA_LOGE("Failed to start camera preview\n");
+    DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
     return NS_ERROR_FAILURE;
   }
 
-  OnPreviewStateChange(CameraControlListener::kPreviewStarted);
+  if (aStartPreview->mDOMPreview) {
+    mDOMPreview->Started();
+  }
+
+  OnPreviewStateChange(PREVIEW_STARTED);
   return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::StopPreviewImpl()
+nsGonkCameraControl::StopPreviewInternal(bool aForced)
 {
-  RETURN_IF_NO_CAMERA_HW();
+  DOM_CAMERA_LOGI("%s: stopping preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
 
-  DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this);
+  // StopPreview() is a synchronous call--it doesn't return
+  // until the camera preview thread exits.
+  if (mDOMPreview) {
+    if (mCameraHw.get()) {
+      mCameraHw->StopPreview();
+    }
+    mDOMPreview->Stopped(aForced);
+    mDOMPreview = nullptr;
+  }
 
-  mCameraHw->StopPreview();
-
-  OnPreviewStateChange(CameraControlListener::kPreviewStopped);
+  OnPreviewStateChange(PREVIEW_STOPPED);
   return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::AutoFocusImpl(bool aCancelExistingCall)
+nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-  RETURN_IF_NO_CAMERA_HW();
-  if (aCancelExistingCall) {
+  return StopPreviewInternal();
+}
+
+nsresult
+nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
+{
+  if (aAutoFocus->mCancel) {
     if (mCameraHw.get()) {
       mCameraHw->CancelAutoFocus();
     }
   }
 
+  mAutoFocusOnSuccessCb = aAutoFocus->mOnSuccessCb;
+  mAutoFocusOnErrorCb = aAutoFocus->mOnErrorCb;
+
+  RETURN_IF_NO_CAMERA_HW();
   if (mCameraHw->AutoFocus() != OK) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-nsresult
-nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize)
+void
+nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight)
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
   /**
    * We keep a copy of the specified size so that if the picture size
    * changes, we can choose a new thumbnail size close to what was asked for
    * last time.
    */
-  mLastThumbnailSize = aSize;
+  mLastThumbnailWidth = aWidth;
+  mLastThumbnailHeight = aHeight;
 
   /**
    * If either of width or height is zero, set the other to zero as well.
    * This should disable inclusion of a thumbnail in the final picture.
    */
-  if (!aSize.width || !aSize.height) {
-    DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n",
-      aSize.width, aSize.height);
-    Size size = { 0, 0 };
-    return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
+  if (!aWidth || !aHeight) {
+    DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", aWidth, aHeight);
+    RwAutoLockWrite write(mRwLock);
+    mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0);
+    mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0);
+    return;
   }
 
   /**
    * Choose the supported thumbnail size that is closest to the specified size.
    * Some drivers will fail to take a picture if the thumbnail does not have
    * the same aspect ratio as the set picture size, so we need to enforce that
    * too.
    */
   int smallestDelta = INT_MAX;
   uint32_t smallestDeltaIndex = UINT32_MAX;
-  int targetArea = aSize.width * aSize.height;
+  int targetArea = aWidth * aHeight;
 
-  nsAutoTArray<Size, 8> supportedSizes;
-  Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes);
+  nsAutoTArray<idl::CameraSize, 8> supportedSizes;
+  GetParameter(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes);
 
   for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
     int area = supportedSizes[i].width * supportedSizes[i].height;
     int delta = abs(area - targetArea);
 
     if (area != 0
       && delta < smallestDelta
-      && supportedSizes[i].width * mLastPictureSize.height /
-         supportedSizes[i].height == mLastPictureSize.width
+      && supportedSizes[i].width * mLastPictureHeight / supportedSizes[i].height == mLastPictureWidth
     ) {
       smallestDelta = delta;
       smallestDeltaIndex = i;
     }
   }
 
   if (smallestDeltaIndex == UINT32_MAX) {
-    DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n",
-      aSize.width, aSize.height);
-    return NS_ERROR_INVALID_ARG;
+    DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n", aWidth, aHeight);
+    return;
   }
 
-  Size size = supportedSizes[smallestDeltaIndex];
-  DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
-    size.width, size.height, aSize.width, aSize.height);
-  if (size.width > INT32_MAX || size.height > INT32_MAX) {
+  uint32_t w = supportedSizes[smallestDeltaIndex].width;
+  uint32_t h = supportedSizes[smallestDeltaIndex].height;
+  DOM_CAMERA_LOGI("Requested thumbnail size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h);
+  if (w > INT32_MAX || h > INT32_MAX) {
     DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
-    return NS_ERROR_FAILURE;
+    return;
   }
 
-  return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
+  RwAutoLockWrite write(mRwLock);
+  mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(w));
+  mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(h));
 }
 
-nsresult
-nsGonkCameraControl::SetThumbnailSize(const Size& aSize)
+void
+nsGonkCameraControl::UpdateThumbnailSize()
 {
-  class SetThumbnailSize : public nsRunnable
-  {
-  public:
-    SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
-      : mCameraControl(aCameraControl)
-      , mSize(aSize)
-    {
-      MOZ_COUNT_CTOR(SetThumbnailSize);
-    }
-    ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); }
-
-    NS_IMETHODIMP
-    Run() MOZ_OVERRIDE
-    {
-      nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize);
-      if (NS_FAILED(rv)) {
-        mCameraControl->OnError(CameraControlListener::kInUnspecified,
-                                CameraControlListener::kErrorSetThumbnailSizeFailed);
-      }
-      return NS_OK;
-    }
-
-  protected:
-    nsRefPtr<nsGonkCameraControl> mCameraControl;
-    Size mSize;
-  };
-
-  if (NS_GetCurrentThread() == mCameraThread) {
-    return SetThumbnailSizeImpl(aSize);
-  }
-
-  return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL);
+  SetThumbnailSize(mLastThumbnailWidth, mLastThumbnailHeight);
 }
 
-nsresult
-nsGonkCameraControl::UpdateThumbnailSize()
+void
+nsGonkCameraControl::SetPictureSize(uint32_t aWidth, uint32_t aHeight)
 {
-  return SetThumbnailSize(mLastThumbnailSize);
-}
-
-nsresult
-nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
   /**
    * Some drivers are less friendly about getting one of these set to zero,
    * so if either is not specified, ignore both and go with current or
    * default settings.
    */
-  if (!aSize.width || !aSize.height) {
-    DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize.width, aSize.height);
-    return NS_ERROR_INVALID_ARG;
+  if (!aWidth || !aHeight) {
+    DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aWidth, aHeight);
+    return;
   }
 
-  if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) {
-    DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height);
-    return NS_OK;
+  if (aWidth == mLastPictureWidth && aHeight == mLastPictureHeight) {
+    DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aWidth, aHeight);
+    return;
   }
 
   /**
    * Choose the supported picture size that is closest in area to the
    * specified size. Some drivers will fail to take a picture if the
    * thumbnail size is not the same aspect ratio, so we update that
    * as well to a size closest to the last user-requested one.
    */
   int smallestDelta = INT_MAX;
   uint32_t smallestDeltaIndex = UINT32_MAX;
-  int targetArea = aSize.width * aSize.height;
-
-  nsAutoTArray<Size, 8> supportedSizes;
-  Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
+  int targetArea = aWidth * aHeight;
+  
+  nsAutoTArray<idl::CameraSize, 8> supportedSizes;
+  GetParameter(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
 
   for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
     int area = supportedSizes[i].width * supportedSizes[i].height;
     int delta = abs(area - targetArea);
 
     if (area != 0 && delta < smallestDelta) {
       smallestDelta = delta;
       smallestDeltaIndex = i;
     }
   }
 
   if (smallestDeltaIndex == UINT32_MAX) {
-    DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
-      aSize.width, aSize.height);
-    return NS_ERROR_INVALID_ARG;
+    DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", aWidth, aHeight);
+    return;
+  }
+
+  uint32_t w = supportedSizes[smallestDeltaIndex].width;
+  uint32_t h = supportedSizes[smallestDeltaIndex].height;
+  DOM_CAMERA_LOGI("Requested picture size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h);
+  if (w > INT32_MAX || h > INT32_MAX) {
+    DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
+    return;
   }
 
-  Size size = supportedSizes[smallestDeltaIndex];
-  DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
-    size.width, size.height, aSize.width, aSize.height);
-  if (size.width > INT32_MAX || size.height > INT32_MAX) {
-    DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
-    return NS_ERROR_FAILURE;
+  mLastPictureWidth = w;
+  mLastPictureHeight = h;
+
+  {
+    // We must release the write-lock before updating the thumbnail size
+    RwAutoLockWrite write(mRwLock);
+    mParams.setPictureSize(static_cast<int>(w), static_cast<int>(h));
   }
 
-  nsresult rv = mParams.Set(CAMERA_PARAM_PICTURESIZE, size);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  mLastPictureSize = size;
-
-  // Finally, update the thumbnail size in case the picture
-  // aspect ratio changed.
-  return UpdateThumbnailSize();
+  // Finally, update the thumbnail size
+  UpdateThumbnailSize();
 }
 
 int32_t
 nsGonkCameraControl::RationalizeRotation(int32_t aRotation)
 {
   int32_t r = aRotation;
 
   // The result of this operation is an angle from 0..270 degrees,
@@ -767,418 +988,410 @@ nsGonkCameraControl::RationalizeRotation
   if (r < 0) {
     r += 360;
   }
 
   return r;
 }
 
 nsresult
-nsGonkCameraControl::SetPictureSize(const Size& aSize)
+nsGonkCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
 {
-  class SetPictureSize : public nsRunnable
-  {
-  public:
-    SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
-      : mCameraControl(aCameraControl)
-      , mSize(aSize)
-    {
-      MOZ_COUNT_CTOR(SetPictureSize);
+  if (aTakePicture->mCancel) {
+    if (mCameraHw.get()) {
+      mCameraHw->CancelTakePicture();
     }
-    ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); }
-
-    NS_IMETHODIMP
-    Run() MOZ_OVERRIDE
-    {
-      nsresult rv = mCameraControl->SetPictureSizeImpl(mSize);
-      if (NS_FAILED(rv)) {
-        mCameraControl->OnError(CameraControlListener::kInUnspecified,
-                                CameraControlListener::kErrorSetPictureSizeFailed);
-      }
-      return NS_OK;
-    }
-
-  protected:
-    nsRefPtr<nsGonkCameraControl> mCameraControl;
-    Size mSize;
-  };
-
-  if (NS_GetCurrentThread() == mCameraThread) {
-    return SetPictureSizeImpl(aSize);
   }
 
-  return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL);
-}
+  mTakePictureOnSuccessCb = aTakePicture->mOnSuccessCb;
+  mTakePictureOnErrorCb = aTakePicture->mOnErrorCb;
+
+  RETURN_IF_NO_CAMERA_HW();
+
+  // batch-update camera configuration
+  mDeferConfigUpdate = true;
+
+  SetPictureSize(aTakePicture->mSize.width, aTakePicture->mSize.height);
+
+  // Picture format -- need to keep it for the callback.
+  mFileFormat = aTakePicture->mFileFormat;
+  SetParameter(CameraParameters::KEY_PICTURE_FORMAT, NS_ConvertUTF16toUTF8(mFileFormat).get());
+
+  // Round 'rotation' up to a positive value from 0..270 degrees, in steps of 90.
+  int32_t r = static_cast<uint32_t>(aTakePicture->mRotation);
+  r += mCameraHw->GetSensorOrientation(GonkCameraHardware::OFFSET_SENSOR_ORIENTATION);
+  r = RationalizeRotation(r);
+  DOM_CAMERA_LOGI("setting picture rotation to %d degrees (mapped from %d)\n", r, aTakePicture->mRotation);
+  SetParameter(CameraParameters::KEY_ROTATION, nsPrintfCString("%u", r).get());
 
-nsresult
-nsGonkCameraControl::TakePictureImpl()
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-  RETURN_IF_NO_CAMERA_HW();
+  // Add any specified positional information -- don't care if these fail.
+  if (!isnan(aTakePicture->mPosition.latitude)) {
+    DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aTakePicture->mPosition.latitude);
+    SetParameter(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.latitude).get());
+  }
+  if (!isnan(aTakePicture->mPosition.longitude)) {
+    DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aTakePicture->mPosition.longitude);
+    SetParameter(CameraParameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.longitude).get());
+  }
+  if (!isnan(aTakePicture->mPosition.altitude)) {
+    DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aTakePicture->mPosition.altitude);
+    SetParameter(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.altitude).get());
+  }
+  if (!isnan(aTakePicture->mPosition.timestamp)) {
+    DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aTakePicture->mPosition.timestamp);
+    SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get());
+  }
+
+  // Add the non-GPS timestamp.  The EXIF date/time field is formatted as
+  // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
+  // is meant to be stored as a local time.  Since we are given seconds from
+  // Epoch GMT, we use localtime_r() to handle the conversion.
+  time_t time = aTakePicture->mDateTime;
+  if ((uint64_t)time != aTakePicture->mDateTime) {
+    DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime);
+  } else {
+    struct tm t;
+    if (localtime_r(&time, &t)) {
+      char dateTime[20];
+      if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
+        DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
+        // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME;
+        // for those who don't, we use the raw string key, and if the platform
+        // doesn't support it, it will be ignored.
+        //
+        // See bug 832494.
+        SetParameter("exif-datetime", dateTime);
+      } else {
+        DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
+      }
+    } else {
+      DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
+    }
+  }
+
+  mDeferConfigUpdate = false;
+  PushParameters();
 
   if (mCameraHw->TakePicture() != OK) {
     return NS_ERROR_FAILURE;
   }
-
-  // In Gonk, taking a picture implicitly stops the preview stream,
+  
+  // In Gonk, taking a picture implicitly kills the preview stream,
   // so we need to reflect that here.
-  OnPreviewStateChange(CameraControlListener::kPreviewPaused);
+  OnPreviewStateChange(PREVIEW_STOPPED);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::PushParametersImpl()
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   DOM_CAMERA_LOGI("Pushing camera parameters\n");
   RETURN_IF_NO_CAMERA_HW();
 
+  RwAutoLockRead lock(mRwLock);
   if (mCameraHw->PushParameters(mParams) != OK) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::PullParametersImpl()
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   DOM_CAMERA_LOGI("Pulling camera parameters\n");
   RETURN_IF_NO_CAMERA_HW();
 
-  return mCameraHw->PullParameters(mParams);
+  RwAutoLockWrite lock(mRwLock);
+  mCameraHw->PullParameters(mParams);
+  return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
-                                        const StartRecordingOptions* aOptions)
+nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
 {
   NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
 
   /**
    * Get the base path from device storage and append the app-specified
    * filename to it.  The filename may include a relative subpath
    * (e.g.) "DCIM/IMG_0001.jpg".
    *
    * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
    */
-  NS_ENSURE_TRUE(aFileDescriptor, NS_ERROR_FAILURE);
+  nsRefPtr<DeviceStorageFileDescriptor> dsfd = aStartRecording->mDSFileDescriptor;
+  NS_ENSURE_TRUE(dsfd, NS_ERROR_FAILURE);
   nsAutoString fullPath;
-  mVideoFile = aFileDescriptor->mDSFile;
+  mVideoFile = dsfd->mDSFile;
   mVideoFile->GetFullPath(fullPath);
   DOM_CAMERA_LOGI("Video filename is '%s'\n",
                   NS_LossyConvertUTF16toASCII(fullPath).get());
 
   if (!mVideoFile->IsSafePath()) {
     DOM_CAMERA_LOGE("Invalid video file name\n");
     return NS_ERROR_INVALID_ARG;
   }
 
   nsresult rv;
-  int fd = aFileDescriptor->mFileDescriptor.PlatformHandle();
-  if (aOptions) {
-    rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes,
-                        aOptions->maxVideoLengthMs);
-  } else {
-    rv = SetupRecording(fd, 0, 0, 0);
-  }
+  rv = SetupRecording(dsfd->mFileDescriptor.PlatformHandle(),
+                      aStartRecording->mOptions.rotation,
+                      aStartRecording->mOptions.maxFileSizeBytes,
+                      aStartRecording->mOptions.maxVideoLengthMs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     // important: we MUST destroy the recorder if start() fails!
     mRecorder = nullptr;
     return NS_ERROR_FAILURE;
   }
 
-  OnRecorderStateChange(CameraControlListener::kRecorderStarted, -1, -1);
   return NS_OK;
 }
 
-nsresult
-nsGonkCameraControl::StopRecordingImpl()
+class RecordingComplete : public nsRunnable
 {
-  class RecordingComplete : public nsRunnable
+public:
+  RecordingComplete(DeviceStorageFile* aFile)
+    : mFile(aFile)
+  { }
+
+  ~RecordingComplete() { }
+
+  NS_IMETHOD Run()
   {
-  public:
-    RecordingComplete(DeviceStorageFile* aFile)
-      : mFile(aFile)
-    { }
-
-    ~RecordingComplete() { }
+    MOZ_ASSERT(NS_IsMainThread());
 
-    NS_IMETHODIMP
-    Run()
-    {
-      MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified"));
+    return NS_OK;
+  }
 
-      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
-      return NS_OK;
-    }
+private:
+  nsRefPtr<DeviceStorageFile> mFile;
+};
 
-  private:
-    nsRefPtr<DeviceStorageFile> mFile;
-  };
-
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
+nsresult
+nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
+{
   // nothing to do if we have no mRecorder
   NS_ENSURE_TRUE(mRecorder, NS_OK);
 
   mRecorder->stop();
   mRecorder = nullptr;
-  OnRecorderStateChange(CameraControlListener::kRecorderStopped, -1, -1);
 
   // notify DeviceStorage that the new video file is closed and ready
-  return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> recordingComplete = new RecordingComplete(mVideoFile);
+  return NS_DispatchToMainThread(recordingComplete, NS_DISPATCH_NORMAL);
 }
 
 void
-nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess)
+nsGonkCameraControl::AutoFocusComplete(bool aSuccess)
 {
-  class AutoFocusComplete : public nsRunnable
-  {
-  public:
-    AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess)
-      : mCameraControl(aCameraControl)
-      , mSuccess(aSuccess)
-    { }
-
-    NS_IMETHODIMP
-    Run() MOZ_OVERRIDE
-    {
-      mCameraControl->OnAutoFocusComplete(mSuccess);
-      return NS_OK;
-    }
-
-  protected:
-    nsRefPtr<nsGonkCameraControl> mCameraControl;
-    bool mSuccess;
-  };
-
-  if (NS_GetCurrentThread() == mCameraThread) {
-    /**
-     * Auto focusing can change some of the camera's parameters, so
-     * we need to pull a new set before notifying any clients.
-     */
-    PullParametersImpl();
-    CameraControlImpl::OnAutoFocusComplete(aSuccess);
-    return;
-  }
+  /**
+   * Auto focusing can change some of the camera's parameters, so
+   * we need to pull a new set before sending the result to the
+   * main thread.
+   */
+  PullParametersImpl();
 
   /**
-   * Because the callback needs to call PullParametersImpl(),
-   * we need to dispatch this callback through the Camera Thread.
+   * If we make it here, regardless of the value of 'aSuccess', we
+   * consider the autofocus _process_ to have succeeded.  It is up
+   * to the onSuccess callback to determine how to handle the case
+   * where the camera wasn't actually able to acquire focus.
    */
-  mCameraThread->Dispatch(new AutoFocusComplete(this, aSuccess), NS_DISPATCH_NORMAL);
+  nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb, mWindowId);
+  /**
+   * Remember to set these to null so that we don't hold any extra
+   * references to our document's window.
+   */
+  mAutoFocusOnSuccessCb = nullptr;
+  mAutoFocusOnErrorCb = nullptr;
+  nsresult rv = NS_DispatchToMainThread(autoFocusResult);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch autoFocus() onSuccess callback to main thread!");
+  }
 }
 
 void
-nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength)
+nsGonkCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength)
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
   uint8_t* data = new uint8_t[aLength];
 
   memcpy(data, aData, aLength);
 
-  nsString s(NS_LITERAL_STRING("image/"));
-  s.Append(mFileFormat);
-  DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s).get(), aLength);
-  OnTakePictureComplete(data, aLength, s);
+  // TODO: see bug 779144.
+  nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(data, aLength, NS_LITERAL_STRING("image/jpeg"), mTakePictureOnSuccessCb, mWindowId);
+  /**
+   * Remember to set these to null so that we don't hold any extra
+   * references to our document's window.
+   */
+  mTakePictureOnSuccessCb = nullptr;
+  mTakePictureOnErrorCb = nullptr;
+  nsresult rv = NS_DispatchToMainThread(takePictureResult);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch takePicture() onSuccess callback to main thread!");
+  }
+}
 
-  if (mResumePreviewAfterTakingPicture) {
-    nsresult rv = StartPreview();
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv);
-      OnPreviewStateChange(CameraControlListener::kPreviewStopped);
-    }
+void
+nsGonkCameraControl::TakePictureError()
+{
+  nsCOMPtr<nsIRunnable> takePictureError = new CameraErrorResult(mTakePictureOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId);
+  mTakePictureOnSuccessCb = nullptr;
+  mTakePictureOnErrorCb = nullptr;
+  nsresult rv = NS_DispatchToMainThread(takePictureError);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch takePicture() onError callback to main thread!");
   }
-
-  DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n");
 }
 
 void
-nsGonkCameraControl::OnTakePictureError()
-{
-  CameraControlImpl::OnError(CameraControlListener::kInTakePicture,
-                             CameraControlListener::kErrorApiFailed);
-}
-
-nsresult
-nsGonkCameraControl::SetPreviewSize(const Size& aSize)
+nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight)
 {
-  MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
-  nsTArray<Size> previewSizes;
-  nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes);
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv);
-    return rv;
-  }
-
-  Size best = aSize;
+  android::Vector<Size> previewSizes;
+  uint32_t bestWidth = aWidth;
+  uint32_t bestHeight = aHeight;
   uint32_t minSizeDelta = UINT32_MAX;
   uint32_t delta;
+  Size size;
 
-  if (!aSize.width && !aSize.height) {
+  {
+    RwAutoLockRead lock(mRwLock);
+    mParams.getSupportedPreviewSizes(previewSizes);
+  }
+
+  if (!aWidth && !aHeight) {
     // no size specified, take the first supported size
-    best = previewSizes[0];
-  } else if (aSize.width && aSize.height) {
+    size = previewSizes[0];
+    bestWidth = size.width;
+    bestHeight = size.height;
+  } else if (aWidth && aHeight) {
     // both height and width specified, find the supported size closest to requested size
-    uint32_t targetArea = aSize.width * aSize.height;
-    for (uint32_t i = 0; i < previewSizes.Length(); i++) {
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
       Size size = previewSizes[i];
-      uint32_t delta = abs((long int)(size.width * size.height - targetArea));
+      uint32_t delta = abs((long int)(size.width * size.height - aWidth * aHeight));
       if (delta < minSizeDelta) {
         minSizeDelta = delta;
-        best = size;
+        bestWidth = size.width;
+        bestHeight = size.height;
       }
     }
-  } else if (!aSize.width) {
+  } else if (!aWidth) {
     // width not specified, find closest height match
-    for (uint32_t i = 0; i < previewSizes.Length(); i++) {
-      Size size = previewSizes[i];
-      delta = abs((long int)(size.height - aSize.height));
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
+      size = previewSizes[i];
+      delta = abs((long int)(size.height - aHeight));
       if (delta < minSizeDelta) {
         minSizeDelta = delta;
-        best = size;
+        bestWidth = size.width;
+        bestHeight = size.height;
       }
     }
-  } else if (!aSize.height) {
+  } else if (!aHeight) {
     // height not specified, find closest width match
-    for (uint32_t i = 0; i < previewSizes.Length(); i++) {
-      Size size = previewSizes[i];
-      delta = abs((long int)(size.width - aSize.width));
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
+      size = previewSizes[i];
+      delta = abs((long int)(size.width - aWidth));
       if (delta < minSizeDelta) {
         minSizeDelta = delta;
-        best = size;
+        bestWidth = size.width;
+        bestHeight = size.height;
       }
     }
   }
 
+  mWidth = bestWidth;
+  mHeight = bestHeight;
   {
-    ICameraControlParameterSetAutoEnter set(this);
-
-    // Some camera drivers will ignore our preview size if it's larger
-    // that the currently set video recording size, so we need to set
-    // both here just in case.
-    rv = SetAndPush(CAMERA_PARAM_PREVIEWSIZE, best);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set picture mode preview size (0x%x)\n", rv);
-      return rv;
-    }
-
-    rv = SetAndPush(CAMERA_PARAM_VIDEOSIZE, best);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to bump up picture mode video size (0x%x)\n", rv);
-      return rv;
-    }
+    RwAutoLockWrite lock(mRwLock);
+    mParams.setPreviewSize(mWidth, mHeight);
   }
-
-  mCurrentConfiguration.mPreviewSize = best;
-  return NS_OK;
+  PushParameters();
 }
 
 nsresult
 nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
 {
-  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
-
   // read preferences for camcorder
   mMediaProfiles = MediaProfiles::getInstance();
 
   nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile);
   mRecorderProfile = GetGonkRecorderProfileManager().get()->Get(profile.get());
   if (!mRecorderProfile) {
     DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
     return NS_ERROR_INVALID_ARG;
   }
 
   const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
   int width = video->GetWidth();
   int height = video->GetHeight();
   int fps = video->GetFramerate();
-  if (fps == -1 || width < 0 || height < 0) {
-    DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n",
-      fps, width, height);
+  if (fps == -1 || width == -1 || height == -1) {
+    DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", fps, width, height);
     return NS_ERROR_FAILURE;
   }
 
   PullParametersImpl();
 
-  Size size;
-  size.width = static_cast<uint32_t>(width);
-  size.height = static_cast<uint32_t>(height);
+  // configure camera video recording parameters
+  const size_t SIZE = 256;
+  char buffer[SIZE];
 
   {
-    ICameraControlParameterSetAutoEnter set(this);
-
-    // The camera interface allows for hardware to provide two video
-    //  streams, a low resolution preview and a potentially high resolution
-    //  stream for encoding. For now we don't use this and set preview and video
-    //  size to the same thing.
-    nsresult rv = SetAndPush(CAMERA_PARAM_PREVIEWSIZE, size);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv);
-      return rv;
-    }
+    RwAutoLockWrite lock(mRwLock);
+    mParams.setPreviewSize(width, height);
+    mParams.setPreviewFrameRate(fps);
 
-    rv = SetAndPush(CAMERA_PARAM_VIDEOSIZE, size);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv);
-      return rv;
-    }
-
-    rv = SetAndPush(CAMERA_PARAM_PREVIEWFRAMERATE, fps);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
-      return rv;
-    }
-    mPreviewFps = fps;
+    /**
+     * "record-size" is probably deprecated in later ICS;
+     * might need to set "video-size" instead of "record-size".
+     * See bug 795332.
+     */
+    snprintf(buffer, SIZE, "%dx%d", width, height);
+    mParams.set("record-size", buffer);
   }
 
-  mLastRecorderSize = size;
+  // push the updated camera configuration immediately
+  PushParameters();
   return NS_OK;
 }
 
 class GonkRecorderListener : public IMediaRecorderClient
 {
 public:
   GonkRecorderListener(nsGonkCameraControl* aCameraControl)
     : mCameraControl(aCameraControl)
   {
-    DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n",
-      __func__, __LINE__, this, mCameraControl.get());
+    DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", __func__, __LINE__, this, mCameraControl.get());
   }
 
   void notify(int msg, int ext1, int ext2)
   {
     if (mCameraControl) {
-      mCameraControl->OnRecorderEvent(msg, ext1, ext2);
+      mCameraControl->HandleRecorderEvent(msg, ext1, ext2);
     }
   }
 
   IBinder* onAsBinder()
   {
     DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
     return nullptr;
   }
 
 protected:
   ~GonkRecorderListener() { }
   nsRefPtr<nsGonkCameraControl> mCameraControl;
 };
 
 void
-nsGonkCameraControl::OnRecorderEvent(int msg, int ext1, int ext2)
+nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2)
 {
   /**
    * Refer to base/include/media/mediarecorder.h for a complete list
    * of error and info message codes.  There are duplicate values
    * within the status/error code space, as determined by code inspection:
    *
    *    +------- msg
    *    | +----- ext1
@@ -1216,79 +1429,79 @@ nsGonkCameraControl::OnRecorderEvent(int
    * 3. Specific error codes are from utils/Errors.h and/or
    *    include/media/stagefright/MediaErrors.h.
    * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
    * 5. These are mostly informational and we can ignore them; note that
    *    although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
    *    MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
    *    enum, they are used with different ext1 codes.  /o\
    */
-  int trackNum = CameraControlListener::kNoTrackNumber;
+  int trackNum = -1;  // no track
 
   switch (msg) {
     // Recorder-related events
     case MEDIA_RECORDER_EVENT_INFO:
       switch (ext1) {
         case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
           DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
-          OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("FileSizeLimitReached"), ext2, trackNum);
           return;
 
         case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
           DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
-          OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("VideoLengthLimitReached"), ext2, trackNum);
           return;
 
         case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
           DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
-          OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
           return;
       }
       break;
 
     case MEDIA_RECORDER_EVENT_ERROR:
       switch (ext1) {
         case MEDIA_RECORDER_ERROR_UNKNOWN:
           DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2);
-          OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("MediaRecorderFailed"), ext2, trackNum);
           return;
 
         case MEDIA_ERROR_SERVER_DIED:
           DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
-          OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("MediaServerFailed"), ext2, trackNum);
           return;
       }
       break;
 
     // Track-related events, see note 1(a) above.
     case MEDIA_RECORDER_TRACK_EVENT_INFO:
       trackNum = (ext1 & 0xF0000000) >> 28;
       ext1 &= 0xFFFF;
       switch (ext1) {
         case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
           if (ext2 == OK) {
             DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
-            OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
+            OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
             return;
           }
           DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
-          OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
+          OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
           return;
 
         case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME:
           DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2);
           return;
       }
       break;
 
     case MEDIA_RECORDER_TRACK_EVENT_ERROR:
       trackNum = (ext1 & 0xF0000000) >> 28;
       ext1 &= 0xFFFF;
       DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
-      OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
+      OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
       return;
   }
 
   // All unhandled cases wind up here
   DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
 }
 
 nsresult
@@ -1334,46 +1547,77 @@ nsGonkCameraControl::SetupRecording(int 
 
   // recording API needs file descriptor of output file
   CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
   CHECK_SETARG(mRecorder->prepare());
   return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::ReleaseHardwareImpl()
+nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode)
+{
+  // stop any currently running preview
+  StopPreviewInternal(true /* forced */);
+
+  // setup the video mode
+  nsresult rv = SetupVideoMode(aGetPreviewStreamVideoMode->mOptions.profile);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  const RecorderVideoProfile* video = mRecorderProfile->GetVideoProfile();
+  int width = video->GetWidth();
+  int height = video->GetHeight();
+  int fps = video->GetFramerate();
+  DOM_CAMERA_LOGI("recording preview format: %d x %d (%d fps)\n", width, height, fps);
+
+  // create and return new preview stream object
+  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, width, height, fps, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId);
+  rv = NS_DispatchToMainThread(getPreviewStreamResult);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 
   // if we're recording, stop recording
   if (mRecorder) {
     DOM_CAMERA_LOGI("shutting down existing video recorder\n");
     mRecorder->stop();
     mRecorder = nullptr;
   }
 
   // stop the preview
-  StopPreviewImpl();
+  StopPreviewInternal(true /* forced */);
 
   // release the hardware handle
   if (mCameraHw.get()){
      mCameraHw->Close();
      mCameraHw.clear();
   }
 
-  OnHardwareStateChange(CameraControlListener::kHardwareClosed);
+  if (aReleaseHardware) {
+    nsCOMPtr<nsIRunnable> releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId);
+    return NS_DispatchToMainThread(releaseHardwareResult);
+  }
+
   return NS_OK;
 }
 
 already_AddRefed<GonkRecorderProfileManager>
 nsGonkCameraControl::GetGonkRecorderProfileManager()
 {
   if (!mProfileManager) {
-    nsTArray<Size> sizes;
-    nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
+    nsTArray<idl::CameraSize> sizes;
+    nsresult rv = GetVideoSizes(sizes);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     mProfileManager = new GonkRecorderProfileManager(mCameraId);
     mProfileManager->SetSupportedResolutions(sizes);
   }
 
   nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
   return profileMgr.forget();
@@ -1381,90 +1625,92 @@ nsGonkCameraControl::GetGonkRecorderProf
 
 already_AddRefed<RecorderProfileManager>
 nsGonkCameraControl::GetRecorderProfileManagerImpl()
 {
   nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
   return profileMgr.forget();
 }
 
-void
-nsGonkCameraControl::OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer)
+nsresult
+nsGonkCameraControl::GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes)
 {
-  nsRefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
-
-  GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get());
+  aVideoSizes.Clear();
 
-  GrallocImage::GrallocData data;
-  data.mGraphicBuffer = static_cast<layers::GraphicBufferLocked*>(aBuffer);
-  data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width,
-                          mCurrentConfiguration.mPreviewSize.height);
-  videoImage->SetData(data);
+  android::Vector<Size> sizes;
+  {
+    RwAutoLockRead lock(mRwLock);
 
-  OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
-                    mCurrentConfiguration.mPreviewSize.height);
-}
-
-void
-nsGonkCameraControl::OnError(CameraControlListener::CameraErrorContext aWhere,
-                             CameraControlListener::CameraError aError)
-{
-  if (aError == CameraControlListener::kErrorServiceFailed) {
-    OnPreviewStateChange(CameraControlListener::kPreviewStopped);
-    OnHardwareStateChange(CameraControlListener::kHardwareClosed);
+    mParams.getSupportedVideoSizes(sizes);
+    if (sizes.size() == 0) {
+      DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n");
+      mParams.getSupportedPreviewSizes(sizes);
+    }
   }
 
-  CameraControlImpl::OnError(aWhere, aError);
+  if (sizes.size() == 0) {
+    DOM_CAMERA_LOGW("Camera doesn't report any supported video sizes at all\n");
+    return NS_OK;
+  }
+
+  for (size_t i = 0; i < sizes.size(); ++i) {
+    idl::CameraSize size;
+    size.width = sizes[i].width;
+    size.height = sizes[i].height;
+    aVideoSizes.AppendElement(size);
+  }
+  return NS_OK;
 }
 
 // Gonk callback handlers.
 namespace mozilla {
 
 void
-OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
+ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
 {
-  gc->OnTakePictureComplete(aData, aLength);
+  gc->TakePictureComplete(aData, aLength);
+}
+
+void
+ReceiveImageError(nsGonkCameraControl* gc)
+{
+  gc->TakePictureError();
 }
 
 void
-OnTakePictureError(nsGonkCameraControl* gc)
+AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
+{
+  gc->AutoFocusComplete(aSuccess);
+}
+
+static void
+GonkFrameBuilder(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight)
 {
-  gc->OnTakePictureError();
+  /**
+   * Cast the generic Image back to our platform-specific type and
+   * populate it.
+   */
+  GrallocImage* videoImage = static_cast<GrallocImage*>(aImage);
+  GrallocImage::GrallocData data;
+  data.mGraphicBuffer = static_cast<layers::GraphicBufferLocked*>(aBuffer);
+  data.mPicSize = IntSize(aWidth, aHeight);
+  videoImage->SetData(data);
 }
 
 void
-OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
+ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
 {
-  gc->OnAutoFocusComplete(aSuccess);
-}
-
-void
-OnNewPreviewFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
-{
-  gc->OnNewPreviewFrame(aBuffer);
+  gc->ReceiveFrame(aBuffer, ImageFormat::GRALLOC_PLANAR_YCBCR, GonkFrameBuilder);
 }
 
 void
 OnShutter(nsGonkCameraControl* gc)
 {
   gc->OnShutter();
 }
 
 void
 OnClosed(nsGonkCameraControl* gc)
 {
   gc->OnClosed();
 }
 
-void
-OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError,
-        int32_t aArg1, int32_t aArg2)
-{
-#ifdef PR_LOGGING
-  DOM_CAMERA_LOGE("OnError : aError=%d, aArg1=%d, aArg2=%d\n", aError, aArg1, aArg2);
-#else
-  unused << aArg1;
-  unused << aArg2;
-#endif
-  gc->OnError(CameraControlListener::kInUnspecified, aError);
-}
-
 } // namespace mozilla
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -1,10 +1,10 @@
 /*
- * Copyright (C) 2012-2013 Mozilla Foundation
+ * Copyright (C) 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
@@ -13,160 +13,136 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERACONTROL_H
 #define DOM_CAMERA_GONKCAMERACONTROL_H
 
 #include "base/basictypes.h"
+#include "prrwlock.h"
 #include <media/MediaProfiles.h>
-#include "mozilla/ReentrantMonitor.h"
 #include "DeviceStorage.h"
+#include "nsIDOMCameraManager.h"
+#include "DOMCameraControl.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "GonkRecorder.h"
 #include "GonkCameraHwMgr.h"
-#include "GonkCameraParameters.h"
 
 namespace android {
-  class GonkCameraHardware;
-  class MediaProfiles;
-  class GonkRecorder;
+class GonkCameraHardware;
+class MediaProfiles;
+class GonkRecorder;
 }
 
 namespace mozilla {
 
 namespace layers {
-  class GraphicBufferLocked;
-  class ImageContainer;
+class GraphicBufferLocked;
 }
 
 class GonkRecorderProfile;
 class GonkRecorderProfileManager;
 
 class nsGonkCameraControl : public CameraControlImpl
 {
 public:
-  nsGonkCameraControl(uint32_t aCameraId);
-  nsresult Init(const Configuration* aInitialConfig);
-  nsresult InitImpl();
-
-  void OnAutoFocusComplete(bool aSuccess);
-  void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
-  void OnTakePictureError();
-  void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer);
-  void OnRecorderEvent(int msg, int ext1, int ext2);
-  void OnError(CameraControlListener::CameraErrorContext aWhere,
-               CameraControlListener::CameraError aError);
+  nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
+  void DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
+  nsresult Init();
 
-  virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE;
-  virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE;
-  virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE;
-  virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE;
-  virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE;
-  virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE;
+  const char* GetParameter(const char* aKey);
+  const char* GetParameterConstChar(uint32_t aKey);
+  double GetParameterDouble(uint32_t aKey);
+  int32_t GetParameterInt32(uint32_t aKey);
+  void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
+  void GetParameter(uint32_t aKey, nsTArray<idl::CameraSize>& aSizes);
+  void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
+  void SetParameter(const char* aKey, const char* aValue);
+  void SetParameter(uint32_t aKey, const char* aValue);
+  void SetParameter(uint32_t aKey, double aValue);
+  void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
+  void SetParameter(uint32_t aKey, int aValue);
+  void SetParameter(uint32_t aKey, const idl::CameraSize& aSize);
+  nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes);
+  nsresult PushParameters();
 
-  virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE;
-
-  virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE;
-  virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE;
-
-  nsresult PushParameters();
-  nsresult PullParameters();
+  void AutoFocusComplete(bool aSuccess);
+  void TakePictureComplete(uint8_t* aData, uint32_t aLength);
+  void TakePictureError();
+  void HandleRecorderEvent(int msg, int ext1, int ext2);
 
 protected:
   ~nsGonkCameraControl();
 
-  using CameraControlImpl::OnNewPreviewFrame;
-  using CameraControlImpl::OnAutoFocusComplete;
-  using CameraControlImpl::OnTakePictureComplete;
-  using CameraControlImpl::OnConfigurationChange;
-  using CameraControlImpl::OnError;
-
-  virtual void BeginBatchParameterSet() MOZ_OVERRIDE;
-  virtual void EndBatchParameterSet() MOZ_OVERRIDE;
-
-  virtual nsresult SetConfigurationImpl(const Configuration& aConfig) MOZ_OVERRIDE;
-  nsresult SetConfigurationInternal(const Configuration& aConfig);
-  nsresult SetPictureConfiguration(const Configuration& aConfig);
-  nsresult SetVideoConfiguration(const Configuration& aConfig);
-
-  template<class T> nsresult SetAndPush(uint32_t aKey, const T& aValue);
-
-  virtual nsresult StartPreviewImpl() MOZ_OVERRIDE;
-  virtual nsresult StopPreviewImpl() MOZ_OVERRIDE;
-  virtual nsresult AutoFocusImpl(bool aCancelExistingCall) MOZ_OVERRIDE;
-  virtual nsresult TakePictureImpl() MOZ_OVERRIDE;
-  virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
-                                      const StartRecordingOptions* aOptions = nullptr) MOZ_OVERRIDE;
-  virtual nsresult StopRecordingImpl() MOZ_OVERRIDE;
-  virtual nsresult PushParametersImpl() MOZ_OVERRIDE;
-  virtual nsresult PullParametersImpl() MOZ_OVERRIDE;
-  virtual nsresult ReleaseHardwareImpl() MOZ_OVERRIDE;
-  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE;
+  nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
+  nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
+  nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
+  nsresult StopPreviewInternal(bool aForced = false);
+  nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
+  nsresult TakePictureImpl(TakePictureTask* aTakePicture);
+  nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
+  nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
+  nsresult PushParametersImpl();
+  nsresult PullParametersImpl();
+  nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode);
+  nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware);
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
   already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
 
   nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs);
   nsresult SetupVideoMode(const nsAString& aProfile);
-  nsresult SetPreviewSize(const Size& aSize);
-
-  friend class SetPictureSize;
-  friend class SetThumbnailSize;
-  nsresult SetPictureSize(const Size& aSize);
-  nsresult SetPictureSizeImpl(const Size& aSize);
-  nsresult SetThumbnailSize(const Size& aSize);
-  nsresult UpdateThumbnailSize();
-  nsresult SetThumbnailSizeImpl(const Size& aSize);
+  void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
+  void SetThumbnailSize(uint32_t aWidth, uint32_t aHeight);
+  void UpdateThumbnailSize();
+  void SetPictureSize(uint32_t aWidth, uint32_t aHeight);
 
   int32_t RationalizeRotation(int32_t aRotation);
 
   android::sp<android::GonkCameraHardware> mCameraHw;
+  double                    mExposureCompensationMin;
+  double                    mExposureCompensationStep;
+  bool                      mDeferConfigUpdate;
+  PRRWLock*                 mRwLock;
+  android::CameraParameters mParams;
+  uint32_t                  mWidth;
+  uint32_t                  mHeight;
+  uint32_t                  mLastPictureWidth;
+  uint32_t                  mLastPictureHeight;
+  uint32_t                  mLastThumbnailWidth;
+  uint32_t                  mLastThumbnailHeight;
 
-  Size                      mLastPictureSize;
-  Size                      mLastThumbnailSize;
-  Size                      mLastRecorderSize;
-  uint32_t                  mPreviewFps;
-  bool                      mResumePreviewAfterTakingPicture;
+  enum {
+    PREVIEW_FORMAT_UNKNOWN,
+    PREVIEW_FORMAT_YUV420P,
+    PREVIEW_FORMAT_YUV420SP
+  };
+  uint32_t                  mFormat;
 
-  Atomic<uint32_t>          mDeferConfigUpdate;
-  GonkCameraParameters      mParams;
-
-  nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
+  uint32_t                  mFps;
+  uint32_t                  mDiscardedFrameCount;
 
   android::MediaProfiles*   mMediaProfiles;
   nsRefPtr<android::GonkRecorder> mRecorder;
 
-  // Camcorder profile settings for the desired quality level
+  // camcorder profile settings for the desired quality level
   nsRefPtr<GonkRecorderProfileManager> mProfileManager;
   nsRefPtr<GonkRecorderProfile> mRecorderProfile;
 
   nsRefPtr<DeviceStorageFile> mVideoFile;
-  nsString                  mFileFormat;
-
-  // Guards against calling StartPreviewImpl() while in OnTakePictureComplete().
-  ReentrantMonitor          mReentrantMonitor;
 
 private:
   nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
   nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;
 };
 
 // camera driver callbacks
-void OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
-void OnTakePictureError(nsGonkCameraControl* gc);
-void OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
-void OnNewPreviewFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
+void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
+void ReceiveImageError(nsGonkCameraControl* gc);
+void AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
+void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
 void OnShutter(nsGonkCameraControl* gc);
 void OnClosed(nsGonkCameraControl* gc);
-void OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError,
-             int32_t aArg1, int32_t aArg2);
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERACONTROL_H
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -1,64 +1,86 @@
 /*
- * Copyright (C) 2012-2013 Mozilla Foundation
+ * Copyright (C) 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include "GonkCameraHwMgr.h"
-
 #include <binder/IPCThreadState.h>
 #include <sys/system_properties.h>
 
 #include "base/basictypes.h"
 #include "nsDebug.h"
 #include "GonkCameraControl.h"
+#include "GonkCameraHwMgr.h"
 #include "GonkNativeWindow.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace android;
 
+#if GIHM_TIMING_RECEIVEFRAME
+#define INCLUDE_TIME_H                  1
+#endif
+#if GIHM_TIMING_OVERALL
+#define INCLUDE_TIME_H                  1
+#endif
+
+#if INCLUDE_TIME_H
+#include <time.h>
+
+static __inline void timespecSubtract(struct timespec* a, struct timespec* b)
+{
+  // a = b - a
+  if (b->tv_nsec < a->tv_nsec) {
+    b->tv_nsec += 1000000000;
+    b->tv_sec -= 1;
+  }
+  a->tv_nsec = b->tv_nsec - a->tv_nsec;
+  a->tv_sec = b->tv_sec - a->tv_sec;
+}
+#endif
+
 GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera)
   : mCameraId(aCameraId)
   , mClosing(false)
+  , mMonitor("GonkCameraHardware.Monitor")
   , mNumFrames(0)
   , mCamera(aCamera)
   , mTarget(aTarget)
   , mInitialized(false)
   , mSensorOrientation(0)
 {
-  DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
+  DOM_CAMERA_LOGT( "%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget );
   Init();
 }
 
 void
 GonkCameraHardware::OnNewFrame()
 {
   if (mClosing) {
     return;
   }
   nsRefPtr<GraphicBufferLocked> buffer = mNativeWindow->getCurrentBuffer();
   if (!buffer) {
     DOM_CAMERA_LOGW("received null frame");
     return;
   }
-  OnNewPreviewFrame(mTarget, buffer);
+  ReceiveFrame(mTarget, buffer);
 }
 
 // Android data callback
 void
 GonkCameraHardware::postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, camera_frame_metadata_t* metadata)
 {
   if (mClosing) {
     return;
@@ -66,49 +88,53 @@ GonkCameraHardware::postData(int32_t aMs
 
   switch (aMsgType) {
     case CAMERA_MSG_PREVIEW_FRAME:
       // Do nothing
       break;
 
     case CAMERA_MSG_COMPRESSED_IMAGE:
       if (aDataPtr != nullptr) {
-        OnTakePictureComplete(mTarget, static_cast<uint8_t*>(aDataPtr->pointer()), aDataPtr->size());
+        ReceiveImage(mTarget, (uint8_t*)aDataPtr->pointer(), aDataPtr->size());
       } else {
-        OnTakePictureError(mTarget);
+        ReceiveImageError(mTarget);
       }
       break;
 
     default:
       DOM_CAMERA_LOGE("Unhandled data callback event %d\n", aMsgType);
       break;
   }
 }
 
 // Android notify callback
 void
 GonkCameraHardware::notify(int32_t aMsgType, int32_t ext1, int32_t ext2)
 {
+  bool bSuccess;
   if (mClosing) {
     return;
   }
 
   switch (aMsgType) {
     case CAMERA_MSG_FOCUS:
-      OnAutoFocusComplete(mTarget, !!ext1);
+      if (ext1) {
+        DOM_CAMERA_LOGI("Autofocus complete");
+        bSuccess = true;
+      } else {
+        DOM_CAMERA_LOGW("Autofocus failed");
+        bSuccess = false;
+      }
+      AutoFocusComplete(mTarget, bSuccess);
       break;
 
     case CAMERA_MSG_SHUTTER:
       OnShutter(mTarget);
       break;
 
-    case CAMERA_MSG_ERROR:
-      OnError(mTarget, CameraControlListener::kErrorServiceFailed, ext1, ext2);
-      break;
-
     default:
       DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType);
       break;
   }
 }
 
 void
 GonkCameraHardware::postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr)
@@ -265,36 +291,22 @@ GonkCameraHardware::TakePicture()
 
 void
 GonkCameraHardware::CancelTakePicture()
 {
   DOM_CAMERA_LOGW("%s: android::Camera do not provide this capability\n", __func__);
 }
 
 int
-GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
-{
-  const String8 s = aParams.Flatten();
-  return mCamera->setParameters(s);
-}
-
-int
 GonkCameraHardware::PushParameters(const CameraParameters& aParams)
 {
   String8 s = aParams.flatten();
   return mCamera->setParameters(s);
 }
 
-nsresult
-GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
-{
-  const String8 s = mCamera->getParameters();
-  return aParams.Unflatten(s);
-}
-
 void
 GonkCameraHardware::PullParameters(CameraParameters& aParams)
 {
   const String8 s = mCamera->getParameters();
   aParams.unflatten(s);
 }
 
 int
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -22,22 +22,25 @@
 #include <camera/CameraParameters.h>
 #include <utils/threads.h>
 
 #include "GonkCameraControl.h"
 #include "CameraCommon.h"
 
 #include "GonkCameraListener.h"
 #include "GonkNativeWindow.h"
-#include "GonkCameraParameters.h"
 #include "mozilla/ReentrantMonitor.h"
 
+// config
+#define GIHM_TIMING_RECEIVEFRAME    0
+#define GIHM_TIMING_OVERALL         1
+
+
 namespace mozilla {
   class nsGonkCameraControl;
-  class GonkCameraParameters;
 }
 
 namespace android {
 
 class GonkCameraHardware : public GonkNativeWindowNewFrameCallback
                          , public CameraListener
 {
 protected:
@@ -78,34 +81,37 @@ public:
   int      GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION);
 
   int      AutoFocus();
   void     CancelAutoFocus();
   int      TakePicture();
   void     CancelTakePicture();
   int      StartPreview();
   void     StopPreview();
-  int      PushParameters(const mozilla::GonkCameraParameters& aParams);
   int      PushParameters(const CameraParameters& aParams);
-  nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
   void     PullParameters(CameraParameters& aParams);
   int      StartRecording();
   int      StopRecording();
   int      SetListener(const sp<GonkCameraListener>& aListener);
   void     ReleaseRecordingFrame(const sp<IMemory>& aFrame);
   int      StoreMetaDataInBuffers(bool aEnabled);
 
 protected:
 
   uint32_t                      mCameraId;
   bool                          mClosing;
+  mozilla::ReentrantMonitor     mMonitor;
   uint32_t                      mNumFrames;
   sp<Camera>                    mCamera;
   mozilla::nsGonkCameraControl* mTarget;
   sp<GonkNativeWindow>          mNativeWindow;
+#if GIHM_TIMING_OVERALL
+  struct timespec               mStart;
+  struct timespec               mAutoFocusStart;
+#endif
   sp<GonkCameraListener>        mListener;
   bool                          mInitialized;
   int                           mRawSensorOrientation;
   int                           mSensorOrientation;
 
   bool IsInitialized()
   {
     return mInitialized;
--- a/dom/camera/GonkCameraManager.cpp
+++ b/dom/camera/GonkCameraManager.cpp
@@ -11,81 +11,80 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <camera/Camera.h>
 
+#include "GonkCameraControl.h"
+#include "DOMCameraManager.h"
 #include "CameraCommon.h"
-#include "GonkCameraControl.h"
-#include "ICameraControl.h"
+#include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
 
-// From ICameraControl, gonk-specific management functions
+// From nsDOMCameraManager, but gonk-specific!
 nsresult
-ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
+nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount)
 {
   aDeviceCount = android::Camera::getNumberOfCameras();
   return NS_OK;
 }
 
 nsresult
-ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
+nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
 {
   int32_t count = android::Camera::getNumberOfCameras();
-  int32_t deviceNum = static_cast<int32_t>(aDeviceNum);
-
   DOM_CAMERA_LOGI("GetCameraName : getNumberOfCameras() returned %d\n", count);
-  if (deviceNum < 0 || deviceNum > count) {
-    DOM_CAMERA_LOGE("GetCameraName : invalid device number (%u)\n", aDeviceNum);
+  if (aDeviceNum > count) {
+    DOM_CAMERA_LOGE("GetCameraName : invalid device number");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   android::CameraInfo info;
-  int rv = android::Camera::getCameraInfo(deviceNum, &info);
+  int rv = android::Camera::getCameraInfo(aDeviceNum, &info);
   if (rv != 0) {
-    DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", deviceNum, rv);
+    DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", aDeviceNum, rv);
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   switch (info.facing) {
     case CAMERA_FACING_BACK:
       aDeviceName.Assign("back");
       break;
 
     case CAMERA_FACING_FRONT:
       aDeviceName.Assign("front");
       break;
 
     default:
       aDeviceName.Assign("extra-camera-");
-      aDeviceName.AppendInt(deviceNum);
+      aDeviceName.AppendInt(aDeviceNum);
       break;
   }
   return NS_OK;
 }
 
-nsresult
-ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
+void
+nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
 {
   int32_t count = android::Camera::getNumberOfCameras();
+  if (count <= 0) {
+    return;
+  }
+
   DOM_CAMERA_LOGI("getListOfCameras : getNumberOfCameras() returned %d\n", count);
-  if (count <= 0) {
-    return NS_OK;
-  }
 
   // Allocate 2 extra slots to reserve space for 'front' and 'back' cameras
   // at the front of the array--we will collapse any empty slots below.
   aList.SetLength(2);
   uint32_t extraIdx = 2;
-  bool gotFront = false;
-  bool gotBack = false;
+  bool gotFront = false, gotBack = false;
   while (count--) {
     nsCString cameraName;
     nsresult result = GetCameraName(count, cameraName);
     if (result != NS_OK) {
       continue;
     }
 
     // The first camera we find named 'back' gets slot 0; and the first
@@ -100,33 +99,13 @@ ICameraControl::GetListOfCameras(nsTArra
       CopyUTF8toUTF16(cameraName, *aList.InsertElementAt(extraIdx));
       extraIdx++;
     }
   }
 
   if (!gotFront) {
     aList.RemoveElementAt(1);
   }
-
+  
   if (!gotBack) {
     aList.RemoveElementAt(0);
   }
-
-  return NS_OK;
 }
-
-// implementation-specific camera factory
-already_AddRefed<ICameraControl>
-ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
-{
-  if (aInitialConfig) {
-    DOM_CAMERA_LOGI("Creating camera %d control, initial mode '%s'\n",
-      aCameraId, aInitialConfig->mMode == kVideoMode ? "video" : "picture");
-  } else {
-    DOM_CAMERA_LOGI("Creating camera %d control, no intial configuration\n", aCameraId);
-  }
-
-  nsRefPtr<nsGonkCameraControl> control = new nsGonkCameraControl(aCameraId);
-  nsresult rv = control->Init(aInitialConfig);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  return control.forget();
-}
deleted file mode 100644
--- a/dom/camera/GonkCameraParameters.cpp
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- * Copyright (C) 2013 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GonkCameraParameters.h"
-#include "camera/CameraParameters.h"
-#include "ICameraControl.h"
-#include "CameraCommon.h"
-
-using namespace mozilla;
-using namespace android;
-
-/* static */ const char*
-GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
-{
-  switch (aKey) {
-    case CAMERA_PARAM_PREVIEWSIZE:
-      return KEY_PREVIEW_SIZE;
-    case CAMERA_PARAM_PREVIEWFORMAT:
-      return KEY_PREVIEW_FORMAT;
-    case CAMERA_PARAM_PREVIEWFRAMERATE:
-      return KEY_PREVIEW_FRAME_RATE;
-    case CAMERA_PARAM_EFFECT:
-      return KEY_EFFECT;
-    case CAMERA_PARAM_WHITEBALANCE:
-      return KEY_WHITE_BALANCE;
-    case CAMERA_PARAM_SCENEMODE:
-      return KEY_SCENE_MODE;
-    case CAMERA_PARAM_FLASHMODE:
-      return KEY_FLASH_MODE;
-    case CAMERA_PARAM_FOCUSMODE:
-      return KEY_FOCUS_MODE;
-    case CAMERA_PARAM_ZOOM:
-      return KEY_ZOOM;
-    case CAMERA_PARAM_METERINGAREAS:
-      return KEY_METERING_AREAS;
-    case CAMERA_PARAM_FOCUSAREAS:
-      return KEY_FOCUS_AREAS;
-    case CAMERA_PARAM_FOCALLENGTH:
-      return KEY_FOCAL_LENGTH;
-    case CAMERA_PARAM_FOCUSDISTANCENEAR:
-      return KEY_FOCUS_DISTANCES;
-    case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
-      return KEY_FOCUS_DISTANCES;
-    case CAMERA_PARAM_FOCUSDISTANCEFAR:
-      return KEY_FOCUS_DISTANCES;
-    case CAMERA_PARAM_EXPOSURECOMPENSATION:
-      return KEY_EXPOSURE_COMPENSATION;
-    case CAMERA_PARAM_PICTURESIZE:
-      return KEY_PICTURE_SIZE;
-    case CAMERA_PARAM_THUMBNAILQUALITY:
-      return KEY_JPEG_THUMBNAIL_QUALITY;
-    case CAMERA_PARAM_PICTURE_SIZE:
-      return KEY_PICTURE_SIZE;
-    case CAMERA_PARAM_PICTURE_FILEFORMAT:
-      return KEY_PICTURE_FORMAT;
-    case CAMERA_PARAM_PICTURE_ROTATION:
-      return KEY_ROTATION;
-    case CAMERA_PARAM_PICTURE_DATETIME:
-      // Not every platform defines a KEY_EXIF_DATETIME;
-      // for those that don't, we use the raw string key, and if the platform
-      // doesn't support it, it will be ignored.
-      //
-      // See bug 832494.
-      return "exif-datetime";
-    case CAMERA_PARAM_VIDEOSIZE:
-      return KEY_VIDEO_SIZE;
-
-    case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
-      return KEY_SUPPORTED_PREVIEW_SIZES;
-    case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
-      return KEY_SUPPORTED_PICTURE_SIZES;
-    case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
-      return KEY_SUPPORTED_VIDEO_SIZES;
-    case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
-      return KEY_SUPPORTED_PICTURE_FORMATS;
-    case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
-      return KEY_SUPPORTED_WHITE_BALANCE;
-    case CAMERA_PARAM_SUPPORTED_SCENEMODES:
-      return KEY_SUPPORTED_SCENE_MODES;
-    case CAMERA_PARAM_SUPPORTED_EFFECTS:
-      return KEY_SUPPORTED_EFFECTS;
-    case CAMERA_PARAM_SUPPORTED_FLASHMODES:
-      return KEY_SUPPORTED_FLASH_MODES;
-    case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
-      return KEY_SUPPORTED_FOCUS_MODES;
-    case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
-      return KEY_MAX_NUM_FOCUS_AREAS;
-    case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
-      return KEY_MAX_NUM_METERING_AREAS;
-    case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
-      return KEY_MIN_EXPOSURE_COMPENSATION;
-    case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
-      return KEY_MAX_EXPOSURE_COMPENSATION;
-    case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
-      return KEY_EXPOSURE_COMPENSATION_STEP;
-    case CAMERA_PARAM_SUPPORTED_ZOOM:
-      return KEY_ZOOM_SUPPORTED;
-    case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
-      return KEY_ZOOM_RATIOS;
-    case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
-      return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
-    default:
-      DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey);
-      return nullptr;
-  }
-}
-
-GonkCameraParameters::GonkCameraParameters()
-  : mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock"))
-  , mDirty(false)
-  , mInitialized(false)
-{
-  MOZ_COUNT_CTOR(GonkCameraParameters);
-  if (!mLock) {
-    MOZ_CRASH("OOM getting new PRRWLock");
-  }
-}
-
-GonkCameraParameters::~GonkCameraParameters()
-{
-  MOZ_COUNT_DTOR(GonkCameraParameters);
-  if (mLock) {
-    PR_DestroyRWLock(mLock);
-    mLock = nullptr;
-  }
-}
-
-// Any members that need to be initialized on the first parameter pull
-// need to get handled in here.
-nsresult
-GonkCameraParameters::Initialize()
-{
-  nsresult rv;
-
-  rv = GetImpl(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  rv = GetImpl(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  mInitialized = true;
-  return NS_OK;
-}
-
-// Handle nsAStrings
-nsresult
-GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
-{
-  return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
-}
-
-nsresult
-GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
-{
-  const char* val;
-  nsresult rv = GetImpl(aKey, val);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  aValue.AssignASCII(val);
-  return NS_OK;
-}
-
-// Handle ICameraControl::Sizes
-nsresult
-GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize)
-{
-  if (aSize.width > INT_MAX || aSize.height > INT_MAX) {
-    // AOSP can only handle signed ints.
-    DOM_CAMERA_LOGE("Camera parameter aKey=%d out of bounds (width=%u, height=%u)\n",
-      aSize.width, aSize.height);
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsresult rv;
-
-  switch (aKey) {
-    case CAMERA_PARAM_THUMBNAILSIZE:
-      // This is a special case--for some reason the thumbnail size
-      // is accessed as two separate values instead of a tuple.
-      // XXXmikeh - make this restore the original values on error
-      rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
-      if (NS_SUCCEEDED(rv)) {
-        rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
-      }
-      break;
-
-    case CAMERA_PARAM_VIDEOSIZE:
-      // "record-size" is probably deprecated in later ICS;
-      // might need to set "video-size" instead of "record-size";
-      // for the time being, set both. See bug 795332.
-      rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
-      if (NS_FAILED(rv)) {
-        break;
-      }
-      // intentional fallthrough
-
-    default:
-      rv = SetImpl(aKey, nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
-      break;
-  }
-
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv);
-  }
-  return rv;
-}
-
-nsresult
-GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize)
-{
-  nsresult rv;
-
-  if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
-    int width;
-    int height;
-
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
-    if (NS_FAILED(rv) || width < 0) {
-      return NS_ERROR_FAILURE;
-    }
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
-    if (NS_FAILED(rv) || height < 0) {
-      return NS_ERROR_FAILURE;
-    }
-
-    aSize.width = static_cast<uint32_t>(width);
-    aSize.height = static_cast<uint32_t>(height);
-    return NS_OK;
-  }
-
-  const char* value;
-  rv = GetImpl(aKey, value);
-  if (NS_FAILED(rv) || !value || *value == '\0') {
-    DOM_CAMERA_LOGW("Camera parameter aKey=%d not available (0x%x)\n", aKey, rv);
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) {
-    DOM_CAMERA_LOGE("Camera parameter aKey=%d size tuple '%s' is invalid\n", aKey, value);
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-// Handle arrays of ICameraControl::Regions
-nsresult
-GonkCameraParameters::SetTranslated(uint32_t aKey, const