Bug 1020368 - remove direct JS_*() calls from CameraRecorderProfiles, r=aosmond,bz
authorMike Habicher <mikeh@mozilla.com>
Fri, 31 Oct 2014 13:19:06 -0400
changeset 229950 95be75403d706081b8508b91f6e60c7846af125d
parent 229949 952aaa4a6b5ff610da2917dcc3d0826ab41834b8
child 229951 f1c77e82a02cbf984e7c53ede93b64259776ccee
push id7326
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:58:42 +0000
treeherdermozilla-aurora@d3a3b2a0f2f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaosmond, bz
bugs1020368
milestone36.0a1
Bug 1020368 - remove direct JS_*() calls from CameraRecorderProfiles, r=aosmond,bz
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,16 +203,32 @@ 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,17 +3,16 @@
  * 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;
@@ -60,22 +59,16 @@ 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)
@@ -109,17 +102,17 @@ CameraControlImpl::OnHardwareStateChange
 }
 
 void
 CameraControlImpl::OnConfigurationChange()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   RwLockAutoEnterRead lock(mListenerLock);
 
-  DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length());
+  DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnConfigurationChange(mCurrentConfiguration);
   }
 }
 
 void
@@ -264,17 +257,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 %d preview frame listener(s)\n",
+  DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu 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,18 +20,16 @@
 #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.
@@ -44,17 +42,16 @@ 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);
@@ -123,18 +120,16 @@ 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
   {
-    kHardwareOpen,
-    kHardwareClosed
+    kHardwareClosed,
+    kHardwareOpen
   };
   virtual void OnHardwareStateChange(HardwareState aState) { }
 
   enum PreviewState
   {
     kPreviewStopped,
     kPreviewPaused,
     kPreviewStarted
deleted file mode 100644
--- a/dom/camera/CameraRecorderProfiles.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "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;
-}
deleted file mode 100644
--- a/dom/camera/CameraRecorderProfiles.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef DOM_CAMERA_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,68 +7,246 @@
 #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 {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities)
+/**
+ * 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_UNLINK_BEGIN(CameraCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-  tmp->mRecorderProfiles = JS::UndefinedValue();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_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_TRAVERSE_BEGIN(CameraCapabilities)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_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_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
+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_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)
-  : mRecorderProfiles(JS::UndefinedValue())
+CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow,
+                                       ICameraControl* aCameraControl)
+  : mMaxFocusAreas(0)
+  , mMaxMeteringAreas(0)
+  , mMaxDetectedFaces(0)
+  , mMinExposureCompensation(0.0)
+  , mMaxExposureCompensation(0.0)
+  , mExposureCompensationStep(0.0)
   , 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);
 }
@@ -77,234 +255,246 @@ CameraCapabilities::WrapObject(JSContext
   do {                                                        \
     if (NS_FAILED(rv)) {                                      \
       DOM_CAMERA_LOGW("Error %x trying to get " #param "\n",  \
         (rv));                                                \
     }                                                         \
   } while(0)
 
 nsresult
-CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl,
-                                          uint32_t aKey, nsTArray<CameraSize>& aSizes)
+CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes)
 {
   nsresult rv;
   nsTArray<ICameraControl::Size> sizes;
 
-  rv = aCameraControl->Get(aKey, sizes);
+  rv = mCameraControl->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;
 }
 
-nsresult
-CameraCapabilities::Populate(ICameraControl* aCameraControl)
+void
+CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval)
 {
-  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);
-    }
+  if (mPreviewSizes.Length() == 0) {
+    nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
+                                        mPreviewSizes);
+    LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
   }
-
-  // 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) const
+CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval)
 {
+  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) const
+CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval)
 {
+  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) const
+CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval)
 {
+  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) const
+CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetEffects(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval)
 {
+  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) const
+CameraCapabilities::GetZoomRatios(nsTArray<double>& retval)
 {
+  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() const
+CameraCapabilities::MaxFocusAreas()
 {
+  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() const
+CameraCapabilities::MaxMeteringAreas()
 {
+  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() const
+CameraCapabilities::MaxDetectedFaces()
 {
+  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() const
+CameraCapabilities::MinExposureCompensation()
 {
+  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() const
+CameraCapabilities::MaxExposureCompensation()
 {
+  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() const
+CameraCapabilities::ExposureCompensationStep()
 {
+  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()
+{
+  nsRefPtr<CameraRecorderProfiles> profiles = mRecorderProfiles;
+  if (!mRecorderProfiles) {
+    profiles = new CameraRecorderProfiles(this, mCameraControl);
+    mRecorderProfiles = profiles;
+  }
+  return profiles;
+}
+
 void
-CameraCapabilities::GetRecorderProfiles(JSContext* aCx,
-                                        JS::MutableHandle<JS::Value> aRetval) const
+CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval)
 {
-  JS::ExposeValueToActiveJS(mRecorderProfiles);
-  aRetval.set(mRecorderProfiles);
-}
-
-void
-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,86 +4,229 @@
  * 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);
-
-  // 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);
+  explicit CameraCapabilities(nsPIDOMWindow* aWindow,
+                              ICameraControl* aCameraControl);
 
   nsPIDOMWindow* GetParentObject() const { return mWindow; }
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  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;
+  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();
 
 protected:
   ~CameraCapabilities();
 
-  nsresult TranslateToDictionary(ICameraControl* aCameraControl,
-                                 uint32_t aKey, nsTArray<CameraSize>& aSizes);
+  nsresult TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes);
 
   nsTArray<CameraSize> mPreviewSizes;
   nsTArray<CameraSize> mPictureSizes;
   nsTArray<CameraSize> mThumbnailSizes;
   nsTArray<CameraSize> mVideoSizes;
 
   nsTArray<nsString> mFileFormats;
   nsTArray<nsString> mWhiteBalanceModes;
@@ -98,18 +241,20 @@ 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;
 
-  nsRefPtr<nsPIDOMWindow> mWindow;
+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,22 +659,17 @@ nsDOMCameraControl::SetOnFacesDetected(C
 }
 
 already_AddRefed<dom::CameraCapabilities>
 nsDOMCameraControl::Capabilities()
 {
   nsRefPtr<CameraCapabilities> caps = mCapabilities;
 
   if (!caps) {
-    caps = new CameraCapabilities(mWindow);
-    nsresult rv = caps->Populate(mCameraControl);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv);
-      return nullptr;
-    }
+    caps = new CameraCapabilities(mWindow, mCameraControl);
     mCapabilities = caps;
   }
 
   return caps.forget();
 }
 
 class ImmediateErrorCallback : public nsRunnable
 {
@@ -1135,20 +1130,19 @@ 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;
@@ -1159,16 +1153,17 @@ 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();
@@ -1183,16 +1178,17 @@ 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());
@@ -1397,17 +1393,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 %u face(s)\n", aFaces.Length());
+  DOM_CAMERA_LOGI("DOM OnFacesDetected %zu 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=%llx\n", __func__, __LINE__, this, mWindowId);
+  DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\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%llx\n", aDOMCameraControl, mWindowId);
+  DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\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%llx )\n", aWindowId);
+  DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\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,16 +40,19 @@ 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; }
@@ -58,14 +61,13 @@ 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,21 +66,18 @@ 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();
 }
 
@@ -140,16 +137,17 @@ 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);
@@ -300,19 +298,16 @@ 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",
@@ -955,17 +950,17 @@ nsGonkCameraControl::SetupRecordingFlash
 nsresult
 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                         const StartRecordingOptions* aOptions)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
-  NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
+  NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), 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.
@@ -1363,42 +1358,35 @@ nsGonkCameraControl::GetSupportedSize(co
   return rv;
 }
 
 nsresult
 nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
-  // 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());
+  RecorderProfile* profile;
+  if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) {
+    DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
+      NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get());
     return NS_ERROR_INVALID_ARG;
   }
 
-  const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
-  int width = video->GetWidth();
-  int height = video->GetHeight();
-  int fps = video->GetFramerate();
-  if (fps == -1 || width < 0 || height < 0) {
-    DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n",
-      fps, width, height);
+  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);
     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);
@@ -1594,18 +1582,22 @@ 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 = mRecorderProfile->ConfigureRecorder(mRecorder);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    GonkRecorderProfile::ConfigureRecorder(*mRecorder, mCameraId,
+                                           mCurrentConfiguration.mRecorderProfile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return 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) {
@@ -1666,37 +1658,86 @@ nsGonkCameraControl::StopImpl()
      mCameraHw->Close();
      mCameraHw.clear();
   }
 
   OnHardwareStateChange(CameraControlListener::kHardwareClosed);
   return NS_OK;
 }
 
-already_AddRefed<GonkRecorderProfileManager>
-nsGonkCameraControl::GetGonkRecorderProfileManager()
+nsresult
+nsGonkCameraControl::LoadRecorderProfiles()
 {
-  if (!mProfileManager) {
+  if (mRecorderProfiles.Count() == 0) {
+    nsTArray<nsRefPtr<RecorderProfile>> profiles;
+    nsresult rv = GonkRecorderProfile::GetAll(mCameraId, profiles);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
     nsTArray<Size> sizes;
-    nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
-    NS_ENSURE_SUCCESS(rv, nullptr);
+    rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
 
-    mProfileManager = new GonkRecorderProfileManager(mCameraId);
-    mProfileManager->SetSupportedResolutions(sizes);
+    // 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;
+        }
+      }
+    }
   }
 
-  nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
-  return profileMgr.forget();
+  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;
 }
 
-already_AddRefed<RecorderProfileManager>
-nsGonkCameraControl::GetRecorderProfileManagerImpl()
+nsresult
+nsGonkCameraControl::GetRecorderProfiles(nsTArray<nsString>& aProfiles)
 {
-  nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
-  return profileMgr.forget();
+  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;
 }
 
 void
 nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
 {
   CameraControlImpl::OnRateLimitPreview(aLimit);
 }
 
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -13,16 +13,17 @@
  * 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"
@@ -74,16 +75,20 @@ 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;
@@ -115,27 +120,30 @@ 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);
 
@@ -152,26 +160,24 @@ 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;
 
-  // Camcorder profile settings for the desired quality level
-  nsRefPtr<GonkRecorderProfileManager> mProfileManager;
-  nsRefPtr<GonkRecorderProfile> mRecorderProfile;
+  // Supported recorder profiles
+  nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
 
   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,26 +1,27 @@
 /*
- * Copyright (C) 2012 Mozilla Foundation
+ * Copyright (C) 2012-2014 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,
@@ -33,221 +34,323 @@ enum {
 static struct {
   const char* name;
   int quality;
 } ProfileList[] = {
   #include "GonkRecorderProfiles.def"
   { nullptr, 0 }
 };
 
-static MediaProfiles* sMediaProfiles = nullptr;
+/* static */ nsClassHashtable<nsUint32HashKey, ProfileHashtable> GonkRecorderProfile::sProfiles;
+/* static */ android::MediaProfiles* sMediaProfiles = nullptr;
 
-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)
+static MediaProfiles*
+GetMediaProfiles()
 {
   if (!sMediaProfiles) {
     sMediaProfiles = MediaProfiles::getInstance();
   }
-  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
-  return sMediaProfiles->getCamcorderProfileParamByName(aParam, static_cast<int>(aCameraId), q);
+  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);
 }
 
-/**
- * Recorder profile.
- */
-static RecorderProfile::FileFormat
-TranslateFileFormat(output_format aFileFormat)
+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)
 {
-  switch (aFileFormat) {
-    case OUTPUT_FORMAT_THREE_GPP: return RecorderProfile::THREE_GPP;
-    case OUTPUT_FORMAT_MPEG_4:    return RecorderProfile::MPEG4;
-    default:                      return RecorderProfile::UNKNOWN;
+  switch (aContainer) {
+    case OUTPUT_FORMAT_THREE_GPP:
+      aContainerName.AssignASCII("3gp");
+      break;
+
+    case OUTPUT_FORMAT_MPEG_4:
+      aContainerName.AssignASCII("mp4");
+      break;
+
+    default:
+      return false;
   }
+
+  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;
 }
 
-GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
-  : RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>(aCameraId, aQualityIndex)
+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)
 {
-  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);
-  }
+  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;
 }
 
-GonkRecorderProfile::~GonkRecorderProfile()
+/* static */
+ProfileHashtable*
+GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId)
 {
-  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  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;
 }
 
 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(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()));
+  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()));
 
-  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitrate());
-  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.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-encoding-bitrate=%d", mAudio.GetBitsPerSecond());
+  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.GetSamplerate());
-  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond());
+  CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
 
   return NS_OK;
 }
 
-/**
- * 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)
+/* static */ nsresult
+GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder,
+                                       uint32_t aCameraId,
+                                       const nsAString& aProfileName)
 {
-  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;
+  ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
+  if (!profiles) {
+    return NS_ERROR_FAILURE;
   }
 
-  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;
+  GonkRecorderProfile* profile;
+  if (!profiles->Get(aProfileName, &profile)) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  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();
+  return profile->ConfigureRecorder(aRecorder);
 }
-
-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,122 +1,152 @@
 /* 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"
+#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
 
 #ifndef CHECK_SETARG_RETURN
 #define CHECK_SETARG_RETURN(x, rv)      \
   do {                                  \
     if (x) {                            \
       DOM_CAMERA_LOGE(#x " failed\n");  \
       return rv;                        \
     }                                   \
   } 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 {
 
 /**
- * Gonk-specific video profile.
+ * class GonkRecorderProfileBase
  */
-class GonkRecorderVideoProfile : public RecorderVideoProfile
+template<class A, class V>
+class GonkRecorderProfileBase : public ICameraControl::RecorderProfile
 {
 public:
-  GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
-  ~GonkRecorderVideoProfile();
-  android::video_encoder GetPlatformCodec() const { return mPlatformCodec; }
+  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; }
 
 protected:
-  android::video_encoder mPlatformCodec;
+  virtual ~GonkRecorderProfileBase() { }
+  A mAudio;
+  V mVideo;
 };
 
 /**
- * Gonk-specific audio profile.
+ * class GonkRecorderVideo
  */
-class GonkRecorderAudioProfile : public RecorderAudioProfile
+class GonkRecorderVideo : public ICameraControl::RecorderProfile::Video
 {
 public:
-  GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
-  ~GonkRecorderAudioProfile();
-  android::audio_encoder GetPlatformCodec() const { return mPlatformCodec; }
+  GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex);
+  virtual ~GonkRecorderVideo() { }
+
+  android::video_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
+  bool IsValid() const { return mIsValid; }
 
 protected:
-  android::audio_encoder mPlatformCodec;
+  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;
 };
 
 /**
- * Gonk-specific recorder profile.
+ * class GonkRecorderAudio
  */
-class GonkRecorderProfile : public RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>
+class GonkRecorderAudio : public ICameraControl::RecorderProfile::Audio
 {
 public:
-  GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
-
-  GonkRecorderAudioProfile* GetGonkAudioProfile() { return &mAudio; }
-  GonkRecorderVideoProfile* GetGonkVideoProfile() { return &mVideo; }
-
-  android::output_format GetOutputFormat() const { return mPlatformOutputFormat; }
+  GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex);
+  virtual ~GonkRecorderAudio() { }
 
-  // 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);
+  android::audio_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
+  bool IsValid() const { return mIsValid; }
 
 protected:
-  virtual ~GonkRecorderProfile();
+  int GetProfileParameter(const char* aParameter);
+  static bool Translate(android::audio_encoder aCodec, nsAString& aCodecName);
 
-  android::output_format mPlatformOutputFormat;
+  uint32_t mCameraId;
+  uint32_t mProfileIndex;
+  bool mIsValid;
+  android::audio_encoder mPlatformEncoder;
 };
 
 /**
- * Gonk-specific profile manager.
+ * class GonkRecorderProfile
  */
-class GonkRecorderProfileManager : public RecorderProfileManager
+class GonkRecorderProfile;
+typedef nsRefPtrHashtable<nsStringHashKey, GonkRecorderProfile> ProfileHashtable;
+
+class GonkRecorderProfile
+  : public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
 {
 public:
-  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; }
+  static nsresult GetAll(uint32_t aCameraId,
+                         nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles);
 
-  /**
-   * 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;
+  // 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);
 
 protected:
-  virtual ~GonkRecorderProfileManager();
+  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; };
 
-  nsTArray<ICameraControl::Size> mSupportedSizes;
+  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);
 };
 
 }; // 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,16 +157,86 @@ 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:
@@ -215,17 +285,19 @@ 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 already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
+  virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) = 0;
+  virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) = 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,17 +12,16 @@ 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
@@ -211,16 +211,24 @@ 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,15 +1,64 @@
 /* -*- 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;
@@ -29,12 +78,14 @@ 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 any recorderProfiles;
+  [Constant, Cached] readonly attribute CameraRecorderProfiles recorderProfiles;
 
   [Constant, Cached] readonly attribute sequence<DOMString> isoModes;
+
+  jsonifier;
 };