Backed out changeset 3fde6fc99b0a (bug 1020368) for non-unified bustage.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 31 Oct 2014 18:51:23 -0400
changeset 237748 23d70439fb8be661d83a02be40307fe57ef7e68e
parent 237747 04a87b6ff211eabb56fad2bba369176d4fa3c8be
child 237749 b695d957565426e638bcbdcb0452eb6339daaa9c
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1020368
milestone36.0a1
backs out3fde6fc99b0a9ea3473849a00fca193c3c037d36
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 3fde6fc99b0a (bug 1020368) for non-unified bustage.
dom/bindings/Bindings.conf
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/DOMCameraManager.cpp
dom/camera/FallbackCameraControl.cpp
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkRecorderProfiles.cpp
dom/camera/GonkRecorderProfiles.h
dom/camera/ICameraControl.h
dom/camera/moz.build
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/CameraCapabilities.webidl
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -203,32 +203,16 @@ DOMInterfaces = {
     'headerFile': 'DOMCameraDetectedFace.h'
 },
 
 'CameraManager': {
     'nativeType': 'nsDOMCameraManager',
     'headerFile': 'DOMCameraManager.h'
 },
 
-'CameraRecorderAudioProfile': {
-    'headerFile': 'DOMCameraCapabilities.h'
-},
-
-'CameraRecorderProfile': {
-    'headerFile': 'DOMCameraCapabilities.h'
-},
-
-'CameraRecorderProfiles': {
-    'headerFile': 'DOMCameraCapabilities.h'
-},
-
-'CameraRecorderVideoProfile': {
-    'headerFile': 'DOMCameraCapabilities.h'
-},
-
 'CanvasRenderingContext2D': {
     'implicitJSContext': [
         'createImageData', 'getImageData'
     ],
     'binaryNames': {
         'mozImageSmoothingEnabled': 'imageSmoothingEnabled',
         'mozFillRule': 'fillRule'
     }
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -3,16 +3,17 @@
  * 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 "nsPrintfCString.h"
 #include "nsIWeakReferenceUtils.h"
+#include "CameraRecorderProfiles.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "CameraControlListener.h"
 
 using namespace mozilla;
 
 nsWeakPtr CameraControlImpl::sCameraThread;
@@ -59,16 +60,22 @@ CameraControlImpl::~CameraControlImpl()
 {
   MOZ_ASSERT(mListenerLock, "mListenerLock missing in ~CameraControlImpl()");
   if (mListenerLock) {
     PR_DestroyRWLock(mListenerLock);
     mListenerLock = nullptr;
   }
 }
 
+already_AddRefed<RecorderProfileManager>
+CameraControlImpl::GetRecorderProfileManager()
+{
+  return GetRecorderProfileManagerImpl();
+}
+
 void
 CameraControlImpl::Shutdown()
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 }
 
 void
 CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState)
@@ -102,17 +109,17 @@ CameraControlImpl::OnHardwareStateChange
 }
 
 void
 CameraControlImpl::OnConfigurationChange()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   RwLockAutoEnterRead lock(mListenerLock);
 
-  DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
+  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
@@ -257,17 +264,17 @@ CameraControlImpl::OnRateLimitPreview(bo
 
 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 %zu preview frame listener(s)\n",
+  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;
   }
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -20,16 +20,18 @@
 #include "CameraControlListener.h"
 
 namespace mozilla {
 
 namespace layers {
   class Image;
 }
 
+class RecorderProfileManager;
+
 class CameraControlImpl : public ICameraControl
 {
 public:
   explicit CameraControlImpl(uint32_t aCameraId);
   virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE;
   virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE;
 
   // See ICameraControl.h for these methods' return values.
@@ -42,16 +44,17 @@ public:
   virtual nsresult StartFaceDetection() MOZ_OVERRIDE;
   virtual nsresult StopFaceDetection() MOZ_OVERRIDE;
   virtual nsresult TakePicture() MOZ_OVERRIDE;
   virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
                                   const StartRecordingOptions* aOptions) MOZ_OVERRIDE;
   virtual nsresult StopRecording() MOZ_OVERRIDE;
   virtual nsresult ResumeContinuousFocus() MOZ_OVERRIDE;
 
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
   uint32_t GetCameraId() { return mCameraId; }
 
   virtual void Shutdown() MOZ_OVERRIDE;
 
   // Event handlers called directly from outside this class.
   void OnShutter();
   void OnClosed();
   void OnUserError(CameraControlListener::UserContext aContext, nsresult aError);
@@ -120,16 +123,18 @@ protected:
   virtual nsresult TakePictureImpl() = 0;
   virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                       const StartRecordingOptions* aOptions) = 0;
   virtual nsresult StopRecordingImpl() = 0;
   virtual nsresult ResumeContinuousFocusImpl() = 0;
   virtual nsresult PushParametersImpl() = 0;
   virtual nsresult PullParametersImpl() = 0;
 
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
+
   void OnShutterInternal();
   void OnClosedInternal();
 
   uint32_t mCameraId;
 
   CameraControlListener::CameraListenerConfiguration mCurrentConfiguration;
 
   CameraControlListener::PreviewState   mPreviewState;
--- a/dom/camera/CameraControlListener.h
+++ b/dom/camera/CameraControlListener.h
@@ -29,18 +29,18 @@ protected:
     MOZ_COUNT_DTOR(CameraControlListener);
   }
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
 
   enum HardwareState
   {
-    kHardwareClosed,
-    kHardwareOpen
+    kHardwareOpen,
+    kHardwareClosed
   };
   virtual void OnHardwareStateChange(HardwareState aState) { }
 
   enum PreviewState
   {
     kPreviewStopped,
     kPreviewPaused,
     kPreviewStarted
new file mode 100644
--- /dev/null
+++ b/dom/camera/CameraRecorderProfiles.cpp
@@ -0,0 +1,195 @@
+/* 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 "CameraRecorderProfiles.h"
+#include "jsapi.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+
+/**
+ * Video profile implementation.
+ */
+RecorderVideoProfile::RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderVideoProfile::~RecorderVideoProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderVideoProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+  NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
+
+  const char* codec = GetCodecName();
+  NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
+
+  JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, codec));
+  JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
+  if (!JS_SetProperty(aCx, o, "codec", v)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mBitrate != -1) {
+    v = INT_TO_JSVAL(mBitrate);
+    if (!JS_SetProperty(aCx, o, "bitrate", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mFramerate != -1) {
+    v = INT_TO_JSVAL(mFramerate);
+    if (!JS_SetProperty(aCx, o, "framerate", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mWidth != -1) {
+    v = INT_TO_JSVAL(mWidth);
+    if (!JS_SetProperty(aCx, o, "width", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mHeight != -1) {
+    v = INT_TO_JSVAL(mHeight);
+    if (!JS_SetProperty(aCx, o, "height", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
+
+/**
+ * Audio profile implementation.
+ */
+RecorderAudioProfile::RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderAudioProfile::~RecorderAudioProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderAudioProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+  NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
+
+  const char* codec = GetCodecName();
+  NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
+
+  JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, codec));
+  JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
+  if (!JS_SetProperty(aCx, o, "codec", v)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mBitrate != -1) {
+    v = INT_TO_JSVAL(mBitrate);
+    if (!JS_SetProperty(aCx, o, "bitrate", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mSamplerate != -1) {
+    v = INT_TO_JSVAL(mSamplerate);
+    if (!JS_SetProperty(aCx, o, "samplerate", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mChannels != -1) {
+    v = INT_TO_JSVAL(mChannels);
+    if (!JS_SetProperty(aCx, o, "channels", v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
+
+/**
+ * Recorder Profile
+ */
+RecorderProfile::RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+  , mName(nullptr)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderProfile::~RecorderProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+/**
+ * Recorder profile manager implementation.
+ */
+RecorderProfileManager::RecorderProfileManager(uint32_t aCameraId)
+  : mCameraId(aCameraId)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderProfileManager::~RecorderProfileManager()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderProfileManager::GetJsObject(JSContext* aCx, JSObject** aObject) const
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+  if (!o) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (uint32_t q = 0; q < GetMaxQualityIndex(); ++q) {
+    if (!IsSupported(q)) {
+      continue;
+    }
+
+    nsRefPtr<RecorderProfile> profile = Get(q);
+    if (!profile) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    const char* profileName = profile->GetName();
+    if (!profileName) {
+      // don't allow anonymous recorder profiles
+      continue;
+    }
+
+    JS::Rooted<JSObject*> p(aCx);
+    nsresult rv = profile->GetJsObject(aCx, p.address());
+    NS_ENSURE_SUCCESS(rv, rv);
+    JS::Rooted<JS::Value> v(aCx, OBJECT_TO_JSVAL(p));
+
+    if (!JS_SetProperty(aCx, o, profileName, v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/CameraRecorderProfiles.h
@@ -0,0 +1,274 @@
+/* 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_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 "CameraCommon.h"
+
+namespace mozilla {
+
+class CameraControlImpl;
+
+class RecorderVideoProfile
+{
+public:
+  RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  virtual ~RecorderVideoProfile();
+
+  int GetBitrate() const    { return mBitrate; }
+  int GetFramerate() const  { return mFramerate; }
+  int GetWidth() const      { return mWidth; }
+  int GetHeight() const     { return mHeight; }
+
+  enum Codec {
+    H263,
+    H264,
+    MPEG4SP,
+    UNKNOWN
+  };
+  Codec GetCodec() const    { return mCodec; }
+  const char* GetCodecName() const
+  {
+    switch (mCodec) {
+      case H263:    return "h263";
+      case H264:    return "h264";
+      case MPEG4SP: return "mpeg4sp";
+      default:      return nullptr;
+    }
+  }
+
+  // Get a representation of this video profile that can be returned
+  // to JS, possibly as a child member of another object.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aObject' is null;
+  //  - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
+  //  - NS_ERROR_FAILURE if construction of the JS object fails.
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
+
+protected:
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  Codec mCodec;
+  int mBitrate;
+  int mFramerate;
+  int mWidth;
+  int mHeight;
+};
+
+class RecorderAudioProfile
+{
+public:
+  RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  virtual ~RecorderAudioProfile();
+
+  int GetBitrate() const    { return mBitrate; }
+  int GetSamplerate() const { return mSamplerate; }
+  int GetChannels() const   { return mChannels; }
+
+  enum Codec {
+    AMRNB,
+    AMRWB,
+    AAC,
+    UNKNOWN
+  };
+
+  Codec GetCodec() const    { return mCodec; }
+  const char* GetCodecName() const
+  {
+    switch (mCodec) {
+      case AMRNB: return "amrnb";
+      case AMRWB: return "amrwb";
+      case AAC:   return "aac";
+      default:    return nullptr;
+    }
+  }
+
+  // Get a representation of this audio profile that can be returned
+  // to JS, possibly as a child member of another object.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aObject' is null;
+  //  - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
+  //  - NS_ERROR_FAILURE if construction of the JS object fails.
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
+
+protected:
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  Codec mCodec;
+  int mBitrate;
+  int mSamplerate;
+  int mChannels;
+};
+
+class RecorderProfile
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
+
+  RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+
+  virtual const RecorderVideoProfile* GetVideoProfile() const = 0;
+  virtual const RecorderAudioProfile* GetAudioProfile() const = 0;
+  const char* GetName() const { return mName; }
+
+  enum FileFormat {
+    THREE_GPP,
+    MPEG4,
+    UNKNOWN
+  };
+  FileFormat GetFileFormat() const { return mFileFormat; }
+  const char* GetFileFormatName() const
+  {
+    switch (mFileFormat) {
+      case THREE_GPP: return "3gp";
+      case MPEG4:     return "mp4";
+      default:        return nullptr;
+    }
+  }
+  const char* GetFileMimeType() const
+  {
+    switch (mFileFormat) {
+      case THREE_GPP: return VIDEO_3GPP;
+      case MPEG4:     return VIDEO_MP4;
+      default:        return nullptr;
+    }
+  }
+
+  // Get a representation of this recorder profile that can be returned
+  // to JS, possibly as a child member of another object.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aObject' is null;
+  //  - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
+  //  - NS_ERROR_FAILURE if construction of the JS object fails.
+  virtual nsresult GetJsObject(JSContext* aCx, JSObject** aObject) = 0;
+
+protected:
+  virtual ~RecorderProfile();
+
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  const char* mName;
+  FileFormat mFileFormat;
+};
+
+template <class Audio, class Video>
+class RecorderProfileBase : public RecorderProfile
+{
+public:
+  RecorderProfileBase(uint32_t aCameraId, uint32_t aQualityIndex)
+    : RecorderProfile(aCameraId, aQualityIndex)
+    , mVideo(aCameraId, aQualityIndex)
+    , mAudio(aCameraId, aQualityIndex)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~RecorderProfileBase()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  const RecorderVideoProfile* GetVideoProfile() const { return &mVideo; }
+  const RecorderAudioProfile* GetAudioProfile() const { return &mAudio; }
+
+  // Get a representation of this recorder profile that can be returned
+  // to JS, possibly as a child member of another object.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aObject' is null;
+  //  - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
+  //  - NS_ERROR_NOT_AVAILABLE if the profile has no file format name;
+  //  - NS_ERROR_FAILURE if construction of the JS object fails.
+  nsresult
+  GetJsObject(JSContext* aCx, JSObject** aObject)
+  {
+    NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+    const char* format = GetFileFormatName();
+    if (!format) {
+      // the profile must have a file format
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+
+    JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
+    if (!o) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, format));
+    JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
+    if (!JS_SetProperty(aCx, o, "format", v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JS::Rooted<JSObject*> video(aCx);
+    nsresult rv = mVideo.GetJsObject(aCx, video.address());
+    NS_ENSURE_SUCCESS(rv, rv);
+    v = OBJECT_TO_JSVAL(video);
+    if (!JS_SetProperty(aCx, o, "video", v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JS::Rooted<JSObject*> audio(aCx);
+    rv = mAudio.GetJsObject(aCx, audio.address());
+    NS_ENSURE_SUCCESS(rv, rv);
+    v = OBJECT_TO_JSVAL(audio);
+    if (!JS_SetProperty(aCx, o, "audio", v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    *aObject = o;
+    return NS_OK;
+  }
+
+protected:
+  Video mVideo;
+  Audio mAudio;
+};
+
+class RecorderProfileManager
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfileManager)
+
+  virtual bool IsSupported(uint32_t aQualityIndex) const { return true; }
+  virtual already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const = 0;
+
+  uint32_t GetMaxQualityIndex() const { return mMaxQualityIndex; }
+
+  // Get a representation of all supported recorder profiles that can be
+  // returned to JS.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aObject' is null;
+  //  - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
+  //  - NS_ERROR_NOT_AVAILABLE if the profile has no file format name;
+  //  - NS_ERROR_FAILURE if construction of the JS object fails.
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject) const;
+
+protected:
+  explicit RecorderProfileManager(uint32_t aCameraId);
+  virtual ~RecorderProfileManager();
+
+  uint32_t mCameraId;
+  uint32_t mMaxQualityIndex;
+};
+
+} // namespace mozilla
+
+#endif // DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -7,246 +7,68 @@
 #include "DOMCameraCapabilities.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/CameraManagerBinding.h"
 #include "mozilla/dom/CameraCapabilitiesBinding.h"
 #include "Navigator.h"
 #include "CameraCommon.h"
 #include "ICameraControl.h"
+#include "CameraRecorderProfiles.h"
 
 namespace mozilla {
 namespace dom {
 
-/**
- * CameraRecorderVideoProfile
- */
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderVideoProfile, mParent)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderVideoProfile)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderVideoProfile)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderVideoProfile)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-JSObject*
-CameraRecorderVideoProfile::WrapObject(JSContext* aCx)
-{
-  return CameraRecorderVideoProfileBinding::Wrap(aCx, this);
-}
-
-CameraRecorderVideoProfile::CameraRecorderVideoProfile(nsISupports* aParent,
-    const ICameraControl::RecorderProfile::Video& aProfile)
-  : mParent(aParent)
-  , mCodec(aProfile.GetCodec())
-  , mBitrate(aProfile.GetBitsPerSecond())
-  , mFramerate(aProfile.GetFramesPerSecond())
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  mSize.mWidth = aProfile.GetSize().width;
-  mSize.mHeight = aProfile.GetSize().height;
-
-  DOM_CAMERA_LOGI("  video: '%s' %ux%u bps=%u fps=%u\n",
-    NS_ConvertUTF16toUTF8(mCodec).get(), mSize.mWidth, mSize.mHeight, mBitrate, mFramerate);
-}
-
-CameraRecorderVideoProfile::~CameraRecorderVideoProfile()
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-/**
- * CameraRecorderAudioProfile
- */
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderAudioProfile, mParent)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderAudioProfile)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderAudioProfile)
+NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderAudioProfile)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-JSObject*
-CameraRecorderAudioProfile::WrapObject(JSContext* aCx)
-{
-  return CameraRecorderAudioProfileBinding::Wrap(aCx, this);
-}
-
-CameraRecorderAudioProfile::CameraRecorderAudioProfile(nsISupports* aParent,
-    const ICameraControl::RecorderProfile::Audio& aProfile)
-  : mParent(aParent)
-  , mCodec(aProfile.GetCodec())
-  , mBitrate(aProfile.GetBitsPerSecond())
-  , mSamplerate(aProfile.GetSamplesPerSecond())
-  , mChannels(aProfile.GetChannels())
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  DOM_CAMERA_LOGI("  audio: '%s' bps=%u samples/s=%u channels=%u\n",
-    NS_ConvertUTF16toUTF8(mCodec).get(), mBitrate, mSamplerate, mChannels);
-}
-
-CameraRecorderAudioProfile::~CameraRecorderAudioProfile()
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-/**
- * CameraRecorderProfile
- */
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfile, mParent)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfile)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfile)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfile)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-JSObject*
-CameraRecorderProfile::WrapObject(JSContext* aCx)
-{
-  return CameraRecorderProfileBinding::Wrap(aCx, this);
-}
+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
 
-CameraRecorderProfile::CameraRecorderProfile(nsISupports* aParent,
-                                             const ICameraControl::RecorderProfile& aProfile)
-  : mParent(aParent)
-  , mName(aProfile.GetName())
-  , mContainerFormat(aProfile.GetContainer())
-  , mMimeType(aProfile.GetMimeType())
-  , mVideo(new CameraRecorderVideoProfile(this, aProfile.GetVideo()))
-  , mAudio(new CameraRecorderAudioProfile(this, aProfile.GetAudio()))
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  DOM_CAMERA_LOGI("profile: '%s' container=%s mime-type=%s\n",
-    NS_ConvertUTF16toUTF8(mName).get(),
-    NS_ConvertUTF16toUTF8(mContainerFormat).get(),
-    NS_ConvertUTF16toUTF8(mMimeType).get());
-}
-
-CameraRecorderProfile::~CameraRecorderProfile()
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-/**
- * CameraRecorderProfiles
- */
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfiles, mParent)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfiles)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfiles)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfiles)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-JSObject*
-CameraRecorderProfiles::WrapObject(JSContext* aCx)
-{
-  return CameraRecorderProfilesBinding::Wrap(aCx, this);
-}
-
-CameraRecorderProfiles::CameraRecorderProfiles(nsISupports* aParent,
-                                               ICameraControl* aCameraControl)
-  : mParent(aParent)
-  , mCameraControl(aCameraControl)
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
+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
 
-CameraRecorderProfiles::~CameraRecorderProfiles()
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-void
-CameraRecorderProfiles::GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames)
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p, flags=0x%x\n",
-    __func__, __LINE__, this, aFlags);
-
-  nsresult rv = mCameraControl->GetRecorderProfiles(aNames);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aNames.Clear();
-  }
-}
-
-CameraRecorderProfile*
-CameraRecorderProfiles::NamedGetter(const nsAString& aName, bool& aFound)
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s'\n", __func__, __LINE__, this,
-    NS_ConvertUTF16toUTF8(aName).get());
-
-  CameraRecorderProfile* profile = mProfiles.GetWeak(aName, &aFound);
-  if (!aFound || !profile) {
-    nsRefPtr<ICameraControl::RecorderProfile> p = mCameraControl->GetProfileInfo(aName);
-    if (p) {
-      profile = new CameraRecorderProfile(this, *p);
-      mProfiles.Put(aName, profile);
-      aFound = true;
-    }
-  }
-  return profile;
-}
-
-bool
-CameraRecorderProfiles::NameIsEnumerable(const nsAString& aName)
-{
-  DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s' (always returns true)\n",
-    __func__, __LINE__, this, NS_ConvertUTF16toUTF8(aName).get());
-
-  return true;
-}
-
-/**
- * CameraCapabilities
- */
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraCapabilities, mWindow)
+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
 
 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_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 /* static */
 bool
 CameraCapabilities::HasSupport(JSContext* aCx, JSObject* aGlobal)
 {
   return Navigator::HasCameraSupport(aCx, aGlobal);
 }
 
-CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow,
-                                       ICameraControl* aCameraControl)
-  : mMaxFocusAreas(0)
-  , mMaxMeteringAreas(0)
-  , mMaxDetectedFaces(0)
-  , mMinExposureCompensation(0.0)
-  , mMaxExposureCompensation(0.0)
-  , mExposureCompensationStep(0.0)
+CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow)
+  : mRecorderProfiles(JS::UndefinedValue())
   , mWindow(aWindow)
-  , mCameraControl(aCameraControl)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   MOZ_COUNT_CTOR(CameraCapabilities);
+  mozilla::HoldJSObjects(this);
 }
 
 CameraCapabilities::~CameraCapabilities()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  mRecorderProfiles = JS::UndefinedValue();
+  mozilla::DropJSObjects(this);
   MOZ_COUNT_DTOR(CameraCapabilities);
 }
 
 JSObject*
 CameraCapabilities::WrapObject(JSContext* aCx)
 {
   return CameraCapabilitiesBinding::Wrap(aCx, this);
 }
@@ -255,246 +77,234 @@ CameraCapabilities::WrapObject(JSContext
   do {                                                        \
     if (NS_FAILED(rv)) {                                      \
       DOM_CAMERA_LOGW("Error %x trying to get " #param "\n",  \
         (rv));                                                \
     }                                                         \
   } while(0)
 
 nsresult
-CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes)
+CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl,
+                                          uint32_t aKey, nsTArray<CameraSize>& aSizes)
 {
   nsresult rv;
   nsTArray<ICameraControl::Size> sizes;
 
-  rv = mCameraControl->Get(aKey, sizes);
+  rv = aCameraControl->Get(aKey, sizes);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   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;
 }
 
-void
-CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval)
+nsresult
+CameraCapabilities::Populate(ICameraControl* aCameraControl)
 {
-  if (mPreviewSizes.Length() == 0) {
-    nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
-                                        mPreviewSizes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
+  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);
+
+  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, mIsoModes);
+  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
+
+  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;
+
+  int32_t faces;
+  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES, faces);
+  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES);
+  mMaxDetectedFaces = faces < 0 ? 0 : faces;
+
+  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);
+
+  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 (0x%x)\n", rv);
+    } else {
+      mRecorderProfiles = JS::ObjectValue(*o);
+    }
   }
+
+  // For now, always return success, since the presence or absence of capabilities
+  // indicates whether or not they are supported.
+  return NS_OK;
+}
+
+void
+CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval) const
+{
   retval = mPreviewSizes;
 }
 
 void
-CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval)
+CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval) const
 {
-  if (mPictureSizes.Length() == 0) {
-    nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PICTURESIZES,
-                                        mPictureSizes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
-  }
   retval = mPictureSizes;
 }
 
 void
-CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval)
+CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval) const
 {
-  if (mThumbnailSizes.Length() == 0) {
-    nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES,
-                                        mThumbnailSizes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
-  }
   retval = mThumbnailSizes;
 }
 
 void
-CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval)
+CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval) const
 {
-  if (mVideoSizes.Length() == 0) {
-    nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
-                                        mVideoSizes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
-  }
   retval = mVideoSizes;
 }
 
 void
-CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval)
+CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval) const
 {
-  if (mFileFormats.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
-                                      mFileFormats);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
-  }
   retval = mFileFormats;
 }
 
 void
-CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval)
+CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval) const
 {
-  if (mWhiteBalanceModes.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
-                                      mWhiteBalanceModes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
-  }
   retval = mWhiteBalanceModes;
 }
 
 void
-CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval)
+CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval) const
 {
-  if (mSceneModes.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES,
-                                      mSceneModes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES);
-  }
   retval = mSceneModes;
 }
 
 void
-CameraCapabilities::GetEffects(nsTArray<nsString>& retval)
+CameraCapabilities::GetEffects(nsTArray<nsString>& retval) const
 {
-  if (mEffects.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS,
-                                      mEffects);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
-  }
   retval = mEffects;
 }
 
 void
-CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval)
+CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval) const
 {
-  if (mFlashModes.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES,
-                                      mFlashModes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
-  }
   retval = mFlashModes;
 }
 
 void
-CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval)
+CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval) const
 {
-  if (mFocusModes.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES,
-                                      mFocusModes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
-  }
   retval = mFocusModes;
 }
 
 void
-CameraCapabilities::GetZoomRatios(nsTArray<double>& retval)
+CameraCapabilities::GetZoomRatios(nsTArray<double>& retval) const
 {
-  if (mZoomRatios.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
-                                      mZoomRatios);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
-  }
   retval = mZoomRatios;
 }
 
 uint32_t
-CameraCapabilities::MaxFocusAreas()
+CameraCapabilities::MaxFocusAreas() const
 {
-  if (mMaxFocusAreas == 0) {
-    int32_t areas;
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
-                                      areas);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
-    mMaxFocusAreas = areas < 0 ? 0 : areas;
-  }
   return mMaxFocusAreas;
 }
 
 uint32_t
-CameraCapabilities::MaxMeteringAreas()
+CameraCapabilities::MaxMeteringAreas() const
 {
-  if (mMaxMeteringAreas == 0) {
-    int32_t areas;
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
-                                      areas);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
-    mMaxMeteringAreas = areas < 0 ? 0 : areas;
-  }
   return mMaxMeteringAreas;
 }
 
 uint32_t
-CameraCapabilities::MaxDetectedFaces()
+CameraCapabilities::MaxDetectedFaces() const
 {
-  if (mMaxDetectedFaces == 0) {
-    int32_t faces;
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES,
-                                      faces);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES);
-    mMaxDetectedFaces = faces < 0 ? 0 : faces;
-  }
   return mMaxDetectedFaces;
 }
 
 double
-CameraCapabilities::MinExposureCompensation()
+CameraCapabilities::MinExposureCompensation() const
 {
-  if (mMinExposureCompensation == 0.0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
-                                      mMinExposureCompensation);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
-  }
   return mMinExposureCompensation;
 }
 
 double
-CameraCapabilities::MaxExposureCompensation()
+CameraCapabilities::MaxExposureCompensation() const
 {
-  if (mMaxExposureCompensation == 0.0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
-                                      mMaxExposureCompensation);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
-  }
   return mMaxExposureCompensation;
 }
 
 double
-CameraCapabilities::ExposureCompensationStep()
+CameraCapabilities::ExposureCompensationStep() const
 {
-  if (mExposureCompensationStep == 0.0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
-                                      mExposureCompensationStep);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
-  }
   return mExposureCompensationStep;
 }
 
-CameraRecorderProfiles*
-CameraCapabilities::RecorderProfiles()
+void
+CameraCapabilities::GetRecorderProfiles(JSContext* aCx,
+                                        JS::MutableHandle<JS::Value> aRetval) const
 {
-  nsRefPtr<CameraRecorderProfiles> profiles = mRecorderProfiles;
-  if (!mRecorderProfiles) {
-    profiles = new CameraRecorderProfiles(this, mCameraControl);
-    mRecorderProfiles = profiles;
-  }
-  return profiles;
+  JS::ExposeValueToActiveJS(mRecorderProfiles);
+  aRetval.set(mRecorderProfiles);
 }
 
 void
-CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval)
+CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval) const
 {
-  if (mIsoModes.Length() == 0) {
-    nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES,
-                                      mIsoModes);
-    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
-  }
   retval = mIsoModes;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/camera/DOMCameraCapabilities.h
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -4,229 +4,86 @@
  * 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__
 
 #include "nsString.h"
 #include "nsAutoPtr.h"
-#include "base/basictypes.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CameraManagerBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsPIDOMWindow.h"
-#include "nsHashKeys.h"
-#include "nsRefPtrHashtable.h"
-#include "nsDataHashtable.h"
-#include "ICameraControl.h"
 
 struct JSContext;
+class nsPIDOMWindow;
 
 namespace mozilla {
+
+class ICameraControl;
+class RecorderProfileManager;
+
 namespace dom {
 
-/**
- * CameraRecorderVideoProfile
- */
-class CameraRecorderVideoProfile MOZ_FINAL : public nsISupports
-                                           , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderVideoProfile)
-
-  explicit CameraRecorderVideoProfile(nsISupports* aParent,
-    const ICameraControl::RecorderProfile::Video& aProfile);
-  nsISupports* GetParentObject() const        { return mParent; }
-  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
-  uint32_t BitsPerSecond() const              { return mBitrate; }
-  uint32_t FramesPerSecond() const            { return mFramerate; }
-  void GetCodec(nsAString& aCodec) const      { aCodec = mCodec; }
-
-  void GetSize(dom::CameraSize& aSize) const  { aSize = mSize; }
-
-  // XXXmikeh - legacy, remove these when the Camera app is updated
-  uint32_t Width() const                      { return mSize.mWidth; }
-  uint32_t Height() const                     { return mSize.mHeight; }
-
-protected:
-  virtual ~CameraRecorderVideoProfile();
-
-  nsCOMPtr<nsISupports> mParent;
-
-  const nsString mCodec;
-  uint32_t mBitrate;
-  uint32_t mFramerate;
-  dom::CameraSize mSize;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderVideoProfile);
-};
-
-/**
- * CameraRecorderAudioProfile
- */
-class CameraRecorderAudioProfile MOZ_FINAL : public nsISupports
-                                           , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderAudioProfile)
-
-  explicit CameraRecorderAudioProfile(nsISupports* aParent,
-    const ICameraControl::RecorderProfile::Audio& aProfile);
-  nsISupports* GetParentObject() const    { return mParent; }
-  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
-  uint32_t BitsPerSecond() const          { return mBitrate; }
-  uint32_t SamplesPerSecond() const       { return mSamplerate; }
-  uint32_t Channels() const               { return mChannels; }
-  void GetCodec(nsAString& aCodec) const  { aCodec = mCodec; }
-
-protected:
-  virtual ~CameraRecorderAudioProfile();
-
-  nsCOMPtr<nsISupports> mParent;
-
-  const nsString mCodec;
-  uint32_t mBitrate;
-  uint32_t mSamplerate;
-  uint32_t mChannels;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderAudioProfile);
-};
-
-/**
- * CameraRecorderProfile
- */
-class CameraRecorderProfile MOZ_FINAL : public nsISupports
-                                      , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfile)
-
-  explicit CameraRecorderProfile(nsISupports* aParent,
-                                 const ICameraControl::RecorderProfile& aProfile);
-  nsISupports* GetParentObject() const          { return mParent; }
-  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
-  void GetMimeType(nsAString& aMimeType) const  { aMimeType = mMimeType; }
-
-  CameraRecorderVideoProfile* Video()           { return mVideo; }
-  CameraRecorderAudioProfile* Audio()           { return mAudio; }
-
-  void GetName(nsAString& aName) const          { aName = mName; }
-
-  void
-  GetContainerFormat(nsAString& aContainerFormat) const
-  {
-    aContainerFormat = mContainerFormat;
-  }
-
-protected:
-  virtual ~CameraRecorderProfile();
-
-  nsCOMPtr<nsISupports> mParent;
-
-  const nsString mName;
-  const nsString mContainerFormat;
-  const nsString mMimeType;
-
-  nsRefPtr<CameraRecorderVideoProfile> mVideo;
-  nsRefPtr<CameraRecorderAudioProfile> mAudio;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfile);
-};
-
-/**
- * CameraRecorderProfiles
- */
-class CameraRecorderProfiles MOZ_FINAL : public nsISupports
-                                       , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfiles)
-
-  explicit CameraRecorderProfiles(nsISupports* aParent,
-                                  ICameraControl* aCameraControl);
-  nsISupports* GetParentObject() const { return mParent; }
-  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
-  CameraRecorderProfile* NamedGetter(const nsAString& aName, bool& aFound);
-  bool NameIsEnumerable(const nsAString& aName);
-  void GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames);
-
-protected:
-  virtual ~CameraRecorderProfiles();
-
-  nsCOMPtr<nsISupports> mParent;
-  nsRefPtr<ICameraControl> mCameraControl;
-  nsRefPtrHashtable<nsStringHashKey, CameraRecorderProfile> mProfiles;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfiles);
-};
-
-/**
- * CameraCapabilities
- */
 class CameraCapabilities MOZ_FINAL : public nsISupports
                                    , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraCapabilities)
 
   // Because this header's filename doesn't match its C++ or DOM-facing
   // classname, we can't rely on the [Func="..."] WebIDL tag to implicitly
   // include the right header for us; instead we must explicitly include a
   // HasSupport() method in each header. We can get rid of these with the
   // Great Renaming proposed in bug 983177.
   static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
 
-  explicit CameraCapabilities(nsPIDOMWindow* aWindow,
-                              ICameraControl* aCameraControl);
+  explicit CameraCapabilities(nsPIDOMWindow* aWindow);
+
+  // Populate the camera capabilities interface from the specific
+  // camera control object.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aCameraControl' is null.
+  nsresult Populate(ICameraControl* aCameraControl);
 
   nsPIDOMWindow* GetParentObject() const { return mWindow; }
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  void GetPreviewSizes(nsTArray<CameraSize>& aRetVal);
-  void GetPictureSizes(nsTArray<CameraSize>& aRetVal);
-  void GetThumbnailSizes(nsTArray<CameraSize>& aRetVal);
-  void GetVideoSizes(nsTArray<CameraSize>& aRetVal);
-  void GetFileFormats(nsTArray<nsString>& aRetVal);
-  void GetWhiteBalanceModes(nsTArray<nsString>& aRetVal);
-  void GetSceneModes(nsTArray<nsString>& aRetVal);
-  void GetEffects(nsTArray<nsString>& aRetVal);
-  void GetFlashModes(nsTArray<nsString>& aRetVal);
-  void GetFocusModes(nsTArray<nsString>& aRetVal);
-  void GetZoomRatios(nsTArray<double>& aRetVal);
-  uint32_t MaxFocusAreas();
-  uint32_t MaxMeteringAreas();
-  uint32_t MaxDetectedFaces();
-  double MinExposureCompensation();
-  double MaxExposureCompensation();
-  double ExposureCompensationStep();
-  void GetIsoModes(nsTArray<nsString>& aRetVal);
-
-  CameraRecorderProfiles* RecorderProfiles();
+  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;
+  uint32_t MaxDetectedFaces() const;
+  double MinExposureCompensation() const;
+  double MaxExposureCompensation() const;
+  double ExposureCompensationStep() const;
+  void GetRecorderProfiles(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) const;
+  void GetIsoModes(nsTArray<nsString>& aRetVal) const;
 
 protected:
   ~CameraCapabilities();
 
-  nsresult TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes);
+  nsresult TranslateToDictionary(ICameraControl* aCameraControl,
+                                 uint32_t aKey, nsTArray<CameraSize>& aSizes);
 
   nsTArray<CameraSize> mPreviewSizes;
   nsTArray<CameraSize> mPictureSizes;
   nsTArray<CameraSize> mThumbnailSizes;
   nsTArray<CameraSize> mVideoSizes;
 
   nsTArray<nsString> mFileFormats;
   nsTArray<nsString> mWhiteBalanceModes;
@@ -241,20 +98,18 @@ protected:
   uint32_t mMaxFocusAreas;
   uint32_t mMaxMeteringAreas;
   uint32_t mMaxDetectedFaces;
 
   double mMinExposureCompensation;
   double mMaxExposureCompensation;
   double mExposureCompensationStep;
 
+  nsRefPtr<RecorderProfileManager> mRecorderProfileManager;
+  JS::Heap<JS::Value> mRecorderProfiles;
+
   nsRefPtr<nsPIDOMWindow> mWindow;
-  nsRefPtr<ICameraControl> mCameraControl;
-  nsRefPtr<CameraRecorderProfiles> mRecorderProfiles;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(CameraCapabilities);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CameraCapabilities_h__
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -659,17 +659,22 @@ nsDOMCameraControl::SetOnFacesDetected(C
 }
 
 already_AddRefed<dom::CameraCapabilities>
 nsDOMCameraControl::Capabilities()
 {
   nsRefPtr<CameraCapabilities> caps = mCapabilities;
 
   if (!caps) {
-    caps = new CameraCapabilities(mWindow, mCameraControl);
+    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;
   }
 
   return caps.forget();
 }
 
 class ImmediateErrorCallback : public nsRunnable
 {
@@ -1130,19 +1135,20 @@ nsDOMCameraControl::DispatchStateEvent(c
 
 // 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:
-      DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n");
       {
         // The hardware is open, so we can return a camera to JS, even if
         // the preview hasn't started yet.
         nsRefPtr<Promise> promise = mGetCameraPromise.forget();
         if (promise) {
           CameraGetPromiseData data;
           data.mCamera = this;
           data.mConfiguration = *mCurrentConfiguration;
@@ -1153,17 +1159,16 @@ nsDOMCameraControl::OnHardwareStateChang
         if (cb) {
           ErrorResult ignored;
           cb->Call(*this, *mCurrentConfiguration, ignored);
         }
       }
       break;
 
     case CameraControlListener::kHardwareClosed:
-      DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
       {
         nsRefPtr<Promise> promise = mReleasePromise.forget();
         if (promise || mReleaseOnSuccessCb) {
           // If we have this event handler, this was a solicited hardware close.
           if (promise) {
             promise->MaybeResolve(JS::UndefinedHandleValue);
           }
           nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
@@ -1178,17 +1183,16 @@ nsDOMCameraControl::OnHardwareStateChang
             cb->Call(ignored);
           }
         }
         DispatchTrustedEvent(NS_LITERAL_STRING("close"));
       }
       break;
 
     default:
-      DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState);
       MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state");
   }
 }
 
 void
 nsDOMCameraControl::OnShutter()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -1393,17 +1397,17 @@ nsDOMCameraControl::OnAutoFocusMoving(bo
   if (aIsMoving) {
     DispatchStateEvent(NS_LITERAL_STRING("focus"), NS_LITERAL_STRING("focusing"));
   }
 }
 
 void
 nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
 {
-  DOM_CAMERA_LOGI("DOM OnFacesDetected %zu face(s)\n", aFaces.Length());
+  DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length());
   MOZ_ASSERT(NS_IsMainThread());
 
   Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;
   uint32_t len = aFaces.Length();
 
   if (faces.SetCapacity(len)) {
     nsRefPtr<DOMCameraDetectedFace> f;
     for (uint32_t i = 0; i < len; ++i) {
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -54,17 +54,17 @@ GetCameraLog()
 ::WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
 
 nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
   : mWindowId(aWindow->WindowID())
   , mPermission(nsIPermissionManager::DENY_ACTION)
   , mWindow(aWindow)
 {
   /* member initializers and constructor code */
-  DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\n", __func__, __LINE__, this, mWindowId);
+  DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
   MOZ_COUNT_CTOR(nsDOMCameraManager);
 }
 
 nsDOMCameraManager::~nsDOMCameraManager()
 {
   /* destructor code */
   MOZ_COUNT_DTOR(nsDOMCameraManager);
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
@@ -359,32 +359,32 @@ nsDOMCameraManager::PermissionCancelled(
     ErrorResult ignored;
     aOnError->Call(NS_LITERAL_STRING("Permission denied."), ignored);
   }
 }
 
 void
 nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
 {
-  DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\n", aDOMCameraControl, mWindowId);
+  DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId);
   MOZ_ASSERT(NS_IsMainThread());
 
   // Put the camera control into the hash table
   CameraControls* controls = sActiveWindows->Get(mWindowId);
   if (!controls) {
     controls = new CameraControls;
     sActiveWindows->Put(mWindowId, controls);
   }
   controls->AppendElement(aDOMCameraControl);
 }
 
 void
 nsDOMCameraManager::Shutdown(uint64_t aWindowId)
 {
-  DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\n", aWindowId);
+  DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId);
   MOZ_ASSERT(NS_IsMainThread());
 
   CameraControls* controls = sActiveWindows->Get(aWindowId);
   if (!controls) {
     return;
   }
 
   uint32_t length = controls->Length();
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -40,19 +40,16 @@ public:
   virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
 
   virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
 
   virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
 
-  virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
-  virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE { return nullptr; }
-
   nsresult PushParameters() { return NS_ERROR_NOT_INITIALIZED; }
   nsresult PullParameters() { return NS_ERROR_NOT_INITIALIZED; }
 
 protected:
   ~FallbackCameraControl();
 
   virtual nsresult StartPreviewImpl() { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult StopPreviewImpl() { return NS_ERROR_NOT_INITIALIZED; }
@@ -61,13 +58,14 @@ protected:
   virtual nsresult StopFaceDetectionImpl() { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult TakePictureImpl() { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                       const StartRecordingOptions* aOptions = nullptr) MOZ_OVERRIDE
                                         { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult StopRecordingImpl() { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult PushParametersImpl() { return NS_ERROR_NOT_INITIALIZED; }
   virtual nsresult PullParametersImpl() { return NS_ERROR_NOT_INITIALIZED; }
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE { return nullptr; }
 
 private:
   FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE;
   FallbackCameraControl& operator=(const FallbackCameraControl&) MOZ_DELETE;
 };
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -66,18 +66,21 @@ nsGonkCameraControl::nsGonkCameraControl
   , mLastThumbnailSize({0, 0})
   , mPreviewFps(30)
   , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
   , mFlashSupported(false)
   , mLuminanceSupported(false)
   , mAutoFlashModeOverridden(false)
   , mSeparateVideoAndPreviewSizesSupported(false)
   , mDeferConfigUpdate(0)
+  , mMediaProfiles(nullptr)
   , mRecorder(nullptr)
   , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
+  , mProfileManager(nullptr)
+  , mRecorderProfile(nullptr)
   , mVideoFile(nullptr)
   , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mImageContainer = LayerManager::CreateImageContainer();
 }
 
@@ -137,17 +140,16 @@ nsGonkCameraControl::Initialize()
 
   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_NOT_INITIALIZED;
   }
 
   DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get());
-  mCurrentConfiguration.mRecorderProfile.Truncate();
 
   // Initialize our camera configuration database.
   PullParametersImpl();
 
   // Set preferred preview frame format.
   mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp"));
   // Turn off any normal pictures returned by the HDR scene mode
   mParams.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE, false);
@@ -298,16 +300,19 @@ nsGonkCameraControl::SetConfigurationImp
   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);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
 
   DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
@@ -950,17 +955,17 @@ nsGonkCameraControl::SetupRecordingFlash
 nsresult
 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                         const StartRecordingOptions* aOptions)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
-  NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
+  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.
@@ -1358,35 +1363,42 @@ nsGonkCameraControl::GetSupportedSize(co
   return rv;
 }
 
 nsresult
 nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
-  RecorderProfile* profile;
-  if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) {
-    DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
-      NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get());
+  // read preferences for camcorder
+  mMediaProfiles = MediaProfiles::getInstance();
+
+  nsAutoCString profile = NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile);
+  mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get());
+  if (!mRecorderProfile) {
+    DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
     return NS_ERROR_INVALID_ARG;
   }
 
-  mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
-  const RecorderProfile::Video& video(profile->GetVideo());
-  const Size& size = video.GetSize();
-  int fps = video.GetFramesPerSecond();
-  if (fps <= 0 || size.width <= 0 || size.height <= 0) {
-    DOM_CAMERA_LOGE("Can't configure video with fps=%d, width=%d, height=%d\n",
-      fps, size.width, size.height);
+  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);
     return NS_ERROR_FAILURE;
   }
 
   PullParametersImpl();
 
+  Size size;
+  size.width = static_cast<uint32_t>(width);
+  size.height = static_cast<uint32_t>(height);
+
   {
     ICameraControlParameterSetAutoEnter set(this);
     nsresult rv;
 
     if (mSeparateVideoAndPreviewSizesSupported) {
       // The camera supports two video streams: a low(er) resolution preview
       // stream and and a potentially high(er) resolution stream for encoding. 
       rv = SetVideoSize(size);
@@ -1582,22 +1594,18 @@ nsGonkCameraControl::SetupRecording(int 
   const size_t SIZE = 256;
   char buffer[SIZE];
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
   mRecorder = new GonkRecorder();
   CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE);
 
-  nsresult rv =
-    GonkRecorderProfile::ConfigureRecorder(*mRecorder, mCameraId,
-                                           mCurrentConfiguration.mRecorderProfile);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   CHECK_SETARG_RETURN(mRecorder->setCamera(mCameraHw), NS_ERROR_FAILURE);
 
   DOM_CAMERA_LOGI("maxVideoLengthMs=%llu\n", aMaxVideoLengthMs);
   const uint64_t kMaxVideoLengthMs = INT64_MAX / 1000;
   if (aMaxVideoLengthMs == 0) {
     aMaxVideoLengthMs = -1;
   } else if (aMaxVideoLengthMs > kMaxVideoLengthMs) {
@@ -1658,86 +1666,37 @@ nsGonkCameraControl::StopImpl()
      mCameraHw->Close();
      mCameraHw.clear();
   }
 
   OnHardwareStateChange(CameraControlListener::kHardwareClosed);
   return NS_OK;
 }
 
-nsresult
-nsGonkCameraControl::LoadRecorderProfiles()
+already_AddRefed<GonkRecorderProfileManager>
+nsGonkCameraControl::GetGonkRecorderProfileManager()
 {
-  if (mRecorderProfiles.Count() == 0) {
-    nsTArray<nsRefPtr<RecorderProfile>> profiles;
-    nsresult rv = GonkRecorderProfile::GetAll(mCameraId, profiles);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
+  if (!mProfileManager) {
     nsTArray<Size> sizes;
-    rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
+    nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
+    NS_ENSURE_SUCCESS(rv, nullptr);
 
-    // Limit profiles to those video sizes supported by the camera hardware...
-    for (nsTArray<RecorderProfile>::size_type i = 0; i < profiles.Length(); ++i) {
-      int width = profiles[i]->GetVideo().GetSize().width; 
-      int height = profiles[i]->GetVideo().GetSize().height;
-      if (width < 0 || height < 0) {
-        DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n",
-          NS_ConvertUTF16toUTF8(profiles[i]->GetName()).get());
-        continue;
-      }
-      for (nsTArray<Size>::size_type n = 0; n < sizes.Length(); ++n) {
-        if (static_cast<uint32_t>(width) == sizes[n].width &&
-            static_cast<uint32_t>(height) == sizes[n].height) {
-          mRecorderProfiles.Put(profiles[i]->GetName(), profiles[i]);
-          break;
-        }
-      }
-    }
+    mProfileManager = new GonkRecorderProfileManager(mCameraId);
+    mProfileManager->SetSupportedResolutions(sizes);
   }
 
-  return NS_OK;
-}
-
-/* static */ PLDHashOperator
-nsGonkCameraControl::Enumerate(const nsAString& aProfileName,
-                               RecorderProfile* aProfile,
-                               void* aUserArg)
-{
-  nsTArray<nsString>* profiles = static_cast<nsTArray<nsString>*>(aUserArg);
-  MOZ_ASSERT(profiles);
-  profiles->AppendElement(aProfileName);
-  return PL_DHASH_NEXT;
+  nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
+  return profileMgr.forget();
 }
 
-nsresult
-nsGonkCameraControl::GetRecorderProfiles(nsTArray<nsString>& aProfiles)
+already_AddRefed<RecorderProfileManager>
+nsGonkCameraControl::GetRecorderProfileManagerImpl()
 {
-  nsresult rv = LoadRecorderProfiles();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  aProfiles.Clear();
-  mRecorderProfiles.EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
-  return NS_OK;
-}
-
-ICameraControl::RecorderProfile*
-nsGonkCameraControl::GetProfileInfo(const nsAString& aProfile)
-{
-  RecorderProfile* profile;
-  if (!mRecorderProfiles.Get(aProfile, &profile)) {
-    return nullptr;
-  }
-  return profile;
+  nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
+  return profileMgr.forget();
 }
 
 void
 nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
 {
   CameraControlImpl::OnRateLimitPreview(aLimit);
 }
 
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -13,17 +13,16 @@
  * 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 "nsRefPtrHashtable.h"
 #include <media/MediaProfiles.h>
 #include "mozilla/ReentrantMonitor.h"
 #include "DeviceStorage.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "GonkRecorder.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkCameraParameters.h"
@@ -75,20 +74,16 @@ public:
   virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE;
 
   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;
 
-  virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) MOZ_OVERRIDE;
-  virtual ICameraControl::RecorderProfile* 
-    GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE;
-
   nsresult PushParameters();
   nsresult PullParameters();
 
 protected:
   ~nsGonkCameraControl();
 
   using CameraControlImpl::OnRateLimitPreview;
   using CameraControlImpl::OnNewPreviewFrame;
@@ -120,30 +115,27 @@ protected:
   virtual nsresult StopFaceDetectionImpl() 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 ResumeContinuousFocusImpl() MOZ_OVERRIDE;
   virtual nsresult PushParametersImpl() MOZ_OVERRIDE;
   virtual nsresult PullParametersImpl() MOZ_OVERRIDE;
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE;
+  already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
 
   nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
                           uint64_t aMaxVideoLengthMs);
   nsresult SetupRecordingFlash(bool aAutoEnableLowLightTorch);
   nsresult SetPreviewSize(const Size& aSize);
   nsresult SetVideoSize(const Size& aSize);
   nsresult PausePreview();
   nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
 
-  nsresult LoadRecorderProfiles();
-  static PLDHashOperator Enumerate(const nsAString& aProfileName,
-                                   RecorderProfile* aProfile,
-                                   void* aUserArg);
-
   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);
 
@@ -160,24 +152,26 @@ protected:
   bool                      mLuminanceSupported;
   bool                      mAutoFlashModeOverridden;
   bool                      mSeparateVideoAndPreviewSizesSupported;
   Atomic<uint32_t>          mDeferConfigUpdate;
   GonkCameraParameters      mParams;
 
   nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
 
+  android::MediaProfiles*   mMediaProfiles;
   nsRefPtr<android::GonkRecorder> mRecorder;
   // Touching mRecorder happens inside this monitor because the destructor
   // can run on any thread, and we need to be able to clean up properly if
   // GonkCameraControl goes away.
   ReentrantMonitor          mRecorderMonitor;
 
-  // Supported recorder profiles
-  nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
+  // 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:
--- a/dom/camera/GonkRecorderProfiles.cpp
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -1,27 +1,26 @@
 /*
- * 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 "GonkRecorderProfiles.h"
 #include <media/MediaProfiles.h>
-#include "nsMimeTypes.h"
 #include "GonkRecorder.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace android;
 
 #define DEF_GONK_RECORDER_PROFILE(e, n) e##_INDEX,
@@ -34,323 +33,221 @@ enum {
 static struct {
   const char* name;
   int quality;
 } ProfileList[] = {
   #include "GonkRecorderProfiles.def"
   { nullptr, 0 }
 };
 
-/* static */ nsClassHashtable<nsUint32HashKey, ProfileHashtable> GonkRecorderProfile::sProfiles;
-/* static */ android::MediaProfiles* sMediaProfiles = nullptr;
+static MediaProfiles* sMediaProfiles = nullptr;
 
-static MediaProfiles*
-GetMediaProfiles()
+static bool
+IsQualitySupported(uint32_t aCameraId, uint32_t aQualityIndex)
+{
+  if (!sMediaProfiles) {
+    sMediaProfiles = MediaProfiles::getInstance();
+  }
+  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
+  return sMediaProfiles->hasCamcorderProfile(static_cast<int>(aCameraId), q);
+}
+
+static int
+GetProfileParam(uint32_t aCameraId, uint32_t aQualityIndex, const char* aParam)
 {
   if (!sMediaProfiles) {
     sMediaProfiles = MediaProfiles::getInstance();
   }
-  MOZ_ASSERT(sMediaProfiles);
-  return sMediaProfiles;
-}
-
-static bool
-IsProfileSupported(uint32_t aCameraId, uint32_t aProfileIndex)
-{
-  MediaProfiles* profiles = GetMediaProfiles();
-  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aProfileIndex].quality);
-  return profiles->hasCamcorderProfile(static_cast<int>(aCameraId), q);
-}
-
-static int
-GetProfileParameter(uint32_t aCameraId, uint32_t aProfileIndex, const char* aParameter)
-{
-  MediaProfiles* profiles = GetMediaProfiles();
-  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aProfileIndex].quality);
-  return profiles->getCamcorderProfileParamByName(aParameter, static_cast<int>(aCameraId), q);
-}
-
-/* static */ bool
-GonkRecorderVideo::Translate(video_encoder aCodec, nsAString& aCodecName)
-{
-  switch (aCodec) {
-    case VIDEO_ENCODER_H263:
-      aCodecName.AssignASCII("h263");
-      break;
-
-    case VIDEO_ENCODER_H264:
-      aCodecName.AssignASCII("h264");
-      break;
-
-    case VIDEO_ENCODER_MPEG_4_SP:
-      aCodecName.AssignASCII("mpeg4sp");
-      break;
-
-    default:
-      return false;
-  }
-
-  return true;
-}
-
-int
-GonkRecorderVideo::GetProfileParameter(const char* aParameter)
-{
-  return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
-}
-
-GonkRecorderVideo::GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex)
-  : mCameraId(aCameraId)
-  , mProfileIndex(aProfileIndex)
-  , mIsValid(false)
-{
-  mPlatformEncoder = static_cast<video_encoder>(GetProfileParameter("vid.codec"));
-  bool isValid = Translate(mPlatformEncoder, mCodec);
-
-  int v = GetProfileParameter("vid.width");
-  if (v >= 0) {
-    mSize.width = v;
-  } else {
-    isValid = false;
-  }
-  v = GetProfileParameter("vid.height");
-  if (v >= 0) {
-    mSize.height = v;
-  } else {
-    isValid = false;
-  }
-  v = GetProfileParameter("vid.bps");
-  if (v >= 0) {
-    mBitsPerSecond = v;
-  } else {
-    isValid = false;
-  }
-  v = GetProfileParameter("vid.fps");
-  if (v >= 0) {
-    mFramesPerSecond = v;
-  } else {
-    isValid = false;
-  }
-
-  mIsValid = isValid;
-}
-
-/* static */ bool
-GonkRecorderAudio::Translate(audio_encoder aCodec, nsAString& aCodecName)
-{
-  switch (aCodec) {
-    case AUDIO_ENCODER_AMR_NB:
-      aCodecName.AssignASCII("amrnb");
-      break;
-
-    case AUDIO_ENCODER_AMR_WB:
-      aCodecName.AssignASCII("amrwb");
-      break;
-
-    case AUDIO_ENCODER_AAC:
-      aCodecName.AssignASCII("aac");
-      break;
-
-    default:
-      return false;
-  }
-
-  return true;
-}
-
-int
-GonkRecorderAudio::GetProfileParameter(const char* aParameter)
-{
-  return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
+  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
+  return sMediaProfiles->getCamcorderProfileParamByName(aParam, static_cast<int>(aCameraId), q);
 }
 
-GonkRecorderAudio::GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex)
-  : mCameraId(aCameraId)
-  , mProfileIndex(aProfileIndex)
-  , mIsValid(false)
-{
-  mPlatformEncoder = static_cast<audio_encoder>(GetProfileParameter("aud.codec"));
-  bool isValid = Translate(mPlatformEncoder, mCodec);
-
-  int v = GetProfileParameter("aud.ch");
-  if (v >= 0) {
-    mChannels = v;
-  } else {
-    isValid = false;
-  }
-  v = GetProfileParameter("aud.bps");
-  if (v >= 0) {
-    mBitsPerSecond = v;
-  } else {
-    isValid = false;
-  }
-  v = GetProfileParameter("aud.hz");
-  if (v >= 0) {
-    mSamplesPerSecond = v;
-  } else {
-    isValid = false;
-  }
-
-  mIsValid = isValid;
-}
-
-/* static */ bool
-GonkRecorderProfile::Translate(output_format aContainer, nsAString& aContainerName)
+/**
+ * Recorder profile.
+ */
+static RecorderProfile::FileFormat
+TranslateFileFormat(output_format aFileFormat)
 {
-  switch (aContainer) {
-    case OUTPUT_FORMAT_THREE_GPP:
-      aContainerName.AssignASCII("3gp");
-      break;
-
-    case OUTPUT_FORMAT_MPEG_4:
-      aContainerName.AssignASCII("mp4");
-      break;
-
-    default:
-      return false;
+  switch (aFileFormat) {
+    case OUTPUT_FORMAT_THREE_GPP: return RecorderProfile::THREE_GPP;
+    case OUTPUT_FORMAT_MPEG_4:    return RecorderProfile::MPEG4;
+    default:                      return RecorderProfile::UNKNOWN;
   }
-
-  return true;
-}
-
-/* static */ bool
-GonkRecorderProfile::GetMimeType(output_format aContainer, nsAString& aMimeType)
-{
-  switch (aContainer) {
-    case OUTPUT_FORMAT_THREE_GPP:
-      aMimeType.AssignASCII(VIDEO_3GPP);
-      break;
-
-    case OUTPUT_FORMAT_MPEG_4:
-      aMimeType.AssignASCII(VIDEO_MP4);
-      break;
-
-    default:
-      return false;
-  }
-
-  return true;
 }
 
-int
-GonkRecorderProfile::GetProfileParameter(const char* aParameter)
-{
-  return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
-}
-
-GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId,
-                                         uint32_t aProfileIndex,
-                                         const nsAString& aName)
-  : GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>(aCameraId,
-                                                                  aProfileIndex, aName)
-  , mCameraId(aCameraId)
-  , mProfileIndex(aProfileIndex)
-  , mIsValid(false)
+GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>(aCameraId, aQualityIndex)
 {
-  mOutputFormat = static_cast<output_format>(GetProfileParameter("file.format"));
-  bool isValid = Translate(mOutputFormat, mContainer);
-  isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false;
-
-  mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid();
-}
-
-/* static */ PLDHashOperator
-GonkRecorderProfile::Enumerate(const nsAString& aProfileName,
-                               GonkRecorderProfile* aProfile,
-                               void* aUserArg)
-{
-  nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>* profiles =
-    static_cast<nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>*>(aUserArg);
-  MOZ_ASSERT(profiles);
-  profiles->AppendElement(aProfile);
-  return PL_DHASH_NEXT;
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformOutputFormat = static_cast<output_format>(GetProfileParam(mCameraId, mQualityIndex, "file.format"));
+  mFileFormat = TranslateFileFormat(mPlatformOutputFormat);
+  if (aQualityIndex < PROFILE_COUNT) {
+    mName = ProfileList[aQualityIndex].name;
+    DOM_CAMERA_LOGI("Created camera %d profile index %d: '%s'\n", mCameraId, mQualityIndex, mName);
+  }
 }
 
-/* static */
-ProfileHashtable*
-GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId)
+GonkRecorderProfile::~GonkRecorderProfile()
 {
-  ProfileHashtable* profiles = sProfiles.Get(aCameraId);
-  if (!profiles) {
-    profiles = new ProfileHashtable;
-    sProfiles.Put(aCameraId, profiles);
-
-    for (uint32_t i = 0; ProfileList[i].name; ++i) {
-      if (IsProfileSupported(aCameraId, i)) {
-        DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", i, ProfileList[i].name);
-        nsAutoString name;
-        name.AssignASCII(ProfileList[i].name);
-        nsRefPtr<GonkRecorderProfile> profile = new GonkRecorderProfile(aCameraId, i, name);
-        if (!profile->IsValid()) {
-          DOM_CAMERA_LOGE("Profile %d '%s' is not valid\n", i, ProfileList[i].name);
-          continue;
-        }
-        profiles->Put(name, profile);
-      } else {
-        DOM_CAMERA_LOGI("Profile %d '%s' not supported by platform\n", i, ProfileList[i].name);
-      }
-    }
-  }
-  return profiles;
-}
-
-/* static */ nsresult
-GonkRecorderProfile::GetAll(uint32_t aCameraId,
-                            nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles)
-{
-  ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
-  if (!profiles) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aProfiles.Clear();
-  profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
-  
-  return NS_OK;
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 nsresult
-GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
+GonkRecorderProfile::ConfigureRecorder(GonkRecorder* aRecorder)
 {
+  if (!aRecorder) {
+    DOM_CAMERA_LOGW("ConfigureRecorder() called with null aRecorder\n");
+    return NS_ERROR_INVALID_ARG;
+  }
+
   static const size_t SIZE = 256;
   char buffer[SIZE];
 
   // set all the params
-  CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
-  CHECK_SETARG(aRecorder.setVideoSource(VIDEO_SOURCE_CAMERA));
-  CHECK_SETARG(aRecorder.setOutputFormat(mOutputFormat));
-  CHECK_SETARG(aRecorder.setVideoFrameRate(mVideo.GetFramesPerSecond()));
-  CHECK_SETARG(aRecorder.setVideoSize(mVideo.GetSize().width, mVideo.GetSize().height));
-  CHECK_SETARG(aRecorder.setVideoEncoder(mVideo.GetPlatformEncoder()));
-  CHECK_SETARG(aRecorder.setAudioEncoder(mAudio.GetPlatformEncoder()));
+  CHECK_SETARG(aRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER));
+  CHECK_SETARG(aRecorder->setVideoSource(VIDEO_SOURCE_CAMERA));
+  CHECK_SETARG(aRecorder->setOutputFormat(GetOutputFormat()));
+  CHECK_SETARG(aRecorder->setVideoFrameRate(mVideo.GetFramerate()));
+  CHECK_SETARG(aRecorder->setVideoSize(mVideo.GetWidth(), mVideo.GetHeight()));
+  CHECK_SETARG(aRecorder->setVideoEncoder(mVideo.GetPlatformCodec()));
+  CHECK_SETARG(aRecorder->setAudioEncoder(mAudio.GetPlatformCodec()));
 
-  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitsPerSecond());
-  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
+  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitrate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
 
-  snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitsPerSecond());
-  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
+  snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitrate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
 
   snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels());
-  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
 
-  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond());
-  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
+  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplerate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
 
   return NS_OK;
 }
 
-/* static */ nsresult
-GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder,
-                                       uint32_t aCameraId,
-                                       const nsAString& aProfileName)
+/**
+ * Recorder audio profile.
+ */
+static RecorderAudioProfile::Codec
+TranslateAudioCodec(audio_encoder aCodec)
+{
+  switch (aCodec) {
+    case AUDIO_ENCODER_AMR_NB:  return RecorderAudioProfile::AMRNB;
+    case AUDIO_ENCODER_AMR_WB:  return RecorderAudioProfile::AMRWB;
+    case AUDIO_ENCODER_AAC:     return RecorderAudioProfile::AAC;
+    default:                    return RecorderAudioProfile::UNKNOWN;
+  }
+}
+
+GonkRecorderAudioProfile::GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderAudioProfile(aCameraId, aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformCodec = static_cast<audio_encoder>(GetProfileParam(mCameraId, mQualityIndex, "aud.codec"));
+  mCodec = TranslateAudioCodec(mPlatformCodec);
+  mBitrate = GetProfileParam(mCameraId, mQualityIndex, "aud.bps");
+  mSamplerate = GetProfileParam(mCameraId, mQualityIndex, "aud.hz");
+  mChannels = GetProfileParam(mCameraId, mQualityIndex, "aud.ch");
+}
+
+GonkRecorderAudioProfile::~GonkRecorderAudioProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+/**
+ * Recorder video profile.
+ */
+static RecorderVideoProfile::Codec
+TranslateVideoCodec(video_encoder aCodec)
 {
-  ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
-  if (!profiles) {
-    return NS_ERROR_FAILURE;
+  switch (aCodec) {
+    case VIDEO_ENCODER_H263:      return RecorderVideoProfile::H263;
+    case VIDEO_ENCODER_H264:      return RecorderVideoProfile::H264;
+    case VIDEO_ENCODER_MPEG_4_SP: return RecorderVideoProfile::MPEG4SP;
+    default:                      return RecorderVideoProfile::UNKNOWN;
+  }
+}
+
+GonkRecorderVideoProfile::GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderVideoProfile(aCameraId, aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformCodec = static_cast<video_encoder>(GetProfileParam(mCameraId, mQualityIndex, "vid.codec"));
+  mCodec = TranslateVideoCodec(mPlatformCodec);
+  mBitrate = GetProfileParam(mCameraId, mQualityIndex, "vid.bps");
+  mFramerate = GetProfileParam(mCameraId, mQualityIndex, "vid.fps");
+  mWidth = GetProfileParam(mCameraId, mQualityIndex, "vid.width");
+  mHeight = GetProfileParam(mCameraId, mQualityIndex, "vid.height");
+}
+
+GonkRecorderVideoProfile::~GonkRecorderVideoProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+GonkRecorderProfileManager::GonkRecorderProfileManager(uint32_t aCameraId)
+  : RecorderProfileManager(aCameraId)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  mMaxQualityIndex = sizeof(ProfileList) / sizeof(ProfileList[0]) - 1;
+}
+
+GonkRecorderProfileManager::~GonkRecorderProfileManager()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+bool
+GonkRecorderProfileManager::IsSupported(uint32_t aQualityIndex) const
+{
+  if (!IsQualitySupported(mCameraId, aQualityIndex)) {
+    // This profile is not supported
+    return false;
   }
 
-  GonkRecorderProfile* profile;
-  if (!profiles->Get(aProfileName, &profile)) {
-    return NS_ERROR_INVALID_ARG;
+  int width = GetProfileParam(mCameraId, aQualityIndex, "vid.width");
+  int height = GetProfileParam(mCameraId, aQualityIndex, "vid.height");
+  if (width == -1 || height == -1) {
+    // This would be unexpected, but we handle it just in case
+    DOM_CAMERA_LOGE("Camera %d recorder profile %d has width=%d, height=%d\n", mCameraId, aQualityIndex, width, height);
+    return false;
   }
 
-  return profile->ConfigureRecorder(aRecorder);
+  for (uint32_t i = 0; i < mSupportedSizes.Length(); ++i) {
+    if (static_cast<uint32_t>(width) == mSupportedSizes[i].width &&
+      static_cast<uint32_t>(height) == mSupportedSizes[i].height)
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
+already_AddRefed<RecorderProfile>
+GonkRecorderProfileManager::Get(uint32_t aQualityIndex) const
+{
+  // This overrides virtual RecorderProfileManager::Get(...)
+  DOM_CAMERA_LOGT("%s:%d : aQualityIndex=%d\n", __func__, __LINE__, aQualityIndex);
+  nsRefPtr<RecorderProfile> profile = new GonkRecorderProfile(mCameraId, aQualityIndex);
+  return profile.forget();
 }
+
+already_AddRefed<GonkRecorderProfile>
+GonkRecorderProfileManager::Get(const char* aProfileName) const
+{
+  DOM_CAMERA_LOGT("%s:%d : aProfileName='%s'\n", __func__, __LINE__, aProfileName);
+  for (uint32_t i = 0; i < mMaxQualityIndex; ++i) {
+    if (strcmp(ProfileList[i].name, aProfileName) == 0) {
+      nsRefPtr<GonkRecorderProfile> profile = nullptr;
+      if (IsSupported(i)) {
+        profile = new GonkRecorderProfile(mCameraId, i);
+        return profile.forget();
+      }
+      return nullptr;
+    }
+  }
+
+  DOM_CAMERA_LOGW("Couldn't file recorder profile named '%s'\n", aProfileName);
+  return nullptr;
+}
--- a/dom/camera/GonkRecorderProfiles.h
+++ b/dom/camera/GonkRecorderProfiles.h
@@ -1,16 +1,17 @@
 /* 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_GONK_RECORDER_PROFILES_H
 #define DOM_CAMERA_GONK_RECORDER_PROFILES_H
 
 #include <media/MediaProfiles.h>
+#include "CameraRecorderProfiles.h"
 #include "ICameraControl.h"
 
 #ifndef CHECK_SETARG_RETURN
 #define CHECK_SETARG_RETURN(x, rv)      \
   do {                                  \
     if (x) {                            \
       DOM_CAMERA_LOGE(#x " failed\n");  \
       return rv;                        \
@@ -18,132 +19,104 @@
   } while(0)
 #endif
 
 #ifndef CHECK_SETARG
 #define CHECK_SETARG(x) CHECK_SETARG_RETURN(x, NS_ERROR_NOT_AVAILABLE)
 #endif
 
 namespace android {
-  class GonkRecorder;
+class GonkRecorder;
 };
 
 namespace mozilla {
 
 /**
- * class GonkRecorderProfileBase
+ * Gonk-specific video profile.
  */
-template<class A, class V>
-class GonkRecorderProfileBase : public ICameraControl::RecorderProfile
+class GonkRecorderVideoProfile : public RecorderVideoProfile
 {
 public:
-  GonkRecorderProfileBase(uint32_t aCameraId, uint32_t aProfileIndex, const nsAString& aName)
-    : RecorderProfile(aName)
-    , mAudio(aCameraId, aProfileIndex)
-    , mVideo(aCameraId, aProfileIndex)
-  { }
-
-  virtual const Audio& GetAudio() const MOZ_OVERRIDE { return mAudio; }
-  virtual const Video& GetVideo() const MOZ_OVERRIDE { return mVideo; }
+  GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  ~GonkRecorderVideoProfile();
+  android::video_encoder GetPlatformCodec() const { return mPlatformCodec; }
 
 protected:
-  virtual ~GonkRecorderProfileBase() { }
-  A mAudio;
-  V mVideo;
+  android::video_encoder mPlatformCodec;
 };
 
 /**
- * class GonkRecorderVideo
+ * Gonk-specific audio profile.
  */
-class GonkRecorderVideo : public ICameraControl::RecorderProfile::Video
+class GonkRecorderAudioProfile : public RecorderAudioProfile
 {
 public:
-  GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex);
-  virtual ~GonkRecorderVideo() { }
-
-  android::video_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
-  bool IsValid() const { return mIsValid; }
+  GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  ~GonkRecorderAudioProfile();
+  android::audio_encoder GetPlatformCodec() const { return mPlatformCodec; }
 
 protected:
-  int GetProfileParameter(const char* aParameter);
-  static bool Translate(android::video_encoder aCodec, nsAString& aCodecName);
-
-  uint32_t mCameraId;
-  uint32_t mProfileIndex;
-  bool mIsValid;
-  android::video_encoder mPlatformEncoder;
+  android::audio_encoder mPlatformCodec;
 };
 
 /**
- * class GonkRecorderAudio
+ * Gonk-specific recorder profile.
  */
-class GonkRecorderAudio : public ICameraControl::RecorderProfile::Audio
+class GonkRecorderProfile : public RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>
 {
 public:
-  GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex);
-  virtual ~GonkRecorderAudio() { }
+  GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+
+  GonkRecorderAudioProfile* GetGonkAudioProfile() { return &mAudio; }
+  GonkRecorderVideoProfile* GetGonkVideoProfile() { return &mVideo; }
+
+  android::output_format GetOutputFormat() const { return mPlatformOutputFormat; }
 
-  android::audio_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
-  bool IsValid() const { return mIsValid; }
+  // Configures the specified recorder using this profile.
+  //
+  // Return values:
+  //  - NS_OK on success;
+  //  - NS_ERROR_INVALID_ARG if 'aRecorder' is null;
+  //  - NS_ERROR_NOT_AVAILABLE if the recorder rejected this profile.
+  nsresult ConfigureRecorder(android::GonkRecorder* aRecorder);
 
 protected:
-  int GetProfileParameter(const char* aParameter);
-  static bool Translate(android::audio_encoder aCodec, nsAString& aCodecName);
+  virtual ~GonkRecorderProfile();
 
-  uint32_t mCameraId;
-  uint32_t mProfileIndex;
-  bool mIsValid;
-  android::audio_encoder mPlatformEncoder;
+  android::output_format mPlatformOutputFormat;
 };
 
 /**
- * class GonkRecorderProfile
+ * Gonk-specific profile manager.
  */
-typedef nsRefPtrHashtable<nsStringHashKey, GonkRecorderProfile> ProfileHashtable;
-
-class GonkRecorderProfile
-  : public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
+class GonkRecorderProfileManager : public RecorderProfileManager
 {
 public:
-  static nsresult GetAll(uint32_t aCameraId,
-                         nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles);
+  GonkRecorderProfileManager(uint32_t aCameraId);
+
+  /**
+   * Call this function to indicate that the specified resolutions are in fact
+   * supported by the camera hardware.  (Just because it appears in a recorder
+   * profile doesn't mean the hardware can handle it.)
+   */
+  void SetSupportedResolutions(const nsTArray<ICameraControl::Size>& aSizes)
+    { mSupportedSizes = aSizes; }
 
-  // Configures the specified recorder using the specified profile.
-  //
-  // Return values:
-  //  - NS_OK on success;
-  //  - NS_ERROR_INVALID_ARG if the profile isn't supported;
-  //  - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile.
-  static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder,
-                                    uint32_t aCameraId,
-                                    const nsAString& aProfileName);
+  /**
+   * Call this function to remove all resolutions set by calling
+   * SetSupportedResolutions().
+   */
+  void ClearSupportedResolutions() { mSupportedSizes.Clear(); }
+
+  bool IsSupported(uint32_t aQualityIndex) const;
+
+  already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const;
+  already_AddRefed<GonkRecorderProfile> Get(const char* aProfileName) const;
 
 protected:
-  GonkRecorderProfile(uint32_t aCameraId,
-                      uint32_t aProfileIndex,
-                      const nsAString& aName);
-
-  int GetProfileParameter(const char* aParameter);
-
-  bool Translate(android::output_format aContainer, nsAString& aContainerName);
-  bool GetMimeType(android::output_format aContainer, nsAString& aMimeType);
-  bool IsValid() const { return mIsValid; };
+  virtual ~GonkRecorderProfileManager();
 
-  nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
-  static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
-  static PLDHashOperator Enumerate(const nsAString& aProfileName,
-                                   GonkRecorderProfile* aProfile,
-                                   void* aUserArg);
-
-  uint32_t mCameraId;
-  uint32_t mProfileIndex;
-  bool mIsValid;
-  android::output_format mOutputFormat;
-
-  static nsClassHashtable<nsUint32HashKey, ProfileHashtable> sProfiles;
-
-private:
-  DISALLOW_EVIL_CONSTRUCTORS(GonkRecorderProfile);
+  nsTArray<ICameraControl::Size> mSupportedSizes;
 };
 
 }; // namespace mozilla
 
 #endif // DOM_CAMERA_GONK_RECORDER_PROFILES_H
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -4,25 +4,25 @@
 
 #ifndef DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "nsISupportsImpl.h"
-#include "base/basictypes.h"
 
 struct DeviceStorageFileDescriptor;
 
 class nsIFile;
 
 namespace mozilla {
 
 class CameraControlListener;
+class RecorderProfileManager;
 
 // XXXmikeh - In some future patch this should move into the ICameraControl
 //  class as well, and the names updated to the 'k'-style;
 //  e.g. kParamPreviewSize, etc.
 enum {
   // current settings
   CAMERA_PARAM_PREVIEWSIZE,
   CAMERA_PARAM_PREVIEWFORMAT,
@@ -157,86 +157,16 @@ public:
     bool      hasLeftEye;
     Point     leftEye;
     bool      hasRightEye;
     Point     rightEye;
     bool      hasMouth;
     Point     mouth;
   };
 
-  class RecorderProfile
-  {
-  public:
-    class Video
-    {
-    public:
-      Video() { }
-      virtual ~Video() { }
-
-      const nsString& GetCodec() const    { return mCodec; }
-      const Size& GetSize() const         { return mSize; }
-      uint32_t GetBitsPerSecond() const   { return mBitsPerSecond; }
-      uint32_t GetFramesPerSecond() const { return mFramesPerSecond; }
-
-    protected:
-      nsString  mCodec;
-      Size      mSize;
-      uint32_t  mBitsPerSecond;
-      uint32_t  mFramesPerSecond;
-
-    private:
-      DISALLOW_EVIL_CONSTRUCTORS(Video);
-    };
-
-    class Audio
-    {
-    public:
-      Audio() { }
-      virtual ~Audio() { }
-
-      const nsString& GetCodec() const    { return mCodec; }
-
-      uint32_t GetChannels() const        { return mChannels; }
-      uint32_t GetBitsPerSecond() const   { return mBitsPerSecond; }
-      uint32_t GetSamplesPerSecond() const { return mSamplesPerSecond; }
-
-    protected:
-      nsString  mCodec;
-      uint32_t  mChannels;
-      uint32_t  mBitsPerSecond;
-      uint32_t  mSamplesPerSecond;
-
-    private:
-      DISALLOW_EVIL_CONSTRUCTORS(Audio);
-    };
-
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
-
-    RecorderProfile(const nsAString& aName)
-      : mName(aName)
-    { }
-
-    const nsString& GetName() const       { return mName; }
-    const nsString& GetContainer() const  { return mContainer; }
-    const nsString& GetMimeType() const   { return mMimeType; }
-
-    virtual const Video& GetVideo() const = 0;
-    virtual const Audio& GetAudio() const = 0;
-
-  protected:
-    virtual ~RecorderProfile() { }
-
-    nsString    mName;
-    nsString    mContainer;
-    nsString    mMimeType;
-
-  private:
-    DISALLOW_EVIL_CONSTRUCTORS(RecorderProfile);
-  };
-
   static already_AddRefed<ICameraControl> Create(uint32_t aCameraId);
 
   virtual void AddListener(CameraControlListener* aListener) = 0;
   virtual void RemoveListener(CameraControlListener* aListener) = 0;
 
   // Camera control methods.
   //
   // Return values:
@@ -285,19 +215,17 @@ public:
   virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) = 0;
 
   virtual nsresult SetLocation(const Position& aLocation) = 0;
 
   virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) = 0;
   virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) = 0;
   virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) = 0;
 
-  virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) = 0;
-  virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) = 0;
-
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
   virtual uint32_t GetCameraId() = 0;
 
   virtual void Shutdown() = 0;
 
 protected:
   virtual ~ICameraControl() { }
 
   friend class ICameraControlParameterSetAutoEnter;
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -12,16 +12,17 @@ EXPORTS += [
     'CameraPreferences.h',
     'DOMCameraManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'CameraControlImpl.cpp',
     'CameraPreferences.cpp',
     'CameraPreviewMediaStream.cpp',
+    'CameraRecorderProfiles.cpp',
     'DOMCameraCapabilities.cpp',
     'DOMCameraControl.cpp',
     'DOMCameraControlListener.cpp',
     'DOMCameraDetectedFace.cpp',
     'DOMCameraManager.cpp',
 ]
 
 if CONFIG['MOZ_B2G_CAMERA']:
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -207,24 +207,16 @@ var interfaceNamesInGlobalScope =
     {name: "CameraControl", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraDetectedFace", b2g: true, pref: "camera.control.face_detection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraFacesDetectedEvent", b2g: true, pref: "camera.control.face_detection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraManager", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "CameraRecorderAudioProfile", b2g: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "CameraRecorderProfile", b2g: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "CameraRecorderProfiles", b2g: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "CameraRecorderVideoProfile", b2g: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraStateChangeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasGradient",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasPattern",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasRenderingContext2D",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/CameraCapabilities.webidl
+++ b/dom/webidl/CameraCapabilities.webidl
@@ -1,64 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-/* The capabilities of the video recorder. These are guaranteed not to change
-   over the lifetime of that partcicular instance.
-*/
-[Func="CameraCapabilities::HasSupport"]
-interface CameraRecorderAudioProfile
-{
-  [Constant, Cached] readonly attribute DOMString codec;
-  [Constant, Cached] readonly attribute unsigned long bitsPerSecond;
-  [Constant, Cached] readonly attribute unsigned long samplesPerSecond;
-  [Constant, Cached] readonly attribute unsigned long channels;
-
-  jsonifier;
-};
-
-[Func="CameraCapabilities::HasSupport"]
-interface CameraRecorderVideoProfile
-{
-  [Constant, Cached] readonly attribute DOMString codec;
-  [Constant, Cached] readonly attribute unsigned long bitsPerSecond;
-  [Constant, Cached] readonly attribute unsigned long framesPerSecond;
-  [Constant, Cached] readonly attribute CameraSize size;
-
-  [Constant, Cached] readonly attribute unsigned long width;
-  [Constant, Cached] readonly attribute unsigned long height;
-
-  jsonifier;
-};
-
-[Func="CameraCapabilities::HasSupport"]
-interface CameraRecorderProfile
-{
-  [Constant, Cached] readonly attribute DOMString name;
-  [Constant, Cached] readonly attribute DOMString containerFormat;
-  [Constant, Cached] readonly attribute DOMString mimeType;
-
-  [Constant, Cached] readonly attribute CameraRecorderAudioProfile audio;
-  [Constant, Cached] readonly attribute CameraRecorderVideoProfile video;
-
-  jsonifier;
-};
-
-[Func="CameraCapabilities::HasSupport"]
-interface CameraRecorderProfiles
-{
-  getter CameraRecorderProfile(DOMString profile);
-
-  jsonifier;
-};
-
 /* The capabilities of a CameraControl instance. These are guaranteed
    not to change over the lifetime of that particular instance.
 */
 [Func="CameraCapabilities::HasSupport"]
 interface CameraCapabilities
 {
   [Constant, Cached] readonly attribute sequence<CameraSize> previewSizes;
   [Constant, Cached] readonly attribute sequence<CameraSize> pictureSizes;
@@ -78,14 +29,12 @@ interface CameraCapabilities
   [Constant, Cached] readonly attribute unsigned long maxFocusAreas;
   [Constant, Cached] readonly attribute unsigned long maxMeteringAreas;
   [Constant, Cached] readonly attribute unsigned long maxDetectedFaces;
 
   [Constant, Cached] readonly attribute double minExposureCompensation;
   [Constant, Cached] readonly attribute double maxExposureCompensation;
   [Constant, Cached] readonly attribute double exposureCompensationStep;
 
-  [Constant, Cached] readonly attribute CameraRecorderProfiles recorderProfiles;
+  [Constant, Cached] readonly attribute any recorderProfiles;
 
   [Constant, Cached] readonly attribute sequence<DOMString> isoModes;
-
-  jsonifier;
 };