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