Bug 779139 - Make DOM-facing camera objects cycle collection participants. r=jst
authorMike Habicher <mikeh@mozilla.com>
Tue, 04 Sep 2012 21:01:56 -0400
changeset 104262 244bba751ce4ae586d66d28c8e9a1f0b41e872c5
parent 104261 73eb2f3088f1862bbd495d2bfe49d03f6dca3083
child 104263 af0971ca7acd8eea374e1961b4cc3625a31e7fa7
push id23417
push userryanvm@gmail.com
push dateThu, 06 Sep 2012 02:27:31 +0000
treeherdermozilla-central@501f4e46a88c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs779139
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 779139 - Make DOM-facing camera objects cycle collection participants. r=jst
dom/base/nsDOMClassInfo.cpp
dom/camera/CameraCapabilities.h
dom/camera/CameraCommon.h
dom/camera/CameraControl.cpp
dom/camera/CameraControl.h
dom/camera/CameraControlImpl.cpp
dom/camera/CameraControlImpl.h
dom/camera/CameraPreview.cpp
dom/camera/CameraPreview.h
dom/camera/DOMCameraCapabilities.cpp
dom/camera/DOMCameraCapabilities.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/camera/DOMCameraManager.cpp
dom/camera/DOMCameraManager.h
dom/camera/DOMCameraPreview.cpp
dom/camera/DOMCameraPreview.h
dom/camera/FallbackCameraCapabilities.cpp
dom/camera/FallbackCameraControl.cpp
dom/camera/FallbackCameraManager.cpp
dom/camera/GonkCameraCapabilities.cpp
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkCameraHwMgr.cpp
dom/camera/GonkCameraHwMgr.h
dom/camera/GonkCameraManager.cpp
dom/camera/GonkCameraPreview.cpp
dom/camera/GonkCameraPreview.h
dom/camera/GonkNativeWindow.cpp
dom/camera/GonkNativeWindow.h
dom/camera/ICameraControl.h
dom/camera/Makefile.in
dom/camera/nsIDOMCameraManager.idl
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -524,18 +524,18 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "BluetoothPropertyEvent.h"
 #endif
 
 #include "nsIDOMNavigatorSystemMessages.h"
 
 #include "mozilla/dom/Activity.h"
 
 #include "DOMCameraManager.h"
-#include "CameraControl.h"
-#include "CameraCapabilities.h"
+#include "DOMCameraControl.h"
+#include "DOMCameraCapabilities.h"
 
 #include "DOMError.h"
 #include "DOMRequest.h"
 #include "nsIOpenWindowEventDetail.h"
 #include "nsIDOMGlobalObjectConstructor.h"
 
 #include "DOMFileHandle.h"
 #include "FileRequest.h"
deleted file mode 100644
--- a/dom/camera/CameraCapabilities.h
+++ /dev/null
@@ -1,45 +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_NSCAMERACAPABILITIES_H
-#define DOM_CAMERA_NSCAMERACAPABILITIES_H
-
-#include "CameraControl.h"
-#include "nsAutoPtr.h"
-#include "mozilla/Attributes.h"
-
-namespace mozilla {
-
-typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd);
-
-class nsCameraCapabilities MOZ_FINAL : public nsICameraCapabilities
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICAMERACAPABILITIES
-
-  nsCameraCapabilities(nsCameraControl* aCamera);
-
-  nsresult ParameterListToNewArray(
-    JSContext* cx,
-    JSObject** aArray,
-    uint32_t aKey,
-    ParseItemAndAddFunc aParseItemAndAdd
-  );
-  nsresult StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
-  nsresult DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
-
-private:
-  nsCameraCapabilities(const nsCameraCapabilities&) MOZ_DELETE;
-  nsCameraCapabilities& operator=(const nsCameraCapabilities&) MOZ_DELETE;
-
-protected:
-  /* additional members */
-  ~nsCameraCapabilities();
-  nsCOMPtr<nsCameraControl> mCamera;
-};
-
-} // namespace mozilla
-
-#endif // DOM_CAMERA_NSCAMERACAPABILITIES_H
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -16,36 +16,90 @@
 #endif
 
 #ifndef NAN
 #define NAN std::numeric_limits<double>::quiet_NaN()
 #endif
 
 #include "nsThreadUtils.h"
 #include "nsIDOMCameraManager.h"
+#include "prlog.h"
 
-#define DOM_CAMERA_LOG( l, ... )          \
-  do {                                    \
-    if ( DOM_CAMERA_LOG_LEVEL >= (l) ) {  \
-      printf_stderr (__VA_ARGS__);        \
-    }                                     \
-  } while (0)
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gCameraLog;
+#define DOM_CAMERA_LOG( type, ... ) PR_LOG(gCameraLog, (PRLogModuleLevel)type, ( __VA_ARGS__ ))
+#else
+#define DOM_CAMERA_LOG( type, ... )
+#endif
 
-#define DOM_CAMERA_LOGA( ... )        DOM_CAMERA_LOG( 0, __VA_ARGS__ )
+#define DOM_CAMERA_LOGA( ... )      DOM_CAMERA_LOG( 0, __VA_ARGS__ )
 
+/**
+ * From the least to the most output.
+ */
 enum {
   DOM_CAMERA_LOG_NOTHING,
   DOM_CAMERA_LOG_ERROR,
   DOM_CAMERA_LOG_WARNING,
-  DOM_CAMERA_LOG_INFO
+  DOM_CAMERA_LOG_INFO,
+  DOM_CAMERA_LOG_TRACE,
+  DOM_CAMERA_LOG_REFERENCES
 };
 
-#define DOM_CAMERA_LOGI( ... )        DOM_CAMERA_LOG( DOM_CAMERA_LOG_INFO,  __VA_ARGS__ )
-#define DOM_CAMERA_LOGW( ... )        DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
-#define DOM_CAMERA_LOGE( ... )        DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
+/**
+ * DOM_CAMERA_LOGR() can be called before 'gCameraLog' is set, so
+ * we need to handle this one a little differently.
+ */
+#ifdef PR_LOGGING
+#define DOM_CAMERA_LOGR( ... )                                  \
+  do {                                                          \
+    if (gCameraLog) {                                           \
+      DOM_CAMERA_LOG( DOM_CAMERA_LOG_REFERENCES, __VA_ARGS__ ); \
+    }                                                           \
+  } while (0)
+#else
+#define DOM_CAMERA_LOGR( ... )
+#endif
+#define DOM_CAMERA_LOGT( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_TRACE, __VA_ARGS__ )
+#define DOM_CAMERA_LOGI( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_INFO, __VA_ARGS__ )
+#define DOM_CAMERA_LOGW( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
+#define DOM_CAMERA_LOGE( ... )      DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
+
+enum {
+  CAMERA_PARAM_EFFECT,
+  CAMERA_PARAM_WHITEBALANCE,
+  CAMERA_PARAM_SCENEMODE,
+  CAMERA_PARAM_FLASHMODE,
+  CAMERA_PARAM_FOCUSMODE,
+  CAMERA_PARAM_ZOOM,
+  CAMERA_PARAM_METERINGAREAS,
+  CAMERA_PARAM_FOCUSAREAS,
+  CAMERA_PARAM_FOCALLENGTH,
+  CAMERA_PARAM_FOCUSDISTANCENEAR,
+  CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
+  CAMERA_PARAM_FOCUSDISTANCEFAR,
+  CAMERA_PARAM_EXPOSURECOMPENSATION,
+
+  CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
+  CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
+  CAMERA_PARAM_SUPPORTED_PICTURESIZES,
+  CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
+  CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
+  CAMERA_PARAM_SUPPORTED_SCENEMODES,
+  CAMERA_PARAM_SUPPORTED_EFFECTS,
+  CAMERA_PARAM_SUPPORTED_FLASHMODES,
+  CAMERA_PARAM_SUPPORTED_FOCUSMODES,
+  CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
+  CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
+  CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
+  CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
+  CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
+  CAMERA_PARAM_SUPPORTED_ZOOM,
+  CAMERA_PARAM_SUPPORTED_ZOOMRATIOS
+};
 
 class CameraErrorResult : public nsRunnable
 {
 public:
   CameraErrorResult(nsICameraErrorCallback* onError, const nsString& aErrorMsg)
     : mOnErrorCb(onError)
     , mErrorMsg(aErrorMsg)
   { }
@@ -60,9 +114,48 @@ public:
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
   const nsString mErrorMsg;
 };
 
+#ifdef PR_LOGGING
+
+static inline void nsLogAddRefCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, uint32_t size)
+{
+  if (count == 1) {
+    DOM_CAMERA_LOGR("++++++++++++++++++++++++++++++++++++++++");
+  }
+  DOM_CAMERA_LOGR("%s:%d : CAMREF-ADD(%s): this=%p, mRefCnt=%d\n", file, line, clazz, p, count);
+}
+
+static inline void nsLogReleaseCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, bool abortOnDelete)
+{
+  DOM_CAMERA_LOGR("%s:%d : CAMREF-REL(%s): this=%p, mRefCnt=%d\n", file, line, clazz, p, count);
+  if (count == 0) {
+    if (!abortOnDelete) {
+      DOM_CAMERA_LOGR("----------------------------------------");
+    } else {
+      DOM_CAMERA_LOGR("---------- ABORTING ON DELETE ----------");
+      *((uint32_t *)0xdeadbeef) = 0x266230;
+    }
+  }
+}
+
+#ifdef NS_LOG_ADDREF
+#undef NS_LOG_ADDREF
+#endif
+#ifdef NS_LOG_RELEASE
+#undef NS_LOG_RELEASE
+#endif
+
+#define NS_LOG_ADDREF( p, n, c, s ) nsLogAddRefCamera(__FILE__, __LINE__, (p), (n), (c), (s))
+#ifdef DOM_CAMERA_DEBUG_REFS_ABORT_ON_DELETE
+#define NS_LOG_RELEASE( p, n, c )   nsLogReleaseCamera(__FILE__, __LINE__, (p), (n), (c), DOM_CAMERA_DEBUG_REFS_ABORT_ON_DELETE)
+#else
+#define NS_LOG_RELEASE( p, n, c )   nsLogReleaseCamera(__FILE__, __LINE__, (p), (n), (c), false)
+#endif
+
+#endif // PR_LOGGING
+
 #endif // DOM_CAMERA_CAMERACOMMON_H
deleted file mode 100644
--- a/dom/camera/CameraControl.cpp
+++ /dev/null
@@ -1,506 +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 "nsCOMPtr.h"
-#include "nsDOMClassInfo.h"
-#include "jsapi.h"
-#include "nsThread.h"
-#include "DOMCameraManager.h"
-#include "CameraControl.h"
-#include "CameraCapabilities.h"
-#include "CameraControl.h"
-#include "mozilla/Services.h"
-#include "nsIObserverService.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
-#include "CameraCommon.h"
-
-using namespace mozilla;
-using namespace dom;
-
-DOMCI_DATA(CameraControl, nsICameraControl)
-
-NS_INTERFACE_MAP_BEGIN(nsCameraControl)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsICameraControl)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraControl)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_THREADSAFE_ADDREF(nsCameraControl)
-NS_IMPL_THREADSAFE_RELEASE(nsCameraControl)
-
-// Helpers for string properties.
-nsresult
-nsCameraControl::SetHelper(uint32_t aKey, const nsAString& aValue)
-{
-  SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get());
-  return NS_OK;
-}
-
-nsresult
-nsCameraControl::GetHelper(uint32_t aKey, nsAString& aValue)
-{
-  const char* value = GetParameterConstChar(aKey);
-  if (!value) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aValue.AssignASCII(value);
-  return NS_OK;
-}
-
-// Helpers for doubles.
-nsresult
-nsCameraControl::SetHelper(uint32_t aKey, double aValue)
-{
-  SetParameter(aKey, aValue);
-  return NS_OK;
-}
-
-nsresult
-nsCameraControl::GetHelper(uint32_t aKey, double* aValue)
-{
-  MOZ_ASSERT(aValue);
-  *aValue = GetParameterDouble(aKey);
-  return NS_OK;
-}
-
-// Helper for weighted regions.
-nsresult
-nsCameraControl::SetHelper(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
-{
-  if (aLimit == 0) {
-    DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
-    return NS_OK;
-  }
-
-  if (!aValue.isObject()) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  uint32_t length = 0;
-
-  JSObject* regions = &aValue.toObject();
-  if (!JS_GetArrayLength(aCx, regions, &length)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
-  if (length > aLimit) {
-    length = aLimit;
-  }
-    
-  nsTArray<CameraRegion> regionArray;
-  regionArray.SetCapacity(length);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    JS::Value v;
-
-    if (!JS_GetElement(aCx, regions, i, &v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    CameraRegion* r = regionArray.AppendElement();
-    /**
-     * These are the default values.  We can remove these when the xpidl
-     * dictionary parser gains the ability to grok default values.
-     */
-    r->top = -1000;
-    r->left = -1000;
-    r->bottom = 1000;
-    r->right = 1000;
-    r->weight = 1000;
-
-    nsresult rv = r->Init(aCx, &v);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n",
-      i,
-      r->top,
-      r->left,
-      r->bottom,
-      r->right,
-      r->weight
-    );
-  }
-  SetParameter(aKey, regionArray);
-  return NS_OK;
-}
-
-nsresult
-nsCameraControl::GetHelper(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
-{
-  nsTArray<CameraRegion> regionArray;
-
-  GetParameter(aKey, regionArray);
-
-  JSObject* array = JS_NewArrayObject(aCx, 0, nullptr);
-  if (!array) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  uint32_t length = regionArray.Length();
-  DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    CameraRegion* r = &regionArray[i];
-    JS::Value v;
-
-    JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
-    if (!o) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    DOM_CAMERA_LOGI("top=%d\n", r->top);
-    v = INT_TO_JSVAL(r->top);
-    if (!JS_SetProperty(aCx, o, "top", &v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("left=%d\n", r->left);
-    v = INT_TO_JSVAL(r->left);
-    if (!JS_SetProperty(aCx, o, "left", &v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
-    v = INT_TO_JSVAL(r->bottom);
-    if (!JS_SetProperty(aCx, o, "bottom", &v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("right=%d\n", r->right);
-    v = INT_TO_JSVAL(r->right);
-    if (!JS_SetProperty(aCx, o, "right", &v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("weight=%d\n", r->weight);
-    v = INT_TO_JSVAL(r->weight);
-    if (!JS_SetProperty(aCx, o, "weight", &v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    v = OBJECT_TO_JSVAL(o);
-    if (!JS_SetElement(aCx, array, i, &v)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  *aValue = JS::ObjectValue(*array);
-  return NS_OK;
-}
-
-/* readonly attribute nsICameraCapabilities capabilities; */
-NS_IMETHODIMP
-nsCameraControl::GetCapabilities(nsICameraCapabilities** aCapabilities)
-{
-  if (!mCapabilities) {
-    mCapabilities = new nsCameraCapabilities(this);
-  }
-
-  nsCOMPtr<nsICameraCapabilities> capabilities = mCapabilities;
-  capabilities.forget(aCapabilities);
-  return NS_OK;
-}
-
-/* attribute DOMString effect; */
-NS_IMETHODIMP
-nsCameraControl::GetEffect(nsAString& aEffect)
-{
-  return GetHelper(CAMERA_PARAM_EFFECT, aEffect);
-}
-NS_IMETHODIMP
-nsCameraControl::SetEffect(const nsAString& aEffect)
-{
-  return SetHelper(CAMERA_PARAM_EFFECT, aEffect);
-}
-
-/* attribute DOMString whiteBalanceMode; */
-NS_IMETHODIMP
-nsCameraControl::GetWhiteBalanceMode(nsAString& aWhiteBalanceMode)
-{
-  return GetHelper(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
-}
-NS_IMETHODIMP
-nsCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode)
-{
-  return SetHelper(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
-}
-
-/* attribute DOMString sceneMode; */
-NS_IMETHODIMP
-nsCameraControl::GetSceneMode(nsAString& aSceneMode)
-{
-  return GetHelper(CAMERA_PARAM_SCENEMODE, aSceneMode);
-}
-NS_IMETHODIMP
-nsCameraControl::SetSceneMode(const nsAString& aSceneMode)
-{
-  return SetHelper(CAMERA_PARAM_SCENEMODE, aSceneMode);
-}
-
-/* attribute DOMString flashMode; */
-NS_IMETHODIMP
-nsCameraControl::GetFlashMode(nsAString& aFlashMode)
-{
-  return GetHelper(CAMERA_PARAM_FLASHMODE, aFlashMode);
-}
-NS_IMETHODIMP
-nsCameraControl::SetFlashMode(const nsAString& aFlashMode)
-{
-  return SetHelper(CAMERA_PARAM_FLASHMODE, aFlashMode);
-}
-
-/* attribute DOMString focusMode; */
-NS_IMETHODIMP
-nsCameraControl::GetFocusMode(nsAString& aFocusMode)
-{
-  return GetHelper(CAMERA_PARAM_FOCUSMODE, aFocusMode);
-}
-NS_IMETHODIMP
-nsCameraControl::SetFocusMode(const nsAString& aFocusMode)
-{
-  return SetHelper(CAMERA_PARAM_FOCUSMODE, aFocusMode);
-}
-
-/* attribute double zoom; */
-NS_IMETHODIMP
-nsCameraControl::GetZoom(double* aZoom)
-{
-  return GetHelper(CAMERA_PARAM_ZOOM, aZoom);
-}
-NS_IMETHODIMP
-nsCameraControl::SetZoom(double aZoom)
-{
-  return SetHelper(CAMERA_PARAM_ZOOM, aZoom);
-}
-
-/* attribute jsval meteringAreas; */
-NS_IMETHODIMP
-nsCameraControl::GetMeteringAreas(JSContext* cx, JS::Value* aMeteringAreas)
-{
-  return GetHelper(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas);
-}
-NS_IMETHODIMP
-nsCameraControl::SetMeteringAreas(JSContext* cx, const JS::Value& aMeteringAreas)
-{
-  return SetHelper(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas, mMaxMeteringAreas);
-}
-
-/* attribute jsval focusAreas; */
-NS_IMETHODIMP
-nsCameraControl::GetFocusAreas(JSContext* cx, JS::Value* aFocusAreas)
-{
-  return GetHelper(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas);
-}
-NS_IMETHODIMP
-nsCameraControl::SetFocusAreas(JSContext* cx, const JS::Value& aFocusAreas)
-{
-  return SetHelper(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas, mMaxFocusAreas);
-}
-
-/* readonly attribute double focalLength; */
-NS_IMETHODIMP
-nsCameraControl::GetFocalLength(double* aFocalLength)
-{
-  return GetHelper(CAMERA_PARAM_FOCALLENGTH, aFocalLength);
-}
-
-/* readonly attribute double focusDistanceNear; */
-NS_IMETHODIMP
-nsCameraControl::GetFocusDistanceNear(double* aFocusDistanceNear)
-{
-  return GetHelper(CAMERA_PARAM_FOCUSDISTANCENEAR, aFocusDistanceNear);
-}
-
-/* readonly attribute double focusDistanceOptimum; */
-NS_IMETHODIMP
-nsCameraControl::GetFocusDistanceOptimum(double* aFocusDistanceOptimum)
-{
-  return GetHelper(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, aFocusDistanceOptimum);
-}
-
-/* readonly attribute double focusDistanceFar; */
-NS_IMETHODIMP
-nsCameraControl::GetFocusDistanceFar(double* aFocusDistanceFar)
-{
-  return GetHelper(CAMERA_PARAM_FOCUSDISTANCEFAR, aFocusDistanceFar);
-}
-
-/* void setExposureCompensation (const JS::Value& aCompensation, JSContext* cx); */
-NS_IMETHODIMP
-nsCameraControl::SetExposureCompensation(const JS::Value& aCompensation, JSContext* cx)
-{
-  if (aCompensation.isNullOrUndefined()) {
-    // use NaN to switch the camera back into auto mode
-    return SetHelper(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
-  }
-
-  double compensation;
-  if (!JS_ValueToNumber(cx, aCompensation, &compensation)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  return SetHelper(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
-}
-
-/* readonly attribute double exposureCompensation; */
-NS_IMETHODIMP
-nsCameraControl::GetExposureCompensation(double* aExposureCompensation)
-{
-  return GetHelper(CAMERA_PARAM_EXPOSURECOMPENSATION, aExposureCompensation);
-}
-
-/* attribute nsICameraShutterCallback onShutter; */
-NS_IMETHODIMP
-nsCameraControl::GetOnShutter(nsICameraShutterCallback** aOnShutter)
-{
-  *aOnShutter = mOnShutterCb;
-  return NS_OK;
-}
-NS_IMETHODIMP
-nsCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter)
-{
-  mOnShutterCb = aOnShutter;
-  return NS_OK;
-}
-
-/* void startRecording (in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
-NS_IMETHODIMP
-nsCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
-{
-  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
-
-  CameraSize size;
-  nsresult rv = size.Init(cx, &aOptions);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, size, onSuccess, onError);
-  mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (!obs) {
-    NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
-    return NS_ERROR_FAILURE;
-  }
-
-  obs->NotifyObservers(nullptr,
-                       "recording-device-events",
-                       NS_LITERAL_STRING("starting").get());
-
-  return NS_OK;
-}
-
-/* void stopRecording (); */
-NS_IMETHODIMP
-nsCameraControl::StopRecording()
-{
-  nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
-  mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (!obs) {
-    NS_WARNING("Could not get the Observer service for CameraControl::StopRecording.");
-    return NS_ERROR_FAILURE;
-  }
-
-  obs->NotifyObservers(nullptr,
-                       "recording-device-events",
-                       NS_LITERAL_STRING("shutdown").get());
-
-  return NS_OK;
-}
-
-/* [implicit_jscontext] void getPreviewStream (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
-NS_IMETHODIMP
-nsCameraControl::GetPreviewStream(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
-{
-  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
-
-  CameraSize size;
-  nsresult rv = size.Init(cx, &aOptions);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIRunnable> getPreviewStreamTask = new GetPreviewStreamTask(this, size, onSuccess, onError);
-  return NS_DispatchToMainThread(getPreviewStreamTask);
-}
-
-/* void autoFocus (in nsICameraAutoFocusCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
-NS_IMETHODIMP
-nsCameraControl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
-{
-  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
-
-  nsCOMPtr<nsIRunnable> autoFocusTask = new AutoFocusTask(this, onSuccess, onError);
-  mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL);
-
-  return NS_OK;
-}
-
-/* void takePicture (in jsval aOptions, in nsICameraTakePictureCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
-NS_IMETHODIMP nsCameraControl::TakePicture(const JS::Value& aOptions, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
-{
-  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
-
-  CameraPictureOptions  options;
-  CameraSize            size;
-  CameraPosition        pos;
-
-  nsresult rv = options.Init(cx, &aOptions);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = size.Init(cx, &options.pictureSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  /**
-   * Default values, until the dictionary parser can handle them.
-   * NaN indicates no value provided.
-   */
-  pos.latitude = NAN;
-  pos.longitude = NAN;
-  pos.altitude = NAN;
-  pos.timestamp = NAN;
-  rv = pos.Init(cx, &options.position);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, size, options.rotation, options.fileFormat, pos, onSuccess, onError);
-  mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
-
-  return NS_OK;
-}
-
-void
-nsCameraControl::AutoFocusComplete(bool aSuccess)
-{
-  /**
-   * Auto focusing can change some of the camera's parameters, so
-   * we need to pull a new set before sending the result to the
-   * main thread.
-   */
-  PullParametersImpl(nullptr);
-
-  nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb);
-
-  nsresult rv = NS_DispatchToMainThread(autoFocusResult);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to dispatch autoFocus() onSuccess callback to main thread!");
-  }
-}
-
-void
-nsCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength)
-{
-  uint8_t* data = new uint8_t[aLength];
-
-  memcpy(data, aData, aLength);
-
-  /**
-   * TODO: pick up the actual specified picture format for the MIME type;
-   * for now, assume we'll be using JPEGs.
-   */
-  nsIDOMBlob* blob = new nsDOMMemoryFile(static_cast<void*>(data), static_cast<uint64_t>(aLength), NS_LITERAL_STRING("image/jpeg"));
-  nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(blob, mTakePictureOnSuccessCb);
-
-  nsresult rv = NS_DispatchToMainThread(takePictureResult);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to dispatch takePicture() onSuccess callback to main thread!");
-  }
-}
new file mode 100644
--- /dev/null
+++ b/dom/camera/CameraControlImpl.cpp
@@ -0,0 +1,244 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+#include "DOMCameraPreview.h"
+#include "CameraControlImpl.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+
+// Helpers for string properties.
+nsresult
+CameraControlImpl::Set(uint32_t aKey, const nsAString& aValue)
+{
+  SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get());
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, nsAString& aValue)
+{
+  const char* value = GetParameterConstChar(aKey);
+  if (!value) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aValue.AssignASCII(value);
+  return NS_OK;
+}
+
+// Helpers for doubles.
+nsresult
+CameraControlImpl::Set(uint32_t aKey, double aValue)
+{
+  SetParameter(aKey, aValue);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(uint32_t aKey, double* aValue)
+{
+  MOZ_ASSERT(aValue);
+  *aValue = GetParameterDouble(aKey);
+  return NS_OK;
+}
+
+// Helper for weighted regions.
+nsresult
+CameraControlImpl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
+{
+  if (aLimit == 0) {
+    DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
+    return NS_OK;
+  }
+
+  if (!aValue.isObject()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t length = 0;
+
+  JSObject* regions = &aValue.toObject();
+  if (!JS_GetArrayLength(aCx, regions, &length)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
+  if (length > aLimit) {
+    length = aLimit;
+  }
+
+  nsTArray<CameraRegion> regionArray;
+  regionArray.SetCapacity(length);
+
+  for (uint32_t i = 0; i < length; ++i) {
+    JS::Value v;
+
+    if (!JS_GetElement(aCx, regions, i, &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    CameraRegion* r = regionArray.AppendElement();
+    /**
+     * These are the default values.  We can remove these when the xpidl
+     * dictionary parser gains the ability to grok default values.
+     */
+    r->top = -1000;
+    r->left = -1000;
+    r->bottom = 1000;
+    r->right = 1000;
+    r->weight = 1000;
+
+    nsresult rv = r->Init(aCx, &v);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n",
+      i,
+      r->top,
+      r->left,
+      r->bottom,
+      r->right,
+      r->weight
+    );
+  }
+  SetParameter(aKey, regionArray);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
+{
+  nsTArray<CameraRegion> regionArray;
+
+  GetParameter(aKey, regionArray);
+
+  JSObject* array = JS_NewArrayObject(aCx, 0, nullptr);
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  uint32_t length = regionArray.Length();
+  DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
+
+  for (uint32_t i = 0; i < length; ++i) {
+    CameraRegion* r = &regionArray[i];
+    JS::Value v;
+
+    JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+    if (!o) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    DOM_CAMERA_LOGI("top=%d\n", r->top);
+    v = INT_TO_JSVAL(r->top);
+    if (!JS_SetProperty(aCx, o, "top", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("left=%d\n", r->left);
+    v = INT_TO_JSVAL(r->left);
+    if (!JS_SetProperty(aCx, o, "left", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
+    v = INT_TO_JSVAL(r->bottom);
+    if (!JS_SetProperty(aCx, o, "bottom", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("right=%d\n", r->right);
+    v = INT_TO_JSVAL(r->right);
+    if (!JS_SetProperty(aCx, o, "right", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+    DOM_CAMERA_LOGI("weight=%d\n", r->weight);
+    v = INT_TO_JSVAL(r->weight);
+    if (!JS_SetProperty(aCx, o, "weight", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    v = OBJECT_TO_JSVAL(o);
+    if (!JS_SetElement(aCx, array, i, &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aValue = JS::ObjectValue(*array);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  /**
+   * The camera preview stream object is DOM-facing, and as such
+   * must be a cycle-collection participant created on the main
+   * thread.
+   */
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIRunnable> getPreviewStreamTask = new GetPreviewStreamTask(this, aSize, onSuccess, onError);
+  return NS_DispatchToCurrentThread(getPreviewStreamTask);
+}
+
+nsresult
+CameraControlImpl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  nsCOMPtr<nsIRunnable> autoFocusTask = new AutoFocusTask(this, onSuccess, onError);
+  return mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, aSize, aRotation, aFileFormat, aPosition, onSuccess, onError);
+  return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+CameraControlImpl::StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, aSize, onSuccess, onError);
+  return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+CameraControlImpl::StopRecording()
+{
+  nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
+  return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+CameraControlImpl::StartPreview(DOMCameraPreview* aDOMPreview)
+{
+  nsCOMPtr<nsIRunnable> startPreviewTask = new StartPreviewTask(this, aDOMPreview);
+  return mCameraThread->Dispatch(startPreviewTask, NS_DISPATCH_NORMAL);
+}
+
+void
+CameraControlImpl::StopPreview()
+{
+  nsCOMPtr<nsIRunnable> stopPreviewTask = new StopPreviewTask(this);
+  mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL);
+}
+
+void
+CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
+{
+  if (mDOMPreview) {
+    mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder);
+  }
+}
+
+NS_IMETHODIMP
+GetPreviewStreamResult::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mOnSuccessCb) {
+    nsCOMPtr<nsIDOMMediaStream> stream = new DOMCameraPreview(mCameraControl, mWidth, mHeight, mFramesPerSecond);
+    mOnSuccessCb->HandleEvent(stream);
+  }
+  return NS_OK;
+}
rename from dom/camera/CameraControl.h
rename to dom/camera/CameraControlImpl.h
--- a/dom/camera/CameraControl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -1,223 +1,227 @@
 /* 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_NSCAMERACONTROL_H
-#define DOM_CAMERA_NSCAMERACONTROL_H
+#ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
+#define DOM_CAMERA_CAMERACONTROLIMPL_H
 
-#include "prtypes.h"
 #include "nsCOMPtr.h"
-#include "nsThread.h"
 #include "nsDOMFile.h"
 #include "DictionaryHelpers.h"
-#include "CameraPreview.h"
 #include "nsIDOMCameraManager.h"
-
-#define DOM_CAMERA_LOG_LEVEL 3
+#include "ICameraControl.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 class GetPreviewStreamTask;
+class StartPreviewTask;
+class StopPreviewTask;
 class AutoFocusTask;
 class TakePictureTask;
 class StartRecordingTask;
 class StopRecordingTask;
 class SetParameterTask;
 class GetParameterTask;
-class PushParametersTask;
-class PullParametersTask;
 
-// Main camera control.
-class nsCameraControl : public nsICameraControl
+class DOMCameraPreview;
+
+class CameraControlImpl : public ICameraControl
 {
   friend class GetPreviewStreamTask;
+  friend class StartPreviewTask;
+  friend class StopPreviewTask;
   friend class AutoFocusTask;
   friend class TakePictureTask;
   friend class StartRecordingTask;
   friend class StopRecordingTask;
   friend class SetParameterTask;
   friend class GetParameterTask;
-  friend class PushParametersTask;
-  friend class PullParametersTask;
 
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICAMERACONTROL
-
-  enum {
-    CAMERA_PARAM_EFFECT,
-    CAMERA_PARAM_WHITEBALANCE,
-    CAMERA_PARAM_SCENEMODE,
-    CAMERA_PARAM_FLASHMODE,
-    CAMERA_PARAM_FOCUSMODE,
-    CAMERA_PARAM_ZOOM,
-    CAMERA_PARAM_METERINGAREAS,
-    CAMERA_PARAM_FOCUSAREAS,
-    CAMERA_PARAM_FOCALLENGTH,
-    CAMERA_PARAM_FOCUSDISTANCENEAR,
-    CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
-    CAMERA_PARAM_FOCUSDISTANCEFAR,
-    CAMERA_PARAM_EXPOSURECOMPENSATION,
+  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread)
+    : mCameraId(aCameraId)
+    , mCameraThread(aCameraThread)
+    , mFileFormat()
+    , mMaxMeteringAreas(0)
+    , mMaxFocusAreas(0)
+    , mDOMPreview(nullptr)
+    , mAutoFocusOnSuccessCb(nullptr)
+    , mAutoFocusOnErrorCb(nullptr)
+    , mTakePictureOnSuccessCb(nullptr)
+    , mTakePictureOnErrorCb(nullptr)
+    , mStartRecordingOnSuccessCb(nullptr)
+    , mStartRecordingOnErrorCb(nullptr)
+    , mOnShutterCb(nullptr)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
-    CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
-    CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
-    CAMERA_PARAM_SUPPORTED_PICTURESIZES,
-    CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
-    CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
-    CAMERA_PARAM_SUPPORTED_SCENEMODES,
-    CAMERA_PARAM_SUPPORTED_EFFECTS,
-    CAMERA_PARAM_SUPPORTED_FLASHMODES,
-    CAMERA_PARAM_SUPPORTED_FOCUSMODES,
-    CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
-    CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
-    CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
-    CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
-    CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
-    CAMERA_PARAM_SUPPORTED_ZOOM,
-    CAMERA_PARAM_SUPPORTED_ZOOMRATIOS
-  };
+  nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartPreview(DOMCameraPreview* aDOMPreview);
+  void StopPreview();
+  nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StopRecording();
+
+  nsresult Set(uint32_t aKey, const nsAString& aValue);
+  nsresult Get(uint32_t aKey, nsAString& aValue);
+  nsresult Set(uint32_t aKey, double aValue);
+  nsresult Get(uint32_t aKey, double* aValue);
+  nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
+  nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
+
+  nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
+  {
+    return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
+  }
+
+  nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
+  {
+    return Set(aCx, CAMERA_PARAM_METERINGAREAS, aValue, mMaxMeteringAreas);
+  }
+
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const nsTArray<CameraRegion>& aRegions) = 0;
-  virtual void PushParameters() = 0;
+  virtual nsresult PushParameters() = 0;
 
-  nsCameraControl(uint32_t aCameraId, nsIThread* aCameraThread)
-    : mCameraId(aCameraId)
-    , mCameraThread(aCameraThread)
-    , mCapabilities(nullptr)
-    , mPreview(nullptr)
-    , mFileFormat()
-    , mMaxMeteringAreas(0)
-    , mMaxFocusAreas(0)
-    , mAutoFocusOnSuccessCb(nullptr)
-    , mAutoFocusOnErrorCb(nullptr)
-    , mTakePictureOnSuccessCb(nullptr)
-    , mTakePictureOnErrorCb(nullptr)
-    , mStartRecordingOnSuccessCb(nullptr)
-    , mStartRecordingOnErrorCb(nullptr)
-    , mOnShutterCb(nullptr)
-  { }
-
-  void TakePictureComplete(uint8_t *aData, uint32_t aLength);
-  void AutoFocusComplete(bool aSuccess);
+  void ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
 
 protected:
-  virtual ~nsCameraControl() { }
-
-  nsresult SetHelper(uint32_t aKey, const nsAString& aValue);
-  nsresult GetHelper(uint32_t aKey, nsAString& aValue);
-  nsresult SetHelper(uint32_t aKey, double aValue);
-  nsresult GetHelper(uint32_t aKey, double* aValue);
-  nsresult SetHelper(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
-  nsresult GetHelper(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
+  virtual ~CameraControlImpl()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0;
+  virtual nsresult StartPreviewImpl(StartPreviewTask* aStartPreview) = 0;
+  virtual nsresult StopPreviewImpl(StopPreviewTask* aStopPreview) = 0;
   virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0;
   virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0;
   virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0;
   virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
-  virtual nsresult PushParametersImpl(PushParametersTask* aPushParameters) = 0;
-  virtual nsresult PullParametersImpl(PullParametersTask* aPullParameters) = 0;
+  virtual nsresult PushParametersImpl() = 0;
+  virtual nsresult PullParametersImpl() = 0;
 
-private:
-  nsCameraControl(const nsCameraControl&) MOZ_DELETE;
-  nsCameraControl& operator=(const nsCameraControl&) MOZ_DELETE;
+  uint32_t            mCameraId;
+  nsCOMPtr<nsIThread> mCameraThread;
+  nsString            mFileFormat;
+  uint32_t            mMaxMeteringAreas;
+  uint32_t            mMaxFocusAreas;
 
-protected:
-  /* additional members */
-  uint32_t                        mCameraId;
-  nsCOMPtr<nsIThread>             mCameraThread;
-  nsCOMPtr<nsICameraCapabilities> mCapabilities;
-  uint32_t                        mPreviewWidth;
-  uint32_t                        mPreviewHeight;
-  nsCOMPtr<CameraPreview>         mPreview;
-  nsString                        mFileFormat;
-  uint32_t                        mMaxMeteringAreas;
-  uint32_t                        mMaxFocusAreas;
+  /**
+   * 'mDOMPreview' is a raw pointer to the object that will receive incoming
+   * preview frames.  This is guaranteed to be valid, or null.
+   *
+   * It is set by a call to StartPreview(), and set to null on StopPreview().
+   * It is up to the caller to ensure that the object will not disappear
+   * out from under this pointer--usually by calling NS_ADDREF().
+   */
+  DOMCameraPreview*   mDOMPreview;
 
   nsCOMPtr<nsICameraAutoFocusCallback>      mAutoFocusOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mAutoFocusOnErrorCb;
   nsCOMPtr<nsICameraTakePictureCallback>    mTakePictureOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mTakePictureOnErrorCb;
   nsCOMPtr<nsICameraStartRecordingCallback> mStartRecordingOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mStartRecordingOnErrorCb;
   nsCOMPtr<nsICameraShutterCallback>        mOnShutterCb;
+
+private:
+  CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
+  CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE;
 };
 
 // Return the resulting preview stream to JS.  Runs on the main thread.
 class GetPreviewStreamResult : public nsRunnable
 {
 public:
-  GetPreviewStreamResult(nsIDOMMediaStream* aStream, nsICameraPreviewStreamCallback* onSuccess)
-    : mStream(aStream)
+  GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsICameraPreviewStreamCallback* onSuccess)
+    : mCameraControl(aCameraControl)
+    , mWidth(aWidth)
+    , mHeight(aHeight)
+    , mFramesPerSecond(aFramesPerSecond)
     , mOnSuccessCb(onSuccess)
-  { }
-
-  NS_IMETHOD Run()
   {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (mOnSuccessCb) {
-      mOnSuccessCb->HandleEvent(mStream);
-    }
-    return NS_OK;
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
+  virtual ~GetPreviewStreamResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  // Run() method is implementation specific.
+  NS_IMETHOD Run();
+
 protected:
-  nsCOMPtr<nsIDOMMediaStream> mStream;
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mFramesPerSecond;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
 };
 
 // Get the desired preview stream.
 class GetPreviewStreamTask : public nsRunnable
 {
 public:
-  GetPreviewStreamTask(nsCameraControl* aCameraControl, CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+  GetPreviewStreamTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
     : mSize(aSize)
     , mCameraControl(aCameraControl)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~GetPreviewStreamTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
     nsresult rv = mCameraControl->GetPreviewStreamImpl(this);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
       rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
   CameraSize mSize;
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Return the autofocus status to JS.  Runs on the main thread.
 class AutoFocusResult : public nsRunnable
 {
 public:
   AutoFocusResult(bool aSuccess, nsICameraAutoFocusCallback* onSuccess)
     : mSuccess(aSuccess)
     , mOnSuccessCb(onSuccess)
   { }
 
+  virtual ~AutoFocusResult() { }
+
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (mOnSuccessCb) {
       mOnSuccessCb->HandleEvent(mSuccess);
     }
     return NS_OK;
@@ -227,92 +231,115 @@ protected:
   bool mSuccess;
   nsCOMPtr<nsICameraAutoFocusCallback> mOnSuccessCb;
 };
 
 // Autofocus the camera.
 class AutoFocusTask : public nsRunnable
 {
 public:
-  AutoFocusTask(nsCameraControl* aCameraControl, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
+  AutoFocusTask(CameraControlImpl* aCameraControl, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~AutoFocusTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->AutoFocusImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
       rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsICameraAutoFocusCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Return the captured picture to JS.  Runs on the main thread.
 class TakePictureResult : public nsRunnable
 {
 public:
   TakePictureResult(nsIDOMBlob* aImage, nsICameraTakePictureCallback* onSuccess)
     : mImage(aImage)
     , mOnSuccessCb(onSuccess)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~TakePictureResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
     if (mOnSuccessCb) {
       mOnSuccessCb->HandleEvent(mImage);
     }
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<nsIDOMBlob> mImage;
   nsCOMPtr<nsICameraTakePictureCallback> mOnSuccessCb;
 };
 
 // Capture a still image with the camera.
 class TakePictureTask : public nsRunnable
 {
 public:
-  TakePictureTask(nsCameraControl* aCameraControl, CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
+  TakePictureTask(CameraControlImpl* aCameraControl, CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
     , mSize(aSize)
     , mRotation(aRotation)
     , mFileFormat(aFileFormat)
     , mPosition(aPosition)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~TakePictureTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->TakePictureImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
       rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
   CameraSize mSize;
   int32_t mRotation;
   nsString mFileFormat;
   CameraPosition mPosition;
   nsCOMPtr<nsICameraTakePictureCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
@@ -320,16 +347,18 @@ public:
 class StartRecordingResult : public nsRunnable
 {
 public:
   StartRecordingResult(nsIDOMMediaStream* aStream, nsICameraStartRecordingCallback* onSuccess)
     : mStream(aStream)
     , mOnSuccessCb(onSuccess)
   { }
 
+  virtual ~StartRecordingResult() { }
+
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (mOnSuccessCb) {
       mOnSuccessCb->HandleEvent(mStream);
     }
     return NS_OK;
@@ -339,100 +368,129 @@ protected:
   nsCOMPtr<nsIDOMMediaStream> mStream;
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
 };
 
 // Start video recording.
 class StartRecordingTask : public nsRunnable
 {
 public:
-  StartRecordingTask(nsCameraControl* aCameraControl, CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+  StartRecordingTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
     : mSize(aSize)
     , mCameraControl(aCameraControl)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StartRecordingTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->StartRecordingImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
       rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
   CameraSize mSize;
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Stop video recording.
 class StopRecordingTask : public nsRunnable
 {
 public:
-  StopRecordingTask(nsCameraControl* aCameraControl)
+  StopRecordingTask(CameraControlImpl* aCameraControl)
     : mCameraControl(aCameraControl)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StopRecordingTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->StopRecordingImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
 };
 
-// Pushes all camera parameters to the camera.
-class PushParametersTask : public nsRunnable
+// Start the preview.
+class StartPreviewTask : public nsRunnable
 {
 public:
-  PushParametersTask(nsCameraControl* aCameraControl)
+  StartPreviewTask(CameraControlImpl* aCameraControl, DOMCameraPreview* aDOMPreview)
     : mCameraControl(aCameraControl)
-  { }
+    , mDOMPreview(aDOMPreview)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StartPreviewTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
-    nsresult rv = mCameraControl->PushParametersImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    nsresult rv = mCameraControl->StartPreviewImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  DOMCameraPreview* mDOMPreview; // DOMCameraPreview NS_ADDREFs itself for us
 };
 
-// Get all camera parameters from the camera.
-class PullParametersTask : public nsRunnable
+// Stop the preview.
+class StopPreviewTask : public nsRunnable
 {
 public:
-  PullParametersTask(nsCameraControl* aCameraControl)
+  StopPreviewTask(CameraControlImpl* aCameraControl)
     : mCameraControl(aCameraControl)
-  { }
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~StopPreviewTask()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
 
   NS_IMETHOD Run()
   {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
-    nsresult rv = mCameraControl->PullParametersImpl(this);
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+    mCameraControl->StopPreviewImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
-    NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
-  nsCOMPtr<nsCameraControl> mCameraControl;
+  nsRefPtr<CameraControlImpl> mCameraControl;
 };
 
 } // namespace mozilla
 
-#endif // DOM_CAMERA_NSCAMERACONTROL_H
+#endif // DOM_CAMERA_CAMERACONTROLIMPL_H
deleted file mode 100644
--- a/dom/camera/CameraPreview.cpp
+++ /dev/null
@@ -1,114 +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 "CameraPreview.h"
-#include "Layers.h"
-#include "ImageContainer.h"
-#define DOM_CAMERA_LOG_LEVEL  3
-#include "CameraCommon.h"
-
-using namespace mozilla;
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(CameraPreview, CameraPreview)
-
-class CameraPreviewListener : public MediaStreamListener
-{
-public:
-  CameraPreviewListener(CameraPreview* aPreview) :
-    mPreview(aPreview)
-  {
-    DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-  }
-
-  ~CameraPreviewListener()
-  {
-    DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-  }
-
-  void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
-  {
-    const char* state;
-
-    DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-    switch (aConsuming) {
-      case NOT_CONSUMED:
-        state = "not consuming";
-        break;
-
-      case CONSUMED:
-        state = "consuming";
-        break;
-
-      default:
-        state = "unknown";
-        break;
-    }
-
-    DOM_CAMERA_LOGA("camera viewfinder is %s\n", state);
-
-    switch (aConsuming) {
-      case NOT_CONSUMED:
-        mPreview->Stop();
-        break;
-
-      case CONSUMED:
-        mPreview->Start();
-        break;
-    }
-  }
-
-protected:
-  nsCOMPtr<CameraPreview> mPreview;
-};
-
-CameraPreview::CameraPreview(nsIThread* aCameraThread, uint32_t aWidth, uint32_t aHeight)
-  : nsDOMMediaStream()
-  , mWidth(aWidth)
-  , mHeight(aHeight)
-  , mFramesPerSecond(0)
-  , mFrameCount(0)
-  , mCameraThread(aCameraThread)
-{
-  DOM_CAMERA_LOGI("%s:%d : mWidth=%d, mHeight=%d : this=%p\n", __func__, __LINE__, mWidth, mHeight, this);
-
-  mImageContainer = LayerManager::CreateImageContainer();
-  MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
-  mStream = gm->CreateInputStream(this);
-  mInput = GetStream()->AsSourceStream();
-  mInput->AddListener(new CameraPreviewListener(this));
-}
-
-void
-CameraPreview::SetFrameRate(uint32_t aFramesPerSecond)
-{
-  mFramesPerSecond = aFramesPerSecond;
-  mInput->AddTrack(TRACK_VIDEO, mFramesPerSecond, 0, new VideoSegment());
-  mInput->AdvanceKnownTracksTime(MEDIA_TIME_MAX);
-}
-
-void
-CameraPreview::Start()
-{
-  nsCOMPtr<nsIRunnable> cameraPreviewControl = NS_NewRunnableMethod(this, &CameraPreview::StartImpl);
-  nsresult rv = mCameraThread->Dispatch(cameraPreviewControl, NS_DISPATCH_NORMAL);
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("failed to start camera preview (%d)\n", rv);
-  }
-}
-
-void
-CameraPreview::Stop()
-{
-  nsCOMPtr<nsIRunnable> cameraPreviewControl = NS_NewRunnableMethod(this, &CameraPreview::StopImpl);
-  nsresult rv = mCameraThread->Dispatch(cameraPreviewControl, NS_DISPATCH_NORMAL);
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("failed to stop camera preview (%d)\n", rv);
-  }
-}
-
-CameraPreview::~CameraPreview()
-{
-  DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
deleted file mode 100644
--- a/dom/camera/CameraPreview.h
+++ /dev/null
@@ -1,62 +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_CAMERAPREVIEW_H
-#define DOM_CAMERA_CAMERAPREVIEW_H
-
-#include "MediaStreamGraph.h"
-#include "StreamBuffer.h"
-#include "nsDOMMediaStream.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
-#include "CameraCommon.h"
-
-using namespace mozilla;
-using namespace mozilla::layers;
-
-namespace mozilla {
-
-class CameraPreview : public nsDOMMediaStream
-                    , public MediaStreamListener
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  CameraPreview(nsIThread* aCameraThread, uint32_t aWidth, uint32_t aHeight);
-
-  void SetFrameRate(uint32_t aFramesPerSecond);
-
-  NS_IMETHODIMP
-  GetCurrentTime(double* aCurrentTime) {
-    return nsDOMMediaStream::GetCurrentTime(aCurrentTime);
-  }
-
-  void Start();
-  void Stop();
-
-  virtual nsresult StartImpl() = 0;
-  virtual nsresult StopImpl() = 0;
-
-protected:
-  virtual ~CameraPreview();
-
-  uint32_t mWidth;
-  uint32_t mHeight;
-  uint32_t mFramesPerSecond;
-  SourceMediaStream* mInput;
-  nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
-  VideoSegment mVideoSegment;
-  uint32_t mFrameCount;
-  nsCOMPtr<nsIThread> mCameraThread;
-
-  enum { TRACK_VIDEO = 1 };
-
-private:
-  CameraPreview(const CameraPreview&) MOZ_DELETE;
-  CameraPreview& operator=(const CameraPreview&) MOZ_DELETE;
-};
-
-} // namespace mozilla
-
-#endif // DOM_CAMERA_CAMERAPREVIEW_H
rename from dom/camera/GonkCameraCapabilities.cpp
rename to dom/camera/DOMCameraCapabilities.cpp
--- a/dom/camera/GonkCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -1,61 +1,52 @@
 /* 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 <string.h>
-#include <stdlib.h>
+#include <cstring>
+#include <cstdlib>
+#include "base/basictypes.h"
 #include "nsDOMClassInfo.h"
 #include "jsapi.h"
-#include "camera/CameraParameters.h"
-#include "CameraControl.h"
-#include "CameraCapabilities.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
+#include "DOMCameraControl.h"
+#include "DOMCameraCapabilities.h"
 #include "CameraCommon.h"
 
-using namespace android;
 using namespace mozilla;
 
 DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
 
-NS_INTERFACE_MAP_BEGIN(nsCameraCapabilities)
+NS_IMPL_CYCLE_COLLECTION_0(DOMCameraCapabilities)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCameraCapabilities)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_ADDREF(nsCameraCapabilities)
-NS_IMPL_RELEASE(nsCameraCapabilities)
-
-
-nsCameraCapabilities::nsCameraCapabilities(nsCameraControl* aCamera)
-  : mCamera(aCamera)
-{
-  // member initializers and constructor code
-  DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-nsCameraCapabilities::~nsCameraCapabilities()
-{
-  // destructor code
-  DOM_CAMERA_LOGI("%s:%d : this=%p, mCamera=%p\n", __func__, __LINE__, this, mCamera.get());
-}
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCameraCapabilities)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCameraCapabilities)
 
 static nsresult
 ParseZoomRatioItemAndAdd(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd)
 {
   if (!*aEnd) {
     // make 'aEnd' follow the same semantics as strchr().
     aEnd = nullptr;
   }
 
+  /**
+   * The by-100 divisor is Gonk-specific.  For now, assume other platforms
+   * return actual fractoinal multipliers.
+   */
   double d = strtod(aStart, aEnd);
+#if MOZ_WIDGET_GONK
   d /= 100;
+#endif
 
   jsval v = JS_NumberValue(d);
 
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
@@ -112,17 +103,17 @@ ParseDimensionItemAndAdd(JSContext* aCx,
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsCameraCapabilities::ParameterListToNewArray(JSContext* aCx, JSObject** aArray, uint32_t aKey, ParseItemAndAddFunc aParseItemAndAdd)
+DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx, JSObject** aArray, uint32_t aKey, ParseItemAndAddFunc aParseItemAndAdd)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
   const char* value = mCamera->GetParameterConstChar(aKey);
   if (!value) {
     // in case we get nonsense data back
     *aArray = nullptr;
     return NS_OK;
@@ -134,217 +125,226 @@ nsCameraCapabilities::ParameterListToNew
   }
 
   const char* p = value;
   uint32_t index = 0;
   nsresult rv;
   char* q;
 
   while (p) {
-    q = strchr(p, ',');
+    /**
+     * In C's string.h, strchr() is declared as returning 'char*'; in C++'s
+     * cstring, it is declared as returning 'const char*', _except_ in MSVC,
+     * where the C version is declared to return const like the C++ version.
+     *
+     * Unfortunately, for both cases, strtod() and strtol() take a 'char**' as
+     * the end-of-conversion pointer, so we need to cast away strchr()'s
+     * const-ness here to make the MSVC build everything happy.
+     */
+    q = const_cast<char*>(strchr(p, ','));
     if (q != p) { // skip consecutive delimiters, just in case
       rv = aParseItemAndAdd(aCx, *aArray, index, p, &q);
       NS_ENSURE_SUCCESS(rv, rv);
       ++index;
     }
     p = q;
     if (p) {
       ++p;
     }
   }
 
   return JS_FreezeObject(aCx, *aArray) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-nsCameraCapabilities::StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
+DOMCameraCapabilities::StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
 {
   JSObject* array;
 
   nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseStringItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aArray = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 nsresult
-nsCameraCapabilities::DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
+DOMCameraCapabilities::DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
 {
   JSObject* array;
   nsresult rv;
 
   rv = ParameterListToNewArray(aCx, &array, aKey, ParseDimensionItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aArray = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 /* readonly attribute jsval previewSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetPreviewSizes(JSContext* cx, JS::Value* aPreviewSizes)
+DOMCameraCapabilities::GetPreviewSizes(JSContext* cx, JS::Value* aPreviewSizes)
 {
-  return DimensionListToNewObject(cx, aPreviewSizes, nsCameraControl::CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
+  return DimensionListToNewObject(cx, aPreviewSizes, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
 }
 
 /* readonly attribute jsval pictureSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetPictureSizes(JSContext* cx, JS::Value* aPictureSizes)
+DOMCameraCapabilities::GetPictureSizes(JSContext* cx, JS::Value* aPictureSizes)
 {
-  return DimensionListToNewObject(cx, aPictureSizes, nsCameraControl::CAMERA_PARAM_SUPPORTED_PICTURESIZES);
+  return DimensionListToNewObject(cx, aPictureSizes, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
 }
 
 /* readonly attribute jsval fileFormats; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFileFormats(JSContext* cx, JS::Value* aFileFormats)
+DOMCameraCapabilities::GetFileFormats(JSContext* cx, JS::Value* aFileFormats)
 {
-  return StringListToNewObject(cx, aFileFormats, nsCameraControl::CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
+  return StringListToNewObject(cx, aFileFormats, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
 }
 
 /* readonly attribute jsval whiteBalanceModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetWhiteBalanceModes(JSContext* cx, JS::Value* aWhiteBalanceModes)
+DOMCameraCapabilities::GetWhiteBalanceModes(JSContext* cx, JS::Value* aWhiteBalanceModes)
 {
-  return StringListToNewObject(cx, aWhiteBalanceModes, nsCameraControl::CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
+  return StringListToNewObject(cx, aWhiteBalanceModes, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
 }
 
 /* readonly attribute jsval sceneModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetSceneModes(JSContext* cx, JS::Value* aSceneModes)
+DOMCameraCapabilities::GetSceneModes(JSContext* cx, JS::Value* aSceneModes)
 {
-  return StringListToNewObject(cx, aSceneModes, nsCameraControl::CAMERA_PARAM_SUPPORTED_SCENEMODES);
+  return StringListToNewObject(cx, aSceneModes, CAMERA_PARAM_SUPPORTED_SCENEMODES);
 }
 
 /* readonly attribute jsval effects; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetEffects(JSContext* cx, JS::Value* aEffects)
+DOMCameraCapabilities::GetEffects(JSContext* cx, JS::Value* aEffects)
 {
-  return StringListToNewObject(cx, aEffects, nsCameraControl::CAMERA_PARAM_SUPPORTED_EFFECTS);
+  return StringListToNewObject(cx, aEffects, CAMERA_PARAM_SUPPORTED_EFFECTS);
 }
 
 /* readonly attribute jsval flashModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFlashModes(JSContext* cx, JS::Value* aFlashModes)
+DOMCameraCapabilities::GetFlashModes(JSContext* cx, JS::Value* aFlashModes)
 {
-  return StringListToNewObject(cx, aFlashModes, nsCameraControl::CAMERA_PARAM_SUPPORTED_FLASHMODES);
+  return StringListToNewObject(cx, aFlashModes, CAMERA_PARAM_SUPPORTED_FLASHMODES);
 }
 
 /* readonly attribute jsval focusModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFocusModes(JSContext* cx, JS::Value* aFocusModes)
+DOMCameraCapabilities::GetFocusModes(JSContext* cx, JS::Value* aFocusModes)
 {
-  return StringListToNewObject(cx, aFocusModes, nsCameraControl::CAMERA_PARAM_SUPPORTED_FOCUSMODES);
+  return StringListToNewObject(cx, aFocusModes, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
 }
 
 /* readonly attribute long maxFocusAreas; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
+DOMCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
   if (!value) {
     // in case we get nonsense data back
     *aMaxFocusAreas = 0;
     return NS_OK;
   }
 
   *aMaxFocusAreas = atoi(value);
   return NS_OK;
 }
 
 /* readonly attribute double minExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
+DOMCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
   if (!value) {
     // in case we get nonsense data back
     *aMinExposureCompensation = 0;
     return NS_OK;
   }
 
   *aMinExposureCompensation = atof(value);
   return NS_OK;
 }
 
 /* readonly attribute double maxExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
+DOMCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
   if (!value) {
     // in case we get nonsense data back
     *aMaxExposureCompensation = 0;
     return NS_OK;
   }
 
   *aMaxExposureCompensation = atof(value);
   return NS_OK;
 }
 
 /* readonly attribute double stepExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
+DOMCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
   if (!value) {
     // in case we get nonsense data back
     *aStepExposureCompensation = 0;
     return NS_OK;
   }
 
   *aStepExposureCompensation = atof(value);
   return NS_OK;
 }
 
 /* readonly attribute long maxMeteringAreas; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
+DOMCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
   if (!value) {
     // in case we get nonsense data back
     *aMaxMeteringAreas = 0;
     return NS_OK;
   }
 
   *aMaxMeteringAreas = atoi(value);
   return NS_OK;
 }
 
 /* readonly attribute jsval zoomRatios; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetZoomRatios(JSContext* cx, JS::Value* aZoomRatios)
+DOMCameraCapabilities::GetZoomRatios(JSContext* cx, JS::Value* aZoomRatios)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
-  const char* value = mCamera->GetParameterConstChar(nsCameraControl::CAMERA_PARAM_SUPPORTED_ZOOM);
-  if (!value || strcmp(value, CameraParameters::TRUE) != 0) {
+  const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_ZOOM);
+  if (!value || strcmp(value, "true") != 0) {
     // if zoom is not supported, return a null object
     *aZoomRatios = JSVAL_NULL;
     return NS_OK;
   }
 
   JSObject* array;
 
-  nsresult rv = ParameterListToNewArray(cx, &array, nsCameraControl::CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd);
+  nsresult rv = ParameterListToNewArray(cx, &array, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aZoomRatios = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 /* readonly attribute jsval videoSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetVideoSizes(JSContext* cx, JS::Value* aVideoSizes)
+DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::Value* aVideoSizes)
 {
-  return DimensionListToNewObject(cx, aVideoSizes, nsCameraControl::CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
+  return DimensionListToNewObject(cx, aVideoSizes, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
 }
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -0,0 +1,56 @@
+/* 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_DOMCAMERACAPABILITIES_H
+#define DOM_CAMERA_DOMCAMERACAPABILITIES_H
+
+#include "nsCycleCollectionParticipant.h"
+#include "ICameraControl.h"
+#include "nsAutoPtr.h"
+#include "CameraCommon.h"
+
+namespace mozilla {
+
+typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd);
+
+class DOMCameraCapabilities MOZ_FINAL : public nsICameraCapabilities
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DOMCameraCapabilities)
+  NS_DECL_NSICAMERACAPABILITIES
+
+  DOMCameraCapabilities(ICameraControl* aCamera)
+    : mCamera(aCamera)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  nsresult ParameterListToNewArray(
+    JSContext* cx,
+    JSObject** aArray,
+    uint32_t aKey,
+    ParseItemAndAddFunc aParseItemAndAdd
+  );
+  nsresult StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
+  nsresult DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
+
+private:
+  DOMCameraCapabilities(const DOMCameraCapabilities&) MOZ_DELETE;
+  DOMCameraCapabilities& operator=(const DOMCameraCapabilities&) MOZ_DELETE;
+
+protected:
+  /* additional members */
+  ~DOMCameraCapabilities()
+  {
+    // destructor code
+    DOM_CAMERA_LOGT("%s:%d : this=%p, mCamera=%p\n", __func__, __LINE__, this, mCamera.get());
+  }
+
+  nsRefPtr<ICameraControl> mCamera;
+};
+
+} // namespace mozilla
+
+#endif // DOM_CAMERA_DOMCAMERACAPABILITIES_H
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraControl.cpp
@@ -0,0 +1,370 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+#include "nsCOMPtr.h"
+#include "nsDOMClassInfo.h"
+#include "jsapi.h"
+#include "nsThread.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "DOMCameraManager.h"
+#include "DOMCameraCapabilities.h"
+#include "DOMCameraControl.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+using namespace dom;
+
+DOMCI_DATA(CameraControl, nsICameraControl)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMCameraControl)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMCapabilities)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMCameraControl)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDOMCapabilities)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraControl)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsICameraControl)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraControl)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraControl)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraControl)
+
+nsDOMCameraControl::~nsDOMCameraControl()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+/* readonly attribute nsICameraCapabilities capabilities; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetCapabilities(nsICameraCapabilities** aCapabilities)
+{
+  if (!mDOMCapabilities) {
+    mDOMCapabilities = new DOMCameraCapabilities(mCameraControl);
+  }
+
+  nsCOMPtr<nsICameraCapabilities> capabilities = mDOMCapabilities;
+  capabilities.forget(aCapabilities);
+  return NS_OK;
+}
+
+/* attribute DOMString effect; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetEffect(nsAString& aEffect)
+{
+  return mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetEffect(const nsAString& aEffect)
+{
+  return mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect);
+}
+
+/* attribute DOMString whiteBalanceMode; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetWhiteBalanceMode(nsAString& aWhiteBalanceMode)
+{
+  return mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode)
+{
+  return mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
+}
+
+/* attribute DOMString sceneMode; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetSceneMode(nsAString& aSceneMode)
+{
+  return mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode)
+{
+  return mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode);
+}
+
+/* attribute DOMString flashMode; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFlashMode(nsAString& aFlashMode)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode)
+{
+  return mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode);
+}
+
+/* attribute DOMString focusMode; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocusMode(nsAString& aFocusMode)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode)
+{
+  return mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
+}
+
+/* attribute double zoom; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetZoom(double* aZoom)
+{
+  return mCameraControl->Get(CAMERA_PARAM_ZOOM, aZoom);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetZoom(double aZoom)
+{
+  return mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
+}
+
+/* attribute jsval meteringAreas; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetMeteringAreas(JSContext* cx, JS::Value* aMeteringAreas)
+{
+  return mCameraControl->Get(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetMeteringAreas(JSContext* cx, const JS::Value& aMeteringAreas)
+{
+  return mCameraControl->SetMeteringAreas(cx, aMeteringAreas);
+}
+
+/* attribute jsval focusAreas; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocusAreas(JSContext* cx, JS::Value* aFocusAreas)
+{
+  return mCameraControl->Get(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetFocusAreas(JSContext* cx, const JS::Value& aFocusAreas)
+{
+  return mCameraControl->SetFocusAreas(cx, aFocusAreas);
+}
+
+/* readonly attribute double focalLength; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocalLength(double* aFocalLength)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, aFocalLength);
+}
+
+/* readonly attribute double focusDistanceNear; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocusDistanceNear(double* aFocusDistanceNear)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, aFocusDistanceNear);
+}
+
+/* readonly attribute double focusDistanceOptimum; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocusDistanceOptimum(double* aFocusDistanceOptimum)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, aFocusDistanceOptimum);
+}
+
+/* readonly attribute double focusDistanceFar; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetFocusDistanceFar(double* aFocusDistanceFar)
+{
+  return mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, aFocusDistanceFar);
+}
+
+/* void setExposureCompensation (const JS::Value& aCompensation, JSContext* cx); */
+NS_IMETHODIMP
+nsDOMCameraControl::SetExposureCompensation(const JS::Value& aCompensation, JSContext* cx)
+{
+  if (aCompensation.isNullOrUndefined()) {
+    // use NaN to switch the camera back into auto mode
+    return mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
+  }
+
+  double compensation;
+  if (!JS_ValueToNumber(cx, aCompensation, &compensation)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
+}
+
+/* readonly attribute double exposureCompensation; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetExposureCompensation(double* aExposureCompensation)
+{
+  return mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, aExposureCompensation);
+}
+
+/* attribute nsICameraShutterCallback onShutter; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetOnShutter(nsICameraShutterCallback** aOnShutter)
+{
+  // TODO: see bug 779138.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter)
+{
+  // TODO: see bug 779138.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void startRecording (in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
+NS_IMETHODIMP
+nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
+{
+  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
+
+  CameraSize size;
+  nsresult rv = size.Init(cx, &aOptions);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
+    return NS_ERROR_FAILURE;
+  }
+
+  obs->NotifyObservers(nullptr,
+                       "recording-device-events",
+                       NS_LITERAL_STRING("starting").get());
+
+  return mCameraControl->StartRecording(size, onSuccess, onError);
+}
+
+/* void stopRecording (); */
+NS_IMETHODIMP
+nsDOMCameraControl::StopRecording()
+{
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    NS_WARNING("Could not get the Observer service for CameraControl::StopRecording.");
+    return NS_ERROR_FAILURE;
+  }
+
+  obs->NotifyObservers(nullptr,
+                       "recording-device-events",
+                       NS_LITERAL_STRING("shutdown").get());
+
+  return mCameraControl->StopRecording();
+}
+
+/* [implicit_jscontext] void getPreviewStream (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
+NS_IMETHODIMP
+nsDOMCameraControl::GetPreviewStream(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
+{
+  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
+
+  CameraSize size;
+  nsresult rv = size.Init(cx, &aOptions);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return mCameraControl->GetPreviewStream(size, onSuccess, onError);
+}
+
+/* void resumePreview(); */
+NS_IMETHODIMP
+nsDOMCameraControl::ResumePreview()
+{
+  return mCameraControl->StartPreview(nullptr);
+}
+
+/* void autoFocus (in nsICameraAutoFocusCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
+NS_IMETHODIMP
+nsDOMCameraControl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
+  return mCameraControl->AutoFocus(onSuccess, onError);
+}
+
+/* void takePicture (in jsval aOptions, in nsICameraTakePictureCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
+NS_IMETHODIMP
+nsDOMCameraControl::TakePicture(const JS::Value& aOptions, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
+{
+  NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
+
+  CameraPictureOptions  options;
+  CameraSize            size;
+  CameraPosition        pos;
+
+  nsresult rv = options.Init(cx, &aOptions);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = size.Init(cx, &options.pictureSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  /**
+   * Default values, until the dictionary parser can handle them.
+   * NaN indicates no value provided.
+   */
+  pos.latitude = NAN;
+  pos.longitude = NAN;
+  pos.altitude = NAN;
+  pos.timestamp = NAN;
+  rv = pos.Init(cx, &options.position);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return mCameraControl->TakePicture(size, options.rotation, options.fileFormat, pos, onSuccess, onError);
+}
+
+class GetCameraResult : public nsRunnable
+{
+public:
+  GetCameraResult(nsDOMCameraControl* aDOMCameraControl, nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mDOMCameraControl(aDOMCameraControl)
+    , mResult(aResult)
+    , mOnSuccessCb(onSuccess)
+    , mOnErrorCb(onError)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DOM_CAMERA_LOGT("%s : this=%p -- BEFORE CALLBACK\n", __func__, this);
+    if (NS_FAILED(mResult)) {
+      if (mOnErrorCb) {
+        mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+      }
+    } else {
+      if (mOnSuccessCb) {
+        mOnSuccessCb->HandleEvent(mDOMCameraControl);
+      }
+    }
+    DOM_CAMERA_LOGT("%s : this=%p -- AFTER CALLBACK\n", __func__, this);
+
+    /**
+     * Finally, release the extra reference to the DOM-facing camera control.
+     * See the nsDOMCameraControl constructor for the corresponding call to
+     * NS_ADDREF_THIS().
+     */
+    NS_RELEASE(mDOMCameraControl);
+    return NS_OK;
+  }
+
+protected:
+  /**
+   * 'mDOMCameraControl' is a raw pointer to a previously ADDREF()ed object,
+   * which is released in Run().
+   */
+  nsDOMCameraControl* mDOMCameraControl;
+  nsresult mResult;
+  nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+};
+
+nsresult
+nsDOMCameraControl::Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  nsCOMPtr<GetCameraResult> getCameraResult = new GetCameraResult(this, aResult, onSuccess, onError);
+  return NS_DispatchToMainThread(getCameraResult);
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraControl.h
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DOM_CAMERA_DOMCAMERACONTROL_H
+#define DOM_CAMERA_DOMCAMERACONTROL_H
+
+#include "base/basictypes.h"
+#include "prtypes.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "DictionaryHelpers.h"
+#include "ICameraControl.h"
+#include "DOMCameraPreview.h"
+#include "nsIDOMCameraManager.h"
+#include "CameraCommon.h"
+
+namespace mozilla {
+
+using namespace mozilla;
+using namespace dom;
+
+// Main camera control.
+class nsDOMCameraControl : public nsICameraControl
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl)
+  NS_DECL_NSICAMERACONTROL
+
+  nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+
+protected:
+  virtual ~nsDOMCameraControl();
+
+private:
+  nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
+  nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
+
+protected:
+  /* additional members */
+  nsRefPtr<ICameraControl>        mCameraControl; // non-DOM camera control
+  nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
+};
+
+} // namespace mozilla
+
+#endif // DOM_CAMERA_DOMCAMERACONTROL_H
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -1,65 +1,77 @@
 /* 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 "CameraControl.h"
+#include "DOMCameraControl.h"
 #include "DOMCameraManager.h"
 #include "nsDOMClassInfo.h"
 #include "DictionaryHelpers.h"
-
-#undef DOM_CAMERA_LOG_LEVEL
-#define DOM_CAMERA_LOG_LEVEL  DOM_CAMERA_LOG_NOTHING
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
 DOMCI_DATA(CameraManager, nsIDOMCameraManager)
 
 NS_INTERFACE_MAP_BEGIN(nsDOMCameraManager)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCameraManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsDOMCameraManager)
 NS_IMPL_RELEASE(nsDOMCameraManager)
 
 /**
+ * Global camera logging object
+ *
+ * Set the NSPR_LOG_MODULES environment variable to enable logging
+ * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
+ */
+#ifdef PR_LOGGING
+PRLogModuleInfo* gCameraLog;
+#endif
+
+/**
  * nsDOMCameraManager::GetListOfCameras
  * is implementation-specific, and can be found in (e.g.)
  * GonkCameraManager.cpp and FallbackCameraManager.cpp.
  */
 
 nsDOMCameraManager::nsDOMCameraManager(uint64_t aWindowId)
   : mWindowId(aWindowId)
 {
   /* member initializers and constructor code */
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
 }
 
 nsDOMCameraManager::~nsDOMCameraManager()
 {
   /* destructor code */
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 void
 nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
 {
-  // TODO: implement -- see getUserMedia() implementation
+  // TODO: see bug 779145.
 }
 
 // static creator
 already_AddRefed<nsDOMCameraManager>
 nsDOMCameraManager::Create(uint64_t aWindowId)
 {
-  // TODO: check for permissions here to access cameras
+  // TODO: see bug 776934.
 
+#ifdef PR_LOGGING
+  if (!gCameraLog) {
+    gCameraLog = PR_LOG_DEFINE("Camera");
+  }
+#endif
   nsRefPtr<nsDOMCameraManager> cameraManager = new nsDOMCameraManager(aWindowId);
   return cameraManager.forget();
 }
 
 /* [implicit_jscontext] void getCamera ([optional] in jsval aOptions, in nsICameraGetCameraCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraManager::GetCamera(const JS::Value& aOptions, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
@@ -76,15 +88,15 @@ nsDOMCameraManager::GetCamera(const JS::
   }
 
   // reuse the same camera thread to conserve resources
   if (!mCameraThread) {
     rv = NS_NewThread(getter_AddRefs(mCameraThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
-  nsCOMPtr<nsIRunnable> getCameraTask = new GetCameraTask(cameraId, onSuccess, onError, mCameraThread);
-  mCameraThread->Dispatch(getCameraTask, NS_DISPATCH_NORMAL);
+  // Creating this object will trigger the onSuccess handler
+  nsCOMPtr<nsICameraControl> cameraControl = new nsDOMCameraControl(cameraId, mCameraThread, onSuccess, onError);
 
   return NS_OK;
 }
--- a/dom/camera/DOMCameraManager.h
+++ b/dom/camera/DOMCameraManager.h
@@ -31,17 +31,16 @@ private:
   nsDOMCameraManager& operator=(const nsDOMCameraManager&) MOZ_DELETE;
   ~nsDOMCameraManager();
 
 protected:
   uint64_t mWindowId;
   nsCOMPtr<nsIThread> mCameraThread;
 };
 
-
 class GetCameraTask : public nsRunnable
 {
 public:
   GetCameraTask(uint32_t aCameraId, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsIThread* aCameraThread)
     : mCameraId(aCameraId)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
     , mCameraThread(aCameraThread)
@@ -51,33 +50,9 @@ public:
 
 protected:
   uint32_t mCameraId;
   nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
   nsCOMPtr<nsIThread> mCameraThread;
 };
 
-class GetCameraResult : public nsRunnable
-{
-public:
-  GetCameraResult(nsICameraControl* aCameraControl, nsICameraGetCameraCallback* onSuccess)
-    : mCameraControl(aCameraControl)
-    , mOnSuccessCb(onSuccess)
-  { }
-
-  NS_IMETHOD Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // TODO: window management stuff
-    if (mOnSuccessCb) {
-      mOnSuccessCb->HandleEvent(mCameraControl);
-    }
-    return NS_OK;
-  }
-
-protected:
-  nsCOMPtr<nsICameraControl> mCameraControl;
-  nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
-};
-
 #endif // DOM_CAMERA_DOMCAMERAMANAGER_H
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraPreview.cpp
@@ -0,0 +1,289 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+#include "Layers.h"
+#include "VideoUtils.h"
+#include "DOMCameraPreview.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+
+/**
+ * 'PreviewControl' is a helper class that dispatches preview control
+ * events from the main thread.
+ *
+ * NS_NewRunnableMethod() can't be used because it AddRef()s the method's
+ * object, which can't be done off the main thread for cycle collection
+ * participants.
+ *
+ * Before using this class, 'aDOMPreview' must be appropriately AddRef()ed.
+ */
+class PreviewControl : public nsRunnable
+{
+public:
+  enum {
+    START,
+    STOP,
+    STARTED,
+    STOPPED
+  };
+  PreviewControl(DOMCameraPreview* aDOMPreview, uint32_t aControl)
+    : mDOMPreview(aDOMPreview)
+    , mControl(aControl)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "PreviewControl not run on main thread");
+
+    switch (mControl) {
+      case START:
+        mDOMPreview->Start();
+        break;
+
+      case STOP:
+        mDOMPreview->Stop();
+        break;
+
+      case STARTED:
+        mDOMPreview->SetStateStarted();
+        break;
+
+      case STOPPED:
+        mDOMPreview->SetStateStopped();
+        break;
+
+      default:
+        DOM_CAMERA_LOGE("PreviewControl: invalid control %d\n", mControl);
+        break;
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  /**
+   * This must be a raw pointer because this class is not created on the
+   * main thread, and DOMCameraPreview is not threadsafe.  Prior to
+   * issuing a preview control event, the caller must ensure that
+   * mDOMPreview will not disappear.
+   */
+  DOMCameraPreview* mDOMPreview;
+  uint32_t mControl;
+};
+
+class DOMCameraPreviewListener : public MediaStreamListener
+{
+public:
+  DOMCameraPreviewListener(DOMCameraPreview* aDOMPreview) :
+    mDOMPreview(aDOMPreview)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  ~DOMCameraPreviewListener()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
+  {
+    const char* state;
+
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+
+    switch (aConsuming) {
+      case NOT_CONSUMED:
+        state = "not consuming";
+        break;
+
+      case CONSUMED:
+        state = "consuming";
+        break;
+
+      default:
+        state = "unknown";
+        break;
+    }
+
+    DOM_CAMERA_LOGA("camera viewfinder is %s\n", state);
+    nsCOMPtr<nsIRunnable> previewControl;
+
+    switch (aConsuming) {
+      case NOT_CONSUMED:
+        previewControl = new PreviewControl(mDOMPreview, PreviewControl::STOP);
+        break;
+
+      case CONSUMED:
+        previewControl = new PreviewControl(mDOMPreview, PreviewControl::START);
+        break;
+
+      default:
+        return;
+    }
+
+    nsresult rv = NS_DispatchToMainThread(previewControl);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGE("Failed to dispatch preview control (%d)!\n", rv);
+    }
+  }
+
+protected:
+  // Raw pointer; if we exist, 'mDOMPreview' exists as well
+  DOMCameraPreview* mDOMPreview;
+};
+
+DOMCameraPreview::DOMCameraPreview(ICameraControl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFrameRate)
+  : nsDOMMediaStream()
+  , mState(STOPPED)
+  , mWidth(aWidth)
+  , mHeight(aHeight)
+  , mFramesPerSecond(aFrameRate)
+  , mFrameCount(0)
+  , mCameraControl(aCameraControl)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p : mWidth=%d, mHeight=%d, mFramesPerSecond=%d\n", __func__, __LINE__, this, mWidth, mHeight, mFramesPerSecond);
+
+  mImageContainer = LayerManager::CreateImageContainer();
+  MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
+  mStream = gm->CreateInputStream(this);
+  mInput = GetStream()->AsSourceStream();
+
+  mListener = new DOMCameraPreviewListener(this);
+  mInput->AddListener(mListener);
+
+  mInput->AddTrack(TRACK_VIDEO, mFramesPerSecond, 0, new VideoSegment());
+  mInput->AdvanceKnownTracksTime(MEDIA_TIME_MAX);
+}
+
+DOMCameraPreview::~DOMCameraPreview()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mListener=%p\n", __func__, __LINE__, this, mListener);
+  mInput->RemoveListener(mListener);
+}
+
+bool
+DOMCameraPreview::HaveEnoughBuffered()
+{
+  return mInput->HaveEnoughBuffered(TRACK_VIDEO);
+}
+
+void
+DOMCameraPreview::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  if (!aBuffer || !aBuilder) {
+    return;
+  }
+  if (mState != STARTED) {
+    return;
+  }
+
+  ImageFormat format = aFormat;
+  nsRefPtr<Image> image = mImageContainer->CreateImage(&format, 1);
+  aBuilder(image, aBuffer, mWidth, mHeight);
+
+  // AppendFrame() takes over image's reference
+  mVideoSegment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight));
+  mInput->AppendToTrack(TRACK_VIDEO, &mVideoSegment);
+}
+
+void
+DOMCameraPreview::Start()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Start() not called from main thread");
+  if (mState != STOPPED) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Starting preview stream\n");
+
+  /**
+   * Add a reference to ourselves to make sure we stay alive while
+   * the preview is running, as the CameraControlImpl object holds a
+   * weak reference to us.
+   *
+   * This reference is removed in SetStateStopped().
+   */
+  NS_ADDREF_THIS();
+  mState = STARTING;
+  mCameraControl->StartPreview(this);
+}
+
+void
+DOMCameraPreview::SetStateStarted()
+{
+  NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread");
+
+  mState = STARTED;
+  DOM_CAMERA_LOGI("Preview stream started\n");
+}
+
+void
+DOMCameraPreview::Started()
+{
+  if (mState != STARTING) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Dispatching preview stream started\n");
+  nsCOMPtr<nsIRunnable> started = new PreviewControl(this, PreviewControl::STARTED);
+  nsresult rv = NS_DispatchToMainThread(started);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("failed to set statrted state (%d), POTENTIAL MEMORY LEAK!\n", rv);
+  }
+}
+
+void
+DOMCameraPreview::Stop()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Stop() not called from main thread");
+  if (mState != STARTED) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Stopping preview stream\n");
+  mState = STOPPING;
+  mCameraControl->StopPreview();
+  mInput->EndTrack(TRACK_VIDEO);
+  mInput->Finish();
+}
+
+void
+DOMCameraPreview::SetStateStopped()
+{
+  NS_ASSERTION(NS_IsMainThread(), "SetStateStopped() not called from main thread");
+
+  mState = STOPPED;
+  DOM_CAMERA_LOGI("Preview stream stopped\n");
+
+  /**
+   * Only remove the reference added in Start() once the preview
+   * has stopped completely.
+   */
+  NS_RELEASE_THIS();
+}
+
+void
+DOMCameraPreview::Stopped(bool aForced)
+{
+  if (mState != STOPPING && !aForced) {
+    return;
+  }
+
+  DOM_CAMERA_LOGI("Dispatching preview stream stopped\n");
+  nsCOMPtr<nsIRunnable> stopped = new PreviewControl(this, PreviewControl::STOPPED);
+  nsresult rv = NS_DispatchToMainThread(stopped);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("failed to decrement reference count (%d), MEMORY LEAK!\n", rv);
+  }
+}
+
+void
+DOMCameraPreview::Error()
+{
+  DOM_CAMERA_LOGE("Error occurred changing preview state!\n");
+  Stopped(true);
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/DOMCameraPreview.h
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DOM_CAMERA_DOMCAMERAPREVIEW_H
+#define DOM_CAMERA_DOMCAMERAPREVIEW_H
+
+#include "nsCycleCollectionParticipant.h"
+#include "MediaStreamGraph.h"
+#include "StreamBuffer.h"
+#include "ICameraControl.h"
+#include "nsDOMMediaStream.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+
+namespace mozilla {
+
+typedef void (*FrameBuilder)(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight);
+
+/**
+ * DOMCameraPreview is only exposed to the DOM as an nsDOMMediaStream,
+ * which is a cycle-collection participant already.
+ */
+class DOMCameraPreview : public nsDOMMediaStream
+{
+protected:
+  enum { TRACK_VIDEO = 1 };
+
+public:
+  DOMCameraPreview(ICameraControl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond = 30);
+  void ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
+  bool HaveEnoughBuffered();
+
+  NS_IMETHODIMP
+  GetCurrentTime(double* aCurrentTime) {
+    return nsDOMMediaStream::GetCurrentTime(aCurrentTime);
+  }
+
+  void Start();   // called by the MediaStreamListener to start preview
+  void Started(); // called by the CameraControl when preview is started
+  void Stop();    // called by the MediaStreamListener to stop preview
+  void Stopped(bool aForced = false);
+                  // called by the CameraControl when preview is stopped
+  void Error();   // something went wrong, NS_RELEASE needed
+
+  void SetStateStarted();
+  void SetStateStopped();
+
+protected:
+  virtual ~DOMCameraPreview();
+
+  enum {
+    STOPPED,
+    STARTING,
+    STARTED,
+    STOPPING
+  };
+  uint32_t mState;
+
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mFramesPerSecond;
+  SourceMediaStream* mInput;
+  nsRefPtr<ImageContainer> mImageContainer;
+  VideoSegment mVideoSegment;
+  uint32_t mFrameCount;
+  nsRefPtr<ICameraControl> mCameraControl;
+
+  // Raw pointer; AddListener() keeps the reference for us
+  MediaStreamListener* mListener;
+
+private:
+  DOMCameraPreview(const DOMCameraPreview&) MOZ_DELETE;
+  DOMCameraPreview& operator=(const DOMCameraPreview&) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#endif // DOM_CAMERA_DOMCAMERAPREVIEW_H
--- a/dom/camera/FallbackCameraCapabilities.cpp
+++ b/dom/camera/FallbackCameraCapabilities.cpp
@@ -1,140 +1,128 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMClassInfoID.h"
-#include "CameraControl.h"
-#include "CameraCapabilities.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
+#include "DOMCameraControl.h"
+#include "DOMCameraCapabilities.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
 DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
 
-NS_INTERFACE_MAP_BEGIN(nsCameraCapabilities)
+NS_IMPL_CYCLE_COLLECTION_0(DOMCameraCapabilities)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCameraCapabilities)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_ADDREF(nsCameraCapabilities)
-NS_IMPL_RELEASE(nsCameraCapabilities)
-
-nsCameraCapabilities::nsCameraCapabilities(nsCameraControl* aCamera)
-  : mCamera(aCamera)
-{
-  /* member initializers and constructor code */
-  DOM_CAMERA_LOGI("%s:%d : FALLBACK CAMERA CAPABILITIES\n", __func__, __LINE__);
-}
-
-nsCameraCapabilities::~nsCameraCapabilities()
-{
-  /* destructor code */
-}
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCameraCapabilities)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCameraCapabilities)
 
 /* [implicit_jscontext] readonly attribute jsval previewSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetPreviewSizes(JSContext* cx, JS::Value* aPreviewSizes)
+DOMCameraCapabilities::GetPreviewSizes(JSContext* cx, JS::Value* aPreviewSizes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval pictureSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetPictureSizes(JSContext* cx, JS::Value* aPictureSizes)
+DOMCameraCapabilities::GetPictureSizes(JSContext* cx, JS::Value* aPictureSizes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval fileFormats; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFileFormats(JSContext* cx, JS::Value* aFileFormats)
+DOMCameraCapabilities::GetFileFormats(JSContext* cx, JS::Value* aFileFormats)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval whiteBalanceModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetWhiteBalanceModes(JSContext* cx, JS::Value* aWhiteBalanceModes)
+DOMCameraCapabilities::GetWhiteBalanceModes(JSContext* cx, JS::Value* aWhiteBalanceModes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval sceneModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetSceneModes(JSContext* cx, JS::Value* aSceneModes)
+DOMCameraCapabilities::GetSceneModes(JSContext* cx, JS::Value* aSceneModes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval effects; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetEffects(JSContext* cx, JS::Value* aEffects)
+DOMCameraCapabilities::GetEffects(JSContext* cx, JS::Value* aEffects)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval flashModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFlashModes(JSContext* cx, JS::Value* aFlashModes)
+DOMCameraCapabilities::GetFlashModes(JSContext* cx, JS::Value* aFlashModes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval focusModes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetFocusModes(JSContext* cx, JS::Value* aFocusModes)
+DOMCameraCapabilities::GetFocusModes(JSContext* cx, JS::Value* aFocusModes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute long maxFocusAreas; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
+DOMCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute double minExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
+DOMCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute double maxExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
+DOMCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute double stepExposureCompensation; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
+DOMCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute long maxMeteringAreas; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
+DOMCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval zoomRatios; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetZoomRatios(JSContext* cx, JS::Value* aZoomRatios)
+DOMCameraCapabilities::GetZoomRatios(JSContext* cx, JS::Value* aZoomRatios)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* [implicit_jscontext] readonly attribute jsval videoSizes; */
 NS_IMETHODIMP
-nsCameraCapabilities::GetVideoSizes(JSContext* cx, JS::Value* aVideoSizes)
+DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::Value* aVideoSizes)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -1,65 +1,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/. */
 
-#include "nsDOMClassInfoID.h"
-#include "DOMCameraManager.h"
-#include "CameraControl.h"
+#include "DOMCameraControl.h"
+#include "CameraControlImpl.h"
 
 using namespace mozilla;
 
 /**
  * Fallback camera control subclass.  Can be used as a template for the
  * definition of new camera support classes.
  */
-class nsFallbackCameraControl : public nsCameraControl
+class nsFallbackCameraControl : public CameraControlImpl
 {
 public:
-  nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread);
+  nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions);
-  void PushParameters();
+  nsresult PushParameters();
 
 protected:
   ~nsFallbackCameraControl();
 
   nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
+  nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
+  nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
   nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
   nsresult TakePictureImpl(TakePictureTask* aTakePicture);
   nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
   nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
-  nsresult PushParametersImpl(PushParametersTask* aPushParameters);
-  nsresult PullParametersImpl(PullParametersTask* aPullParameters);
+  nsresult PushParametersImpl();
+  nsresult PullParametersImpl();
 
 private:
   nsFallbackCameraControl(const nsFallbackCameraControl&) MOZ_DELETE;
   nsFallbackCameraControl& operator=(const nsFallbackCameraControl&) MOZ_DELETE;
 };
 
 /**
+ * Stub implementation of the DOM-facing camera control constructor.
+ *
+ * This should never get called--it exists to keep the linker happy; if
+ * implemented, it should construct (e.g.) nsFallbackCameraControl and
+ * store a reference in the 'mCameraControl' member (which is why it is
+ * defined here).
+ */
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+}
+
+/**
  * Stub implemetations of the fallback camera control.
  *
  * None of these should ever get called--they exist to keep the linker happy,
  * and may be used as templates for new camera support classes.
  */
-nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread)
-  : nsCameraControl(aCameraId, aCameraThread)
-{ }
+nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+  : CameraControlImpl(aCameraId, aCameraThread)
+{
+}
 
 nsFallbackCameraControl::~nsFallbackCameraControl()
-{ }
+{
+}
 
 const char*
 nsFallbackCameraControl::GetParameter(const char* aKey)
 {
   return nullptr;
 }
 
 const char*
@@ -94,28 +109,41 @@ nsFallbackCameraControl::SetParameter(ui
 {
 }
 
 void
 nsFallbackCameraControl::SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions)
 {
 }
 
-void
+nsresult
 nsFallbackCameraControl::PushParameters()
 {
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsFallbackCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
+nsFallbackCameraControl::StartPreviewImpl(StartPreviewTask* aGetPreviewStream)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFallbackCameraControl::StopPreviewImpl(StopPreviewTask* aGetPreviewStream)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
 nsFallbackCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsFallbackCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
 {
@@ -130,18 +158,18 @@ nsFallbackCameraControl::StartRecordingI
 
 nsresult
 nsFallbackCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
-nsFallbackCameraControl::PushParametersImpl(PushParametersTask* aPushParameters)
+nsFallbackCameraControl::PushParametersImpl()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
-nsFallbackCameraControl::PullParametersImpl(PullParametersTask* aPullParameters)
+nsFallbackCameraControl::PullParametersImpl()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/camera/FallbackCameraManager.cpp
+++ b/dom/camera/FallbackCameraManager.cpp
@@ -7,16 +7,8 @@
 // From nsDOMCameraManager.
 
 /* [implicit_jscontext] jsval getListOfCameras (); */
 NS_IMETHODIMP
 nsDOMCameraManager::GetListOfCameras(JSContext* cx, JS::Value* _retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
-
-using namespace mozilla;
-
-NS_IMETHODIMP
-GetCameraTask::Run()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -21,132 +21,226 @@
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "nsMemory.h"
 #include "jsapi.h"
 #include "nsThread.h"
 #include "nsPrintfCString.h"
 #include "DOMCameraManager.h"
 #include "GonkCameraHwMgr.h"
-#include "CameraCapabilities.h"
+#include "DOMCameraCapabilities.h"
+#include "DOMCameraControl.h"
 #include "GonkCameraControl.h"
-#include "GonkCameraPreview.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
 using namespace mozilla;
+using namespace android;
 
 static const char* getKeyText(uint32_t aKey)
 {
   switch (aKey) {
-    case nsCameraControl::CAMERA_PARAM_EFFECT:
+    case CAMERA_PARAM_EFFECT:
       return CameraParameters::KEY_EFFECT;
-    case nsCameraControl::CAMERA_PARAM_WHITEBALANCE:
+    case CAMERA_PARAM_WHITEBALANCE:
       return CameraParameters::KEY_WHITE_BALANCE;
-    case nsCameraControl::CAMERA_PARAM_SCENEMODE:
+    case CAMERA_PARAM_SCENEMODE:
       return CameraParameters::KEY_SCENE_MODE;
-    case nsCameraControl::CAMERA_PARAM_FLASHMODE:
+    case CAMERA_PARAM_FLASHMODE:
       return CameraParameters::KEY_FLASH_MODE;
-    case nsCameraControl::CAMERA_PARAM_FOCUSMODE:
+    case CAMERA_PARAM_FOCUSMODE:
       return CameraParameters::KEY_FOCUS_MODE;
-    case nsCameraControl::CAMERA_PARAM_ZOOM:
+    case CAMERA_PARAM_ZOOM:
       return CameraParameters::KEY_ZOOM;
-    case nsCameraControl::CAMERA_PARAM_METERINGAREAS:
+    case CAMERA_PARAM_METERINGAREAS:
       return CameraParameters::KEY_METERING_AREAS;
-    case nsCameraControl::CAMERA_PARAM_FOCUSAREAS:
+    case CAMERA_PARAM_FOCUSAREAS:
       return CameraParameters::KEY_FOCUS_AREAS;
-    case nsCameraControl::CAMERA_PARAM_FOCALLENGTH:
+    case CAMERA_PARAM_FOCALLENGTH:
       return CameraParameters::KEY_FOCAL_LENGTH;
-    case nsCameraControl::CAMERA_PARAM_FOCUSDISTANCENEAR:
+    case CAMERA_PARAM_FOCUSDISTANCENEAR:
       return CameraParameters::KEY_FOCUS_DISTANCES;
-    case nsCameraControl::CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
+    case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
       return CameraParameters::KEY_FOCUS_DISTANCES;
-    case nsCameraControl::CAMERA_PARAM_FOCUSDISTANCEFAR:
+    case CAMERA_PARAM_FOCUSDISTANCEFAR:
       return CameraParameters::KEY_FOCUS_DISTANCES;
-    case nsCameraControl::CAMERA_PARAM_EXPOSURECOMPENSATION:
+    case CAMERA_PARAM_EXPOSURECOMPENSATION:
       return CameraParameters::KEY_EXPOSURE_COMPENSATION;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
+    case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
       return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
+    case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
       return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_PICTURESIZES:
+    case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
       return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
+    case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
       return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
+    case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
       return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_SCENEMODES:
+    case CAMERA_PARAM_SUPPORTED_SCENEMODES:
       return CameraParameters::KEY_SUPPORTED_SCENE_MODES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_EFFECTS:
+    case CAMERA_PARAM_SUPPORTED_EFFECTS:
       return CameraParameters::KEY_SUPPORTED_EFFECTS;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_FLASHMODES:
+    case CAMERA_PARAM_SUPPORTED_FLASHMODES:
       return CameraParameters::KEY_SUPPORTED_FLASH_MODES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_FOCUSMODES:
+    case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
       return CameraParameters::KEY_SUPPORTED_FOCUS_MODES;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
+    case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
       return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
+    case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
       return CameraParameters::KEY_MAX_NUM_METERING_AREAS;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
+    case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
       return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
+    case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
       return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
+    case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
       return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_ZOOM:
+    case CAMERA_PARAM_SUPPORTED_ZOOM:
       return CameraParameters::KEY_ZOOM_SUPPORTED;
-    case nsCameraControl::CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
+    case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
       return CameraParameters::KEY_ZOOM_RATIOS;
     default:
       return nullptr;
   }
 }
 
+// nsDOMCameraControl implementation-specific constructor
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+  : mDOMCapabilities(nullptr)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+
+  /**
+   * nsDOMCameraControl is a cycle-collection participant, which means it is
+   * not threadsafe--so we need to bump up its reference count here to make
+   * sure that it exists long enough to be initialized.
+   *
+   * Once it is initialized, the GetCameraResult main-thread runnable will
+   * decrement it again to make sure it can be cleaned up.
+   *
+   * nsGonkCameraControl MUST NOT hold a strong reference to this
+   * nsDOMCameraControl or memory will leak!
+   */
+  NS_ADDREF_THIS();
+  mCameraControl = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError);
+}
+
 // Gonk-specific CameraControl implementation.
 
-nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread)
-  : nsCameraControl(aCameraId, aCameraThread)
+// Initialize nsGonkCameraControl instance--runs on camera thread.
+class InitGonkCameraControl : public nsRunnable
+{
+public:
+  InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mDOMCameraControl(aDOMCameraControl)
+    , mOnSuccessCb(onSuccess)
+    , mOnErrorCb(onError)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  ~InitGonkCameraControl()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsresult rv = mCameraControl->Init();
+    return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb);
+  }
+
+  nsRefPtr<nsGonkCameraControl> mCameraControl;
+  // Raw pointer to DOM-facing camera control--it must NS_ADDREF itself for us
+  nsDOMCameraControl* mDOMCameraControl;
+  nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Construct nsGonkCameraControl on the main thread.
+nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+  : CameraControlImpl(aCameraId, aCameraThread)
   , mHwHandle(0)
   , mExposureCompensationMin(0.0)
   , mExposureCompensationStep(0.0)
   , mDeferConfigUpdate(false)
+  , mWidth(0)
+  , mHeight(0)
+  , mFormat(PREVIEW_FORMAT_UNKNOWN)
+  , mDiscardedFrameCount(0)
 {
-  // Constructor runs on the camera thread--see DOMCameraManager.cpp::GetCameraImpl().
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  // Constructor runs on the main thread...
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
+
+  // ...but initialization is carried out on the camera thread.
+  nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError);
+  mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+nsGonkCameraControl::Init()
+{
   mHwHandle = GonkCameraHardware::GetHandle(this, mCameraId);
-  DOM_CAMERA_LOGI("%s:%d : this = %p, mHwHandle = %d\n", __func__, __LINE__, this, mHwHandle);
+  DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mHwHandle=%d)\n", mCameraId, this, mHwHandle);
 
   // Initialize our camera configuration database.
-  mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
-  PullParametersImpl(nullptr);
+  PullParametersImpl();
+
+  // Try to set preferred image format and frame rate
+  DOM_CAMERA_LOGI("Camera preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS));
+  const char* const PREVIEW_FORMAT = "yuv420p";
+  const char* const BAD_PREVIEW_FORMAT = "yuv420sp";
+  mParams.setPreviewFormat(PREVIEW_FORMAT);
+  mParams.setPreviewFrameRate(mFps);
 
-  // Grab any settings we'll need later.
-  mExposureCompensationMin = mParams.getFloat(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION);
-  mExposureCompensationStep = mParams.getFloat(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP);
-  mMaxMeteringAreas = mParams.getInt(CameraParameters::KEY_MAX_NUM_METERING_AREAS);
-  mMaxFocusAreas = mParams.getInt(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS);
+  // Check that our settings stuck
+  PullParametersImpl();
+  const char* format = mParams.getPreviewFormat();
+  if (strcmp(format, PREVIEW_FORMAT) == 0) {
+    mFormat = PREVIEW_FORMAT_YUV420P;  /* \o/ */
+  } else if (strcmp(format, BAD_PREVIEW_FORMAT) == 0) {
+    mFormat = PREVIEW_FORMAT_YUV420SP;
+    DOM_CAMERA_LOGA("Camera ignored our request for '%s' preview, will have to convert (from %d)\n", PREVIEW_FORMAT, mFormat);
+  } else {
+    mFormat = PREVIEW_FORMAT_UNKNOWN;
+    DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT, format);
+  }
 
-  DOM_CAMERA_LOGI("minimum exposure compensation = %f\n", mExposureCompensationMin);
-  DOM_CAMERA_LOGI("exposure compensation step = %f\n", mExposureCompensationStep);
-  DOM_CAMERA_LOGI("maximum metering areas = %d\n", mMaxMeteringAreas);
-  DOM_CAMERA_LOGI("maximum focus areas = %d\n", mMaxFocusAreas);
+  // Check the frame rate and log if the camera ignored our setting
+  uint32_t fps = mParams.getPreviewFrameRate();
+  if (fps != mFps) {
+    DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using that", mFps, fps);
+    mFps = fps;
+  }
+
+  // Grab any other settings we'll need later.
+  mExposureCompensationMin = mParams.getFloat(mParams.KEY_MIN_EXPOSURE_COMPENSATION);
+  mExposureCompensationStep = mParams.getFloat(mParams.KEY_EXPOSURE_COMPENSATION_STEP);
+  mMaxMeteringAreas = mParams.getInt(mParams.KEY_MAX_NUM_METERING_AREAS);
+  mMaxFocusAreas = mParams.getInt(mParams.KEY_MAX_NUM_FOCUS_AREAS);
+
+  DOM_CAMERA_LOGI(" - minimum exposure compensation: %f\n", mExposureCompensationMin);
+  DOM_CAMERA_LOGI(" - exposure compensation step:    %f\n", mExposureCompensationStep);
+  DOM_CAMERA_LOGI(" - maximum metering areas:        %d\n", mMaxMeteringAreas);
+  DOM_CAMERA_LOGI(" - maximum focus areas:           %d\n", mMaxFocusAreas);
+
+  return mHwHandle != 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsGonkCameraControl::~nsGonkCameraControl()
 {
-  DOM_CAMERA_LOGI("%s:%d : this = %p, mHwHandle = %d\n", __func__, __LINE__, this, mHwHandle);
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mHwHandle = %d\n", __func__, __LINE__, this, mHwHandle);
   GonkCameraHardware::ReleaseHandle(mHwHandle);
   if (mRwLock) {
     PRRWLock* lock = mRwLock;
     mRwLock = nullptr;
     PR_DestroyRWLock(lock);
   }
 
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 }
 
 class RwAutoLockRead
 {
 public:
   RwAutoLockRead(PRRWLock* aRwLock)
     : mRwLock(aRwLock)
   {
@@ -295,34 +389,38 @@ nsGonkCameraControl::GetParameter(uint32
   }
 
   return;
 
 GetParameter_error:
   aRegions.Clear();
 }
 
-void
+nsresult
 nsGonkCameraControl::PushParameters()
 {
-  if (!mDeferConfigUpdate) {
-    DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
-    /**
-     * If we're already on the camera thread, call PushParametersImpl()
-     * directly, so that it executes synchronously.  Some callers
-     * require this so that changes take effect immediately before
-     * we can proceed.
-     */
-    if (NS_IsMainThread()) {
-      nsCOMPtr<nsIRunnable> pushParametersTask = new PushParametersTask(this);
-      mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL);
-    } else {
-      PushParametersImpl(nullptr);
-    }
+  if (mDeferConfigUpdate) {
+    DOM_CAMERA_LOGT("%s:%d - defering config update\n", __func__, __LINE__);
+    return NS_OK;
   }
+
+  /**
+   * If we're already on the camera thread, call PushParametersImpl()
+   * directly, so that it executes synchronously.  Some callers
+   * require this so that changes take effect immediately before
+   * we can proceed.
+   */
+  if (NS_IsMainThread()) {
+    DOM_CAMERA_LOGT("%s:%d - dispatching to main thread\n", __func__, __LINE__);
+    nsCOMPtr<nsIRunnable> pushParametersTask = NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl);
+    return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL);
+  }
+
+  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+  return PushParametersImpl();
 }
 
 void
 nsGonkCameraControl::SetParameter(const char* aKey, const char* aValue)
 {
   {
     RwAutoLockWrite lock(mRwLock);
     mParams.set(aKey, aValue);
@@ -406,32 +504,65 @@ nsGonkCameraControl::SetParameter(uint32
     mParams.set(key, s.get());
   }
   PushParameters();
 }
 
 nsresult
 nsGonkCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
 {
-  nsCOMPtr<CameraPreview> preview = mPreview;
-  nsresult rv;
+  SetPreviewSize(aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height);
+
+  DOM_CAMERA_LOGI("config preview: wated %d x %d, got %d x %d (%d fps, format %d)\n", aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height, mWidth, mHeight, mFps, mFormat);
+
+  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, aGetPreviewStream->mOnSuccessCb);
+  return NS_DispatchToMainThread(getPreviewStreamResult);
+}
 
-  if (!preview) {
-    preview = new GonkCameraPreview(mCameraThread, mHwHandle, aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height);
-    if (!preview) {
-      if (aGetPreviewStream->mOnErrorCb) {
-        rv = NS_DispatchToMainThread(new CameraErrorResult(aGetPreviewStream->mOnErrorCb, NS_LITERAL_STRING("OUT_OF_MEMORY")));
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      return NS_ERROR_OUT_OF_MEMORY;
+nsresult
+nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview)
+{
+  /**
+   * If 'aStartPreview->mDOMPreview' is null, we are just restarting
+   * the preview after taking a picture.  No need to monkey with the
+   * currently set DOM-facing preview object.
+   */
+  if (aStartPreview->mDOMPreview) {
+    if (mDOMPreview) {
+      mDOMPreview->Stopped(true);
     }
+    mDOMPreview = aStartPreview->mDOMPreview;
+  } else if (!mDOMPreview) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  mPreview = preview;
-  return NS_DispatchToMainThread(new GetPreviewStreamResult(preview.get(), aGetPreviewStream->mOnSuccessCb));
+  DOM_CAMERA_LOGI("%s: starting preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
+  if (GonkCameraHardware::StartPreview(mHwHandle) != OK) {
+    DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
+    return NS_ERROR_FAILURE;
+  }
+
+  if (aStartPreview->mDOMPreview) {
+    mDOMPreview->Started();
+  }
+  return NS_OK;
+}
+
+nsresult
+nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
+{
+  DOM_CAMERA_LOGI("%s: stopping preview\n", __func__);
+
+  // StopPreview() is a synchronous call--it doesn't return
+  // until the camera preview thread exits.
+  GonkCameraHardware::StopPreview(mHwHandle);
+  mDOMPreview->Stopped();
+  mDOMPreview = nullptr;
+
+  return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
 {
   nsCOMPtr<nsICameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb;
   if (cb) {
     /**
@@ -456,17 +587,17 @@ nsGonkCameraControl::AutoFocusImpl(AutoF
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
 {
- nsCOMPtr<nsICameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
+  nsCOMPtr<nsICameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
   if (cb) {
     /**
      * We already have a callback, so someone has already
      * called TakePicture() -- cancel it.
      */
     mTakePictureOnSuccessCb = nullptr;
     nsCOMPtr<nsICameraErrorCallback> ecb = mTakePictureOnErrorCb;
     mTakePictureOnErrorCb = nullptr;
@@ -532,29 +663,31 @@ nsGonkCameraControl::TakePictureImpl(Tak
 
   if (GonkCameraHardware::TakePicture(mHwHandle) != OK) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::PushParametersImpl(PushParametersTask* aPushParameters)
+nsGonkCameraControl::PushParametersImpl()
 {
+  DOM_CAMERA_LOGI("Pushing camera parameters\n");
   RwAutoLockRead lock(mRwLock);
   if (GonkCameraHardware::PushParameters(mHwHandle, mParams) != OK) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsGonkCameraControl::PullParametersImpl(PullParametersTask* aPullParameters)
+nsGonkCameraControl::PullParametersImpl()
 {
+  DOM_CAMERA_LOGI("Pulling camera parameters\n");
   RwAutoLockWrite lock(mRwLock);
   GonkCameraHardware::PullParameters(mHwHandle, mParams);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
 {
@@ -563,41 +696,152 @@ nsGonkCameraControl::StartRecordingImpl(
 
 nsresult
 nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 void
-nsGonkCameraControl::ReceiveFrame(layers::GraphicBufferLocked *aBuffer)
+nsGonkCameraControl::AutoFocusComplete(bool aSuccess)
 {
-  nsCOMPtr<CameraPreview> preview = mPreview;
+  /**
+   * Auto focusing can change some of the camera's parameters, so
+   * we need to pull a new set before sending the result to the
+   * main thread.
+   */
+  PullParametersImpl();
+
+  /**
+   * If we make it here, regardless of the value of 'aSuccess', we
+   * consider the autofocus _process_ to have succeeded.  It is up
+   * to the onSuccess callback to determine how to handle the case
+   * where the camera wasn't actually able to acquire focus.
+   */
+  nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb);
+  /**
+   * Remember to set these to null so that we don't hold any extra
+   * references to our document's window.
+   */
+  mAutoFocusOnSuccessCb = nullptr;
+  mAutoFocusOnErrorCb = nullptr;
+  nsresult rv = NS_DispatchToMainThread(autoFocusResult);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch autoFocus() onSuccess callback to main thread!");
+  }
+}
+
+void
+nsGonkCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength)
+{
+  uint8_t* data = new uint8_t[aLength];
+
+  memcpy(data, aData, aLength);
+
+  // TODO: see bug 779144.
+  nsIDOMBlob* blob = new nsDOMMemoryFile(static_cast<void*>(data), static_cast<uint64_t>(aLength), NS_LITERAL_STRING("image/jpeg"));
+  nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(blob, mTakePictureOnSuccessCb);
+  /**
+   * Remember to set these to null so that we don't hold any extra
+   * references to our document's window.
+   */
+  mTakePictureOnSuccessCb = nullptr;
+  mTakePictureOnErrorCb = nullptr;
+  nsresult rv = NS_DispatchToMainThread(takePictureResult);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch takePicture() onSuccess callback to main thread!");
+  }
+}
 
-  if (preview) {
-    GonkCameraPreview* p = static_cast<GonkCameraPreview* >(preview.get());
-    MOZ_ASSERT(p);
-    p->ReceiveFrame(aBuffer);
+void
+nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight)
+{
+  Vector<Size> previewSizes;
+  uint32_t bestWidth = aWidth;
+  uint32_t bestHeight = aHeight;
+  uint32_t minSizeDelta = PR_UINT32_MAX;
+  uint32_t delta;
+  Size size;
+
+  mParams.getSupportedPreviewSizes(previewSizes);
+
+  if (!aWidth && !aHeight) {
+    // no size specified, take the first supported size
+    size = previewSizes[0];
+    bestWidth = size.width;
+    bestHeight = size.height;
+  } else if (aWidth && aHeight) {
+    // both height and width specified, find the supported size closest to requested size
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
+      Size size = previewSizes[i];
+      uint32_t delta = abs((long int)(size.width * size.height - aWidth * aHeight));
+      if (delta < minSizeDelta) {
+        minSizeDelta = delta;
+        bestWidth = size.width;
+        bestHeight = size.height;
+      }
+    }
+  } else if (!aWidth) {
+    // width not specified, find closest height match
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
+      size = previewSizes[i];
+      delta = abs((long int)(size.height - aHeight));
+      if (delta < minSizeDelta) {
+        minSizeDelta = delta;
+        bestWidth = size.width;
+        bestHeight = size.height;
+      }
+    }
+  } else if (!aHeight) {
+    // height not specified, find closest width match
+    for (uint32_t i = 0; i < previewSizes.size(); i++) {
+      size = previewSizes[i];
+      delta = abs((long int)(size.width - aWidth));
+      if (delta < minSizeDelta) {
+        minSizeDelta = delta;
+        bestWidth = size.width;
+        bestHeight = size.height;
+      }
+    }
   }
+
+  mWidth = bestWidth;
+  mHeight = bestHeight;
+  mParams.setPreviewSize(mWidth, mHeight);
+  PushParameters();
 }
 
 // Gonk callback handlers.
 namespace mozilla {
 
 void
 ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
 {
   gc->TakePictureComplete(aData, aLength);
 }
 
 void
-AutoFocusComplete(nsGonkCameraControl* gc, bool success)
+AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
+{
+  gc->AutoFocusComplete(aSuccess);
+}
+
+static void
+GonkFrameBuilder(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight)
 {
-  gc->AutoFocusComplete(success);
+  /**
+   * Cast the generic Image back to our platform-specific type and
+   * populate it.
+   */
+  GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(aImage);
+  GonkIOSurfaceImage::Data data;
+  data.mGraphicBuffer = static_cast<layers::GraphicBufferLocked*>(aBuffer);
+  data.mPicSize = gfxIntSize(aWidth, aHeight);
+  videoImage->SetData(data);
 }
 
 void
-ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked *aBuffer)
+ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
 {
-  gc->ReceiveFrame(aBuffer);
+  gc->ReceiveFrame(aBuffer, ImageFormat::GONK_IO_SURFACE, GonkFrameBuilder);
 }
 
 } // namespace mozilla
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -12,69 +12,88 @@
  * 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.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERACONTROL_H
 #define DOM_CAMERA_GONKCAMERACONTROL_H
 
+#include "base/basictypes.h"
 #include "prtypes.h"
 #include "prrwlock.h"
-#include "CameraControl.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
+#include "nsIDOMCameraManager.h"
+#include "DOMCameraControl.h"
+#include "CameraControlImpl.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 namespace layers {
 class GraphicBufferLocked;
 }
 
-class nsGonkCameraControl : public nsCameraControl
+class nsGonkCameraControl : public CameraControlImpl
 {
 public:
-  nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread);
+  nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult Init();
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions);
-  void PushParameters();
+  nsresult PushParameters();
 
-  void ReceiveFrame(layers::GraphicBufferLocked* aBuffer);
+  void AutoFocusComplete(bool aSuccess);
+  void TakePictureComplete(uint8_t* aData, uint32_t aLength);
 
 protected:
   ~nsGonkCameraControl();
 
   nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
+  nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
+  nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
   nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
   nsresult TakePictureImpl(TakePictureTask* aTakePicture);
   nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
   nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
-  nsresult PushParametersImpl(PushParametersTask* aPushParameters);
-  nsresult PullParametersImpl(PullParametersTask* aPullParameters);
+  nsresult PushParametersImpl();
+  nsresult PullParametersImpl();
+
+  void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
 
   uint32_t                  mHwHandle;
   double                    mExposureCompensationMin;
   double                    mExposureCompensationStep;
   bool                      mDeferConfigUpdate;
   PRRWLock*                 mRwLock;
   android::CameraParameters mParams;
+  uint32_t                  mWidth;
+  uint32_t                  mHeight;
+
+  enum {
+    PREVIEW_FORMAT_UNKNOWN,
+    PREVIEW_FORMAT_YUV420P,
+    PREVIEW_FORMAT_YUV420SP
+  };
+  uint32_t                  mFormat;
+
+  uint32_t                  mFps;
+  uint32_t                  mDiscardedFrameCount;
 
 private:
   nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
   nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;
 };
 
 // camera driver callbacks
 void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
-void AutoFocusComplete(nsGonkCameraControl* gc, bool success);
+void AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
 void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERACONTROL_H
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -13,34 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "base/basictypes.h"
 #include "nsDebug.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkNativeWindow.h"
-
-#define DOM_CAMERA_LOG_LEVEL        3
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace android;
 
-/**
- * See bug 783682.  Most camera implementations, despite claiming they
- * support 'yuv420p' as a preview format, actually ignore this setting
- * and return 'yuv420sp' data anyway.  We have come across a new implementation
- * that, while reporting that 'yuv420p' is supported *and* has been accepted,
- * still returns the frame data in 'yuv420sp' anyway.  So for now, since
- * everyone seems to return this format, we just force it.
- */
-#define FORCE_PREVIEW_FORMAT_YUV420SP   1
-
 #if GIHM_TIMING_RECEIVEFRAME
 #define INCLUDE_TIME_H                  1
 #endif
 #if GIHM_TIMING_OVERALL
 #define INCLUDE_TIME_H                  1
 #endif
 
 #if INCLUDE_TIME_H
@@ -55,30 +43,24 @@ static __inline void timespecSubtract(st
   }
   a->tv_nsec = b->tv_nsec - a->tv_nsec;
   a->tv_sec = b->tv_sec - a->tv_sec;
 }
 #endif
 
 GonkCameraHardware::GonkCameraHardware(GonkCamera* aTarget, uint32_t aCamera)
   : mCamera(aCamera)
-  , mFps(30)
-#if !FORCE_PREVIEW_FORMAT_YUV420SP
-  , mPreviewFormat(PREVIEW_FORMAT_UNKNOWN)
-#else
-  , mPreviewFormat(PREVIEW_FORMAT_YUV420SP)
-#endif
   , mClosing(false)
   , mMonitor("GonkCameraHardware.Monitor")
   , mNumFrames(0)
   , mTarget(aTarget)
   , mInitialized(false)
 {
-  DOM_CAMERA_LOGI( "%s: this = %p (aTarget = %p)\n", __func__, (void*)this, (void*)aTarget );
-  init();
+  DOM_CAMERA_LOGT( "%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget );
+  Init();
 }
 
 void
 GonkCameraHardware::OnNewFrame()
 {
   if (mClosing) {
     return;
   }
@@ -157,73 +139,67 @@ GonkCameraHardware::NotifyCallback(int32
 
     default:
       DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType);
       break;
   }
 }
 
 void
-GonkCameraHardware::init()
+GonkCameraHardware::Init()
 {
-  DOM_CAMERA_LOGI("%s: this = %p\n", __func__, (void* )this);
+  DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this);
 
   if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&mModule) < 0) {
     return;
   }
   char cameraDeviceName[4];
   snprintf(cameraDeviceName, sizeof(cameraDeviceName), "%d", mCamera);
   mHardware = new CameraHardwareInterface(cameraDeviceName);
   if (mHardware->initialize(&mModule->common) != OK) {
     mHardware.clear();
     return;
   }
 
-  mWindow = new GonkNativeWindow(this);
-
   if (sHwHandle == 0) {
     sHwHandle = 1;  // don't use 0
   }
   mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, NULL, (void*)sHwHandle);
-
-  // initialize the local camera parameter database
-  mParams = mHardware->getParameters();
-
-  mHardware->setPreviewWindow(mWindow);
-
   mInitialized = true;
 }
 
 GonkCameraHardware::~GonkCameraHardware()
 {
-  DOM_CAMERA_LOGI( "%s:%d : this = %p\n", __func__, __LINE__, (void*)this );
+  DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this );
   sHw = nullptr;
 }
 
 GonkCameraHardware* GonkCameraHardware::sHw         = nullptr;
 uint32_t            GonkCameraHardware::sHwHandle   = 0;
 
 void
 GonkCameraHardware::ReleaseHandle(uint32_t aHwHandle)
 {
   GonkCameraHardware* hw = GetHardware(aHwHandle);
   DOM_CAMERA_LOGI("%s: aHwHandle = %d, hw = %p (sHwHandle = %d)\n", __func__, aHwHandle, (void*)hw, sHwHandle);
   if (!hw) {
     return;
   }
 
-  DOM_CAMERA_LOGI("%s: before: sHwHandle = %d\n", __func__, sHwHandle);
+  DOM_CAMERA_LOGT("%s: before: sHwHandle = %d\n", __func__, sHwHandle);
   sHwHandle += 1; // invalidate old handles before deleting
   hw->mClosing = true;
   hw->mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
   hw->mHardware->stopPreview();
   hw->mHardware->release();
   GonkNativeWindow* window = static_cast<GonkNativeWindow*>(hw->mWindow.get());
-  window->abandon();
-  DOM_CAMERA_LOGI("%s: after: sHwHandle = %d\n", __func__, sHwHandle);
+  if (window) {
+    window->abandon();
+  }
+  DOM_CAMERA_LOGT("%s: after: sHwHandle = %d\n", __func__, sHwHandle);
   delete hw;     // destroy the camera hardware instance
 }
 
 uint32_t
 GonkCameraHardware::GetHandle(GonkCamera* aTarget, uint32_t aCamera)
 {
   ReleaseHandle(sHwHandle);
 
@@ -234,106 +210,16 @@ GonkCameraHardware::GetHandle(GonkCamera
   }
 
   DOM_CAMERA_LOGE("failed to initialize camera hardware\n");
   delete sHw;
   sHw = nullptr;
   return 0;
 }
 
-uint32_t
-GonkCameraHardware::GetFps(uint32_t aHwHandle)
-{
-  GonkCameraHardware* hw = GetHardware(aHwHandle);
-  if (!hw) {
-    return 0;
-  }
-
-  return hw->mFps;
-}
-
-void
-GonkCameraHardware::GetPreviewSize(uint32_t aHwHandle, uint32_t* aWidth, uint32_t* aHeight)
-{
-  GonkCameraHardware* hw = GetHardware(aHwHandle);
-  if (hw) {
-    *aWidth = hw->mWidth;
-    *aHeight = hw->mHeight;
-  } else {
-    *aWidth = 0;
-    *aHeight = 0;
-  }
-}
-
-void
-GonkCameraHardware::SetPreviewSize(uint32_t aWidth, uint32_t aHeight)
-{
-  Vector<Size> previewSizes;
-  uint32_t bestWidth = aWidth;
-  uint32_t bestHeight = aHeight;
-  uint32_t minSizeDelta = PR_UINT32_MAX;
-  uint32_t delta;
-  Size size;
-
-  mParams.getSupportedPreviewSizes(previewSizes);
-
-  if (!aWidth && !aHeight) {
-    // no size specified, take the first supported size
-    size = previewSizes[0];
-    bestWidth = size.width;
-    bestHeight = size.height;
-  } else if (aWidth && aHeight) {
-    // both height and width specified, find the supported size closest to requested size
-    for (uint32_t i = 0; i < previewSizes.size(); i++) {
-      Size size = previewSizes[i];
-      uint32_t delta = abs((long int)(size.width * size.height - aWidth * aHeight));
-      if (delta < minSizeDelta) {
-        minSizeDelta = delta;
-        bestWidth = size.width;
-        bestHeight = size.height;
-      }
-    }
-  } else if (!aWidth) {
-    // width not specified, find closest height match
-    for (uint32_t i = 0; i < previewSizes.size(); i++) {
-      size = previewSizes[i];
-      delta = abs((long int)(size.height - aHeight));
-      if (delta < minSizeDelta) {
-        minSizeDelta = delta;
-        bestWidth = size.width;
-        bestHeight = size.height;
-      }
-    }
-  } else if (!aHeight) {
-    // height not specified, find closest width match
-    for (uint32_t i = 0; i < previewSizes.size(); i++) {
-      size = previewSizes[i];
-      delta = abs((long int)(size.width - aWidth));
-      if (delta < minSizeDelta) {
-        minSizeDelta = delta;
-        bestWidth = size.width;
-        bestHeight = size.height;
-      }
-    }
-  }
-
-  mWidth = bestWidth;
-  mHeight = bestHeight;
-  mParams.setPreviewSize(mWidth, mHeight);
-}
-
-void
-GonkCameraHardware::SetPreviewSize(uint32_t aHwHandle, uint32_t aWidth, uint32_t aHeight)
-{
-  GonkCameraHardware* hw = GetHardware(aHwHandle);
-  if (hw) {
-    hw->SetPreviewSize(aWidth, aHeight);
-  }
-}
-
 int
 GonkCameraHardware::AutoFocus(uint32_t aHwHandle)
 {
   DOM_CAMERA_LOGI("%s: aHwHandle = %d\n", __func__, aHwHandle);
   GonkCameraHardware* hw = GetHardware(aHwHandle);
   if (!hw) {
     return DEAD_OBJECT;
   }
@@ -391,53 +277,25 @@ GonkCameraHardware::PullParameters(uint3
   if (hw) {
     aParams = hw->mHardware->getParameters();
   }
 }
 
 int
 GonkCameraHardware::StartPreview()
 {
-  const char* format;
-
-#if !FORCE_PREVIEW_FORMAT_YUV420SP
-  DOM_CAMERA_LOGI("Preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS));
-
-  // try to set preferred image format and frame rate
-  const char* const PREVIEW_FORMAT = "yuv420p";
-  const char* const BAD_PREVIEW_FORMAT = "yuv420sp";
-  mParams.setPreviewFormat(PREVIEW_FORMAT);
-  mParams.setPreviewFrameRate(mFps);
-  mHardware->setParameters(mParams);
-
-  // check that our settings stuck
-  mParams = mHardware->getParameters();
-  format = mParams.getPreviewFormat();
-  if (strcmp(format, PREVIEW_FORMAT) == 0) {
-    mPreviewFormat = PREVIEW_FORMAT_YUV420P;  /* \o/ */
-  } else if (strcmp(format, BAD_PREVIEW_FORMAT) == 0) {
-    mPreviewFormat = PREVIEW_FORMAT_YUV420SP;
-    DOM_CAMERA_LOGA("Camera ignored our request for '%s' preview, will have to convert (from %d)\n", PREVIEW_FORMAT, mPreviewFormat);
+  if (mWindow.get()) {
+    GonkNativeWindow* window = static_cast<GonkNativeWindow*>(mWindow.get());
+    window->abandon();
   } else {
-    mPreviewFormat = PREVIEW_FORMAT_UNKNOWN;
-    DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT, format);
-  }
-#else
-  mParams.setPreviewFormat("yuv420sp");
-  mParams.setPreviewFrameRate(mFps);
-  mHardware->setParameters(mParams);
-#endif
-
-  // Check the frame rate and log if the camera ignored our setting
-  uint32_t fps = mParams.getPreviewFrameRate();
-  if (fps != mFps) {
-    DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using it", mFps, fps);
-    mFps = fps;
+    mWindow = new GonkNativeWindow(this);
+    mHardware->setPreviewWindow(mWindow);
   }
 
+  mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
   return mHardware->startPreview();
 }
 
 int
 GonkCameraHardware::StartPreview(uint32_t aHwHandle)
 {
   GonkCameraHardware* hw = GetHardware(aHwHandle);
   DOM_CAMERA_LOGI("%s:%d : aHwHandle = %d, hw = %p\n", __func__, __LINE__, aHwHandle, hw);
@@ -451,19 +309,8 @@ GonkCameraHardware::StartPreview(uint32_
 void
 GonkCameraHardware::StopPreview(uint32_t aHwHandle)
 {
   GonkCameraHardware* hw = GetHardware(aHwHandle);
   if (hw) {
     hw->mHardware->stopPreview();
   }
 }
-
-uint32_t
-GonkCameraHardware::GetPreviewFormat(uint32_t aHwHandle)
-{
-  GonkCameraHardware* hw = GetHardware(aHwHandle);
-  if (!hw) {
-    return PREVIEW_FORMAT_UNKNOWN;
-  }
-
-  return hw->mPreviewFormat;
-}
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -17,18 +17,16 @@
 #ifndef DOM_CAMERA_GONKCAMERAHWMGR_H
 #define DOM_CAMERA_GONKCAMERAHWMGR_H
 
 #include "libcameraservice/CameraHardwareInterface.h"
 #include "binder/IMemory.h"
 #include "mozilla/ReentrantMonitor.h"
 
 #include "GonkCameraControl.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
 #include "GonkNativeWindow.h"
 
 // config
 #define GIHM_TIMING_RECEIVEFRAME    0
 #define GIHM_TIMING_OVERALL         1
 
@@ -39,45 +37,34 @@ namespace mozilla {
 
 typedef class nsGonkCameraControl GonkCamera;
 
 class GonkCameraHardware : GonkNativeWindowNewFrameCallback
 {
 protected:
   GonkCameraHardware(GonkCamera* aTarget, uint32_t aCamera);
   ~GonkCameraHardware();
-  void init();
+  void Init();
 
-  static void                   DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser);
-  static void                   NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser);
+  static void     DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser);
+  static void     NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser);
 
 public:
-  virtual void OnNewFrame() MOZ_OVERRIDE;
+  virtual void    OnNewFrame() MOZ_OVERRIDE;
 
-  static void                   ReleaseHandle(uint32_t aHwHandle);
-  static uint32_t               GetHandle(GonkCamera* aTarget, uint32_t aCamera);
-  static uint32_t               GetFps(uint32_t aHwHandle);
-  static void                   GetPreviewSize(uint32_t aHwHandle, uint32_t* aWidth, uint32_t* aHeight);
-  static void                   SetPreviewSize(uint32_t aHwHandle, uint32_t aWidth, uint32_t aHeight);
-  static int                    AutoFocus(uint32_t aHwHandle);
-  static void                   CancelAutoFocus(uint32_t aHwHandle);
-  static int                    TakePicture(uint32_t aHwHandle);
-  static void                   CancelTakePicture(uint32_t aHwHandle);
-  static int                    StartPreview(uint32_t aHwHandle);
-  static void                   StopPreview(uint32_t aHwHandle);
-  static int                    PushParameters(uint32_t aHwHandle, const CameraParameters& aParams);
-  static void                   PullParameters(uint32_t aHwHandle, CameraParameters& aParams);
-
-  enum {
-    PREVIEW_FORMAT_UNKNOWN,
-    PREVIEW_FORMAT_YUV420P,
-    PREVIEW_FORMAT_YUV420SP
-  };
-  // GetPreviewFormat() MUST be called only after StartPreview().
-  static uint32_t               GetPreviewFormat(uint32_t aHwHandle);
+  static void     ReleaseHandle(uint32_t aHwHandle);
+  static uint32_t GetHandle(GonkCamera* aTarget, uint32_t aCamera);
+  static int      AutoFocus(uint32_t aHwHandle);
+  static void     CancelAutoFocus(uint32_t aHwHandle);
+  static int      TakePicture(uint32_t aHwHandle);
+  static void     CancelTakePicture(uint32_t aHwHandle);
+  static int      StartPreview(uint32_t aHwHandle);
+  static void     StopPreview(uint32_t aHwHandle);
+  static int      PushParameters(uint32_t aHwHandle, const CameraParameters& aParams);
+  static void     PullParameters(uint32_t aHwHandle, CameraParameters& aParams);
 
 protected:
   static GonkCameraHardware*    sHw;
   static uint32_t               sHwHandle;
 
   static GonkCameraHardware*    GetHardware(uint32_t aHwHandle)
   {
     if (aHwHandle == sHwHandle) {
@@ -86,33 +73,27 @@ protected:
        * so even if this function is called with aHwHandle = 0, the
        * result will still be null.
        */
       return sHw;
     }
     return nullptr;
   }
 
-  // Instance wrappers to make member function access easier.
-  void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
+  // Instance wrapper to make member function access easier.
   int StartPreview();
 
   uint32_t                      mCamera;
-  uint32_t                      mWidth;
-  uint32_t                      mHeight;
-  uint32_t                      mFps;
-  uint32_t                      mPreviewFormat;
   bool                          mClosing;
   mozilla::ReentrantMonitor     mMonitor;
   uint32_t                      mNumFrames;
   sp<CameraHardwareInterface>   mHardware;
   GonkCamera*                   mTarget;
   camera_module_t*              mModule;
   sp<ANativeWindow>             mWindow;
-  CameraParameters              mParams;
 #if GIHM_TIMING_OVERALL
   struct timespec               mStart;
   struct timespec               mAutoFocusStart;
 #endif
   bool                          mInitialized;
 
   bool IsInitialized()
   {
--- a/dom/camera/GonkCameraManager.cpp
+++ b/dom/camera/GonkCameraManager.cpp
@@ -13,18 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "jsapi.h"
 #include "libcameraservice/CameraHardwareInterface.h"
 #include "GonkCameraControl.h"
 #include "DOMCameraManager.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
 // From nsDOMCameraManager, but gonk-specific!
 
 /* [implicit_jscontext] jsval getListOfCameras (); */
 NS_IMETHODIMP
 nsDOMCameraManager::GetListOfCameras(JSContext* cx, JS::Value* _retval)
 {
@@ -62,17 +60,17 @@ nsDOMCameraManager::GetListOfCameras(JSC
         break;
 
       case CAMERA_FACING_FRONT:
         v = JS_NewStringCopyZ(cx, "front");
         index = 1;
         break;
 
       default:
-        // TODO: handle extra cameras in getCamera().
+        // TODO: see bug 779143.
         {
           static uint32_t extraIndex = 2;
           nsCString s;
           s.AppendPrintf("extra-camera-%d", count);
           v = JS_NewStringCopyZ(cx, s.get());
           index = extraIndex++;
         }
         break;
@@ -88,20 +86,8 @@ nsDOMCameraManager::GetListOfCameras(JSC
       delete a;
       return NS_ERROR_NOT_AVAILABLE;
     }
   }
 
   *_retval = OBJECT_TO_JSVAL(a);
   return NS_OK;
 }
-
-using namespace mozilla;
-
-NS_IMETHODIMP
-GetCameraTask::Run()
-{
-  nsCOMPtr<nsICameraControl> cameraControl = new nsGonkCameraControl(mCameraId, mCameraThread);
-
-  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
-
-  return NS_DispatchToMainThread(new GetCameraResult(cameraControl, mOnSuccessCb));
-}
deleted file mode 100644
--- a/dom/camera/GonkCameraPreview.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2012 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/basictypes.h"
-#include "VideoUtils.h"
-#include "GonkCameraHwMgr.h"
-#include "GonkCameraPreview.h"
-
-#define DOM_CAMERA_LOG_LEVEL  2
-#include "CameraCommon.h"
-
-#include "GonkIOSurfaceImage.h"
-
-using namespace mozilla;
-
-void
-GonkCameraPreview::ReceiveFrame(mozilla::layers::GraphicBufferLocked* aBuffer)
-{
-  DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-  if (!aBuffer)
-    return;
-
-  ImageFormat format = ImageFormat::GONK_IO_SURFACE;
-  nsRefPtr<Image> image = mImageContainer->CreateImage(&format, 1);
-  GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(image.get());
-  GonkIOSurfaceImage::Data data;
-  data.mGraphicBuffer = aBuffer;
-  data.mPicSize = gfxIntSize(mWidth, mHeight);
-  videoImage->SetData(data);
-
-  // AppendFrame() takes over image's reference
-  mVideoSegment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight));
-  mInput->AppendToTrack(TRACK_VIDEO, &mVideoSegment);
-
-  mFrameCount += 1;
-
-  if ((mFrameCount % 10) == 0) {
-    DOM_CAMERA_LOGI("%s:%d : mFrameCount = %d\n", __func__, __LINE__, mFrameCount);
-  }
-}
-
-nsresult
-GonkCameraPreview::StartImpl()
-{
-  DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  /**
-   * We set and then immediately get the preview size, in case the camera
-   * driver has decided to ignore our given dimensions.
-   */
-  GonkCameraHardware::SetPreviewSize(mHwHandle, mWidth, mHeight);
-  GonkCameraHardware::GetPreviewSize(mHwHandle, &mWidth, &mHeight);
-  SetFrameRate(GonkCameraHardware::GetFps(mHwHandle));
-
-  if (GonkCameraHardware::StartPreview(mHwHandle) != OK) {
-    DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
-    return NS_ERROR_FAILURE;
-  }
-
-  // GetPreviewFormat() must be called after StartPreview().
-  mFormat = GonkCameraHardware::GetPreviewFormat(mHwHandle);
-  DOM_CAMERA_LOGI("preview stream is (actually!) %d x %d (w x h), %d frames per second, format %d\n", mWidth, mHeight, mFramesPerSecond, mFormat);
-  return NS_OK;
-}
-
-nsresult
-GonkCameraPreview::StopImpl()
-{
-  DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  GonkCameraHardware::StopPreview(mHwHandle);
-  mInput->EndTrack(TRACK_VIDEO);
-  mInput->Finish();
-
-  return NS_OK;
-}
deleted file mode 100644
--- a/dom/camera/GonkCameraPreview.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DOM_CAMERA_GONKCAMERAPREVIEW_H
-#define DOM_CAMERA_GONKCAMERAPREVIEW_H
-
-#include "CameraPreview.h"
-
-#define DOM_CAMERA_LOG_LEVEL  3
-#include "CameraCommon.h"
-
-namespace mozilla {
-namespace layers {
-class GraphicBufferLocked;
-} // namespace layers
-} // namespace mozilla
-
-namespace mozilla {
-
-class GonkCameraPreview : public CameraPreview
-{
-public:
-  GonkCameraPreview(nsIThread* aCameraThread, uint32_t aHwHandle, uint32_t aWidth, uint32_t aHeight)
-    : CameraPreview(aCameraThread, aWidth, aHeight)
-    , mHwHandle(aHwHandle)
-    , mDiscardedFrameCount(0)
-    , mFormat(GonkCameraHardware::PREVIEW_FORMAT_UNKNOWN)
-  { }
-
-  void ReceiveFrame(layers::GraphicBufferLocked* aBuffer);
-
-  nsresult StartImpl();
-  nsresult StopImpl();
-
-protected:
-  ~GonkCameraPreview()
-  {
-    Stop();
-  }
-
-  uint32_t mHwHandle;
-  uint32_t mDiscardedFrameCount;
-  uint32_t mFormat;
-
-private:
-  GonkCameraPreview(const GonkCameraPreview&) MOZ_DELETE;
-  GonkCameraPreview& operator=(const GonkCameraPreview&) MOZ_DELETE;
-};
-
-} // namespace mozilla
-
-#endif // DOM_CAMERA_GONKCAMERAPREVIEW_H
--- a/dom/camera/GonkNativeWindow.cpp
+++ b/dom/camera/GonkNativeWindow.cpp
@@ -52,16 +52,18 @@ GonkNativeWindow::GonkNativeWindow()
 GonkNativeWindow::~GonkNativeWindow()
 {
     freeAllBuffersLocked();
 }
 
 void GonkNativeWindow::abandon()
 {
     Mutex::Autolock lock(mMutex);
+    ++mGeneration;
+    CNW_LOGD("abandon: new generation %d", mGeneration);
     freeAllBuffersLocked();
     mDequeueCondition.signal();
 }
 
 void GonkNativeWindow::init()
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -74,16 +76,17 @@ void GonkNativeWindow::init()
 
     mDefaultWidth = 0;
     mDefaultHeight = 0;
     mPixelFormat = 0;
     mUsage = 0;
     mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
     mBufferCount = MIN_BUFFER_SLOTS;
     mFrameCounter = 0;
+    mGeneration = 0;
 }
 
 
 int GonkNativeWindow::hook_setSwapInterval(ANativeWindow* window, int interval)
 {
     GonkNativeWindow* c = getSelf(window);
     return c->setSwapInterval(interval);
 }
@@ -341,17 +344,17 @@ int GonkNativeWindow::queueBuffer(ANativ
     CNW_LOGD("queueBuffer: X");
     return OK;
 }
 
 
 already_AddRefed<GraphicBufferLocked>
 GonkNativeWindow::getCurrentBuffer()
 {
-  CNW_LOGD("GonkNativeWindow::lockCurrentBuffer");
+  CNW_LOGD("GonkNativeWindow::getCurrentBuffer");
   Mutex::Autolock lock(mMutex);
 
   int found = -1;
   for (int i = 0; i < mBufferCount; i++) {
     const int state = mSlots[i].mBufferState;
     if (state == BufferSlot::QUEUED) {
       if (found < 0 ||
           mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
@@ -363,40 +366,46 @@ GonkNativeWindow::getCurrentBuffer()
   if (found < 0) {
     mDequeueCondition.signal();
     return NULL;
   }
 
   mSlots[found].mBufferState = BufferSlot::RENDERING;
 
   nsRefPtr<GraphicBufferLocked> ret =
-    new CameraGraphicBuffer(this, found, mSlots[found].mSurfaceDescriptor);
+    new CameraGraphicBuffer(this, found, mGeneration, mSlots[found].mSurfaceDescriptor);
   mDequeueCondition.signal();
   return ret.forget();
 }
 
-void
-GonkNativeWindow::returnBuffer(uint32_t aIndex)
+bool
+GonkNativeWindow::returnBuffer(uint32_t aIndex, uint32_t aGeneration)
 {
-  CNW_LOGD("GonkNativeWindow::freeBuffer");
+  CNW_LOGD("GonkNativeWindow::returnBuffer: slot=%d (generation=%d)", aIndex, aGeneration);
   Mutex::Autolock lock(mMutex);
 
+  if (aGeneration != mGeneration) {
+    CNW_LOGD("returnBuffer: buffer is from generation %d (current is %d)",
+      aGeneration, mGeneration);
+    return false;
+  }
   if (aIndex < 0 || aIndex >= mBufferCount) {
-    CNW_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+    CNW_LOGE("returnBuffer: slot index out of range [0, %d]: %d",
              mBufferCount, aIndex);
-    return;
-  } else if (mSlots[aIndex].mBufferState != BufferSlot::RENDERING) {
-    printf_stderr("cancelBuffer: slot %d is not owned by the compositor (state=%d)",
+    return false;
+  }
+  if (mSlots[aIndex].mBufferState != BufferSlot::RENDERING) {
+    CNW_LOGE("returnBuffer: slot %d is not owned by the compositor (state=%d)",
                   aIndex, mSlots[aIndex].mBufferState);
-    return;
+    return false;
   }
 
   mSlots[aIndex].mBufferState = BufferSlot::FREE;
   mDequeueCondition.signal();
-  return;
+  return true;
 }
 
 int GonkNativeWindow::lockBuffer(ANativeWindowBuffer* buffer)
 {
     CNW_LOGD("GonkNativeWindow::lockBuffer");
     Mutex::Autolock lock(mMutex);
     return OK;
 }
--- a/dom/camera/GonkNativeWindow.h
+++ b/dom/camera/GonkNativeWindow.h
@@ -68,17 +68,17 @@ public:
     static int hook_setSwapInterval(ANativeWindow* window, int interval);
 
     // Get next frame from the queue and mark it as RENDERING, caller
     // owns the returned buffer.
     already_AddRefed<GraphicBufferLocked> getCurrentBuffer();
 
     // Return the buffer to the queue and mark it as FREE. After that
     // the buffer is useable again for the decoder.
-    void returnBuffer(uint32_t index);
+    bool returnBuffer(uint32_t index, uint32_t generation);
 
     // Release all internal buffers
     void abandon();
 
 protected:
     virtual int cancelBuffer(ANativeWindowBuffer* buffer);
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer);
     virtual int lockBuffer(ANativeWindowBuffer* buffer);
@@ -216,56 +216,61 @@ private:
 
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables. It must be locked whenever the member variables are accessed.
     mutable Mutex mMutex;
 
     // mFrameCounter is the free running counter, incremented for every buffer queued
     uint64_t mFrameCounter;
 
+    // mGeneration is the current generation of buffer slots
+    uint32_t mGeneration;
+
     GonkNativeWindowNewFrameCallback* mNewFrameCallback;
 };
 
 
 // CameraGraphicBuffer maintains the buffer returned from GonkNativeWindow
 class CameraGraphicBuffer : public mozilla::layers::GraphicBufferLocked {
     typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor;
 public:
     CameraGraphicBuffer(GonkNativeWindow* aNativeWindow,
                         uint32_t aIndex,
+                        uint32_t aGeneration,
                         SurfaceDescriptor aBuffer)
         : GraphicBufferLocked(aBuffer)
           , mNativeWindow(aNativeWindow)
           , mIndex(aIndex)
+          , mGeneration(aGeneration)
           , mLocked(true)
     {}
 
     virtual ~CameraGraphicBuffer() {}
 
     // Unlock either returns the buffer to the native window or
     // destroys the buffer if the window is already released.
     virtual void Unlock()  MOZ_OVERRIDE
     {
         if (mLocked) {
             // The window might has been destroyed. The buffer is no longer
             // valid at that point.
             sp<GonkNativeWindow> window = mNativeWindow.promote();
-            if (window.get()) {
-                window->returnBuffer(mIndex);
+            if (window.get() && window->returnBuffer(mIndex, mGeneration)) {
                 mLocked = false;
             } else {
-                // If the window doesn't exist any more, release the buffer by
-                // ourself.
+                // If the window doesn't exist any more, release the buffer
+                // by ourself.
                 ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
                 ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor);
             }
         }
     }
 
 protected:
     wp<GonkNativeWindow> mNativeWindow;
     uint32_t mIndex;
+    uint32_t mGeneration;
     bool mLocked;
 };
 
 }; // namespace android
 
 #endif // DOM_CAMERA_GONKNATIVEWINDOW_H
new file mode 100644
--- /dev/null
+++ b/dom/camera/ICameraControl.h
@@ -0,0 +1,58 @@
+/* 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_ICAMERACONTROL_H
+#define DOM_CAMERA_ICAMERACONTROL_H
+
+#include "base/basictypes.h"
+#include "prtypes.h"
+#include "jsapi.h"
+#include "nsIDOMCameraManager.h"
+#include "DictionaryHelpers.h"
+#include "CameraCommon.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+class DOMCameraPreview;
+
+class ICameraControl
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
+
+  virtual nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0;
+  virtual void StopPreview() = 0;
+  virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StopRecording() = 0;
+
+  virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
+  virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
+  virtual nsresult Set(uint32_t aKey, double aValue) = 0;
+  virtual nsresult Get(uint32_t aKey, double* aValue) = 0;
+  virtual nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) = 0;
+  virtual nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) = 0;
+  virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
+  virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
+
+  virtual const char* GetParameter(const char* aKey) = 0;
+  virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
+  virtual double GetParameterDouble(uint32_t aKey) = 0;
+  virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
+  virtual void SetParameter(const char* aKey, const char* aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, double aValue) = 0;
+  virtual void SetParameter(uint32_t aKey, const nsTArray<CameraRegion>& aRegions) = 0;
+
+protected:
+  virtual ~ICameraControl() { }
+};
+
+} // namespace mozilla
+
+#endif // DOM_CAMERA_ICAMERACONTROL_H
--- a/dom/camera/Makefile.in
+++ b/dom/camera/Makefile.in
@@ -14,34 +14,33 @@ LIBRARY_NAME     = domcamera_s
 XPIDL_MODULE     = dom_camera
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 CPPSRCS = \
   DOMCameraManager.cpp \
-  CameraControl.cpp \
-  CameraPreview.cpp \
+  DOMCameraControl.cpp \
+  DOMCameraPreview.cpp \
+  DOMCameraCapabilities.cpp \
+  CameraControlImpl.cpp \
   $(NULL)
 
 ifeq ($(MOZ_B2G_CAMERA),1)
 CPPSRCS += \
   GonkCameraManager.cpp \
   GonkCameraControl.cpp \
   GonkCameraHwMgr.cpp \
-  GonkCameraPreview.cpp \
   GonkNativeWindow.cpp \
-  GonkCameraCapabilities.cpp \
   $(NULL)
 else
 CPPSRCS += \
   FallbackCameraManager.cpp \
   FallbackCameraControl.cpp \
-  FallbackCameraCapabilities.cpp \
   $(NULL)
 endif
 
 XPIDLSRCS = \
   nsIDOMNavigatorCamera.idl \
   nsIDOMCameraManager.idl \
   $(NULL)
 
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -182,17 +182,17 @@ interface nsICameraErrorCallback : nsISu
 {
     void handleEvent(in DOMString error);
 };
 
 /*
     attributes here affect the preview, any pictures taken, and/or
     any video recorded by the camera.
 */
-[scriptable, uuid(3066c884-d2c3-4477-847d-08ea1c2d188a)]
+[scriptable, uuid(b8949e5c-55b0-49dd-99a9-68d11342915a)]
 interface nsICameraControl : nsISupports
 {
     readonly attribute nsICameraCapabilities capabilities;
 
     /* one of the vales chosen from capabilities.effects;
        default is "none" */
     attribute DOMString         effect;
 
@@ -308,16 +308,20 @@ interface nsICameraControl : nsISupports
        capabilities.previewSizes, e.g.:
         {
             height: 640,
             width:  480,
          }
     */
     [implicit_jscontext]
     void getPreviewStream(in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError);
+
+    /* call in or after the takePicture() onSuccess callback to
+       resume the camera preview stream. */
+    void resumePreview();
 };
 
 [scriptable, function, uuid(a267afbc-d91c-413a-8de5-0b94aecffa3e)]
 interface nsICameraGetCameraCallback : nsISupports
 {
     void handleEvent(in nsICameraControl camera);
 };