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 107858 244bba751ce4ae586d66d28c8e9a1f0b41e872c5
parent 107857 73eb2f3088f1862bbd495d2bfe49d03f6dca3083
child 107859 af0971ca7acd8eea374e1961b4cc3625a31e7fa7
push idunknown
push userunknown
push dateunknown
reviewersjst
bugs779139
milestone18.0a1
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);
 };