Bug 1139027 - Permit running of camera mochitests on B2G desktop. r=mikeh
authorAndrew Osmond <aosmond@gmail.com>
Tue, 10 Mar 2015 19:39:49 -0400
changeset 249738 1a02c68523da83ac02855e0c7e6fb669aabb3330
parent 249737 6f6ff7993a81dfa0d4bae911fc98206ff8cead74
child 249739 ebd64d539f30a3a7b86d40571374ddc7320af722
push id986
push userwcosta@mozilla.com
push dateWed, 11 Mar 2015 16:59:43 +0000
reviewersmikeh
bugs1139027
milestone39.0a1
Bug 1139027 - Permit running of camera mochitests on B2G desktop. r=mikeh
dom/camera/CameraPreferences.cpp
dom/camera/CameraPreferences.h
dom/camera/DOMCameraManager.cpp
dom/camera/FallbackCameraPlatform.cpp
dom/camera/FallbackCameraPlatform.h
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkCameraHwMgr.cpp
dom/camera/GonkCameraHwMgr.h
dom/camera/GonkCameraManager.cpp
dom/camera/GonkCameraParameters.cpp
dom/camera/GonkCameraParameters.h
dom/camera/GonkRecorderProfiles.cpp
dom/camera/GonkRecorderProfiles.h
dom/camera/TestGonkCameraHardware.cpp
dom/camera/TestGonkCameraHardware.h
dom/camera/moz.build
dom/camera/test/camera_common.js
dom/camera/test/mochitest.ini
dom/camera/test/test_bug1037322.html
dom/camera/test/test_bug1099390.html
dom/camera/test/test_bug975472.html
dom/camera/test/test_camera_bad_initial_config.html
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/moz.build
toolkit/library/moz.build
--- a/dom/camera/CameraPreferences.cpp
+++ b/dom/camera/CameraPreferences.cpp
@@ -26,16 +26,18 @@ StaticAutoPtr<nsCString> CameraPreferenc
 
 nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK;
 nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK;
 
 uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
 
 bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
 
+bool CameraPreferences::sPrefCameraParametersPermission = false;
+
 #ifdef MOZ_WIDGET_GONK
 StaticRefPtr<CameraPreferences> CameraPreferences::sObserver;
 
 NS_IMPL_ISUPPORTS(CameraPreferences, nsIObserver);
 #endif
 
 /* static */
 nsresult
@@ -108,16 +110,21 @@ CameraPreferences::Pref CameraPreference
     kPrefValueIsCString,
     { &sPrefTestEnabled }
   },
   {
     "camera.control.test.hardware",
     kPrefValueIsCString,
     { &sPrefHardwareTest }
   },
+  {
+    "camera.control.test.permission",
+    kPrefValueIsBoolean,
+    { &sPrefCameraParametersPermission }
+  },
 #ifdef MOZ_B2G
   {
     "camera.control.test.hardware.gonk.parameters",
     kPrefValueIsCString,
     { &sPrefGonkParameters }
   },
 #endif
   {
--- a/dom/camera/CameraPreferences.h
+++ b/dom/camera/CameraPreferences.h
@@ -75,16 +75,18 @@ protected:
 
   static nsresult sPrefCameraControlMethodErrorOverride;
   static nsresult sPrefCameraControlAsyncErrorOverride;
 
   static uint32_t sPrefCameraControlLowMemoryThresholdMB;
 
   static bool sPrefCameraParametersIsLowMemory;
 
+  static bool sPrefCameraParametersPermission;
+
 #ifdef MOZ_WIDGET_GONK
   static StaticRefPtr<CameraPreferences> sObserver;
 
   nsresult PreinitCameraHardware();
 
 protected:
   // Objects may be instantiated for use as observers.
   CameraPreferences() { }
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -11,16 +11,17 @@
 #include "nsContentPermissionHelper.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "DOMCameraControl.h"
 #include "nsDOMClassInfo.h"
 #include "CameraCommon.h"
+#include "CameraPreferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCameraManager, mWindow)
 
@@ -293,17 +294,21 @@ nsDOMCameraManager::GetCamera(const nsAS
     return nullptr;
   }
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   // If we are a CERTIFIED app, we can short-circuit the permission check,
   // which gets us a performance win.
   uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   principal->GetAppStatus(&status);
-  if (status == nsIPrincipal::APP_STATUS_CERTIFIED && CheckPermission(mWindow)) {
+  // Unprivileged mochitests always fail the dispatched permission check,
+  // even if permission to the camera has been granted.
+  bool immediateCheck = false;
+  CameraPreferences::GetPref("camera.control.test.permission", immediateCheck);
+  if ((status == nsIPrincipal::APP_STATUS_CERTIFIED || immediateCheck) && CheckPermission(mWindow)) {
     PermissionAllowed(cameraId, aInitialConfig, promise);
     return promise.forget();
   }
 
   nsCOMPtr<nsIRunnable> permissionRequest =
     new CameraPermissionRequest(principal, mWindow, this, cameraId,
                                 aInitialConfig, promise);
 
new file mode 100644
--- /dev/null
+++ b/dom/camera/FallbackCameraPlatform.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 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 "FallbackCameraPlatform.h"
+
+using namespace android;
+
+MediaProfiles* MediaProfiles::sMediaProfiles = nullptr;
+
+const char CameraParameters::KEY_PREVIEW_SIZE[] = "preview-size";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES[] = "preview-size-values";
+const char CameraParameters::KEY_PREVIEW_FORMAT[] = "preview-format";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
+const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
+const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
+const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
+const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
+const char CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS[] = "picture-format-values";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH[] = "jpeg-thumbnail-width";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT[] = "jpeg-thumbnail-height";
+const char CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[] = "jpeg-thumbnail-size-values";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY[] = "jpeg-thumbnail-quality";
+const char CameraParameters::KEY_JPEG_QUALITY[] = "jpeg-quality";
+const char CameraParameters::KEY_ROTATION[] = "rotation";
+const char CameraParameters::KEY_GPS_LATITUDE[] = "gps-latitude";
+const char CameraParameters::KEY_GPS_LONGITUDE[] = "gps-longitude";
+const char CameraParameters::KEY_GPS_ALTITUDE[] = "gps-altitude";
+const char CameraParameters::KEY_GPS_TIMESTAMP[] = "gps-timestamp";
+const char CameraParameters::KEY_GPS_PROCESSING_METHOD[] = "gps-processing-method";
+const char CameraParameters::KEY_WHITE_BALANCE[] = "whitebalance";
+const char CameraParameters::KEY_SUPPORTED_WHITE_BALANCE[] = "whitebalance-values";
+const char CameraParameters::KEY_EFFECT[] = "effect";
+const char CameraParameters::KEY_SUPPORTED_EFFECTS[] = "effect-values";
+const char CameraParameters::KEY_ANTIBANDING[] = "antibanding";
+const char CameraParameters::KEY_SUPPORTED_ANTIBANDING[] = "antibanding-values";
+const char CameraParameters::KEY_SCENE_MODE[] = "scene-mode";
+const char CameraParameters::KEY_SUPPORTED_SCENE_MODES[] = "scene-mode-values";
+const char CameraParameters::KEY_FLASH_MODE[] = "flash-mode";
+const char CameraParameters::KEY_SUPPORTED_FLASH_MODES[] = "flash-mode-values";
+const char CameraParameters::KEY_FOCUS_MODE[] = "focus-mode";
+const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values";
+const char CameraParameters::KEY_MAX_NUM_FOCUS_AREAS[] = "max-num-focus-areas";
+const char CameraParameters::KEY_FOCUS_AREAS[] = "focus-areas";
+const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
+const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
+const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation";
+const char CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION[] = "max-exposure-compensation";
+const char CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION[] = "min-exposure-compensation";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP[] = "exposure-compensation-step";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK[] = "auto-whitebalance-lock";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] = "auto-whitebalance-lock-supported";
+const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
+const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
+const char CameraParameters::KEY_ZOOM[] = "zoom";
+const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
+const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
+const char CameraParameters::KEY_ZOOM_SUPPORTED[] = "zoom-supported";
+const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
+const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
+const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
+const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
+const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
+const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
+const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
+const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
+const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
+const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
+const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
+const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
+const char CameraParameters::KEY_LIGHTFX[] = "light-fx";
+
new file mode 100644
--- /dev/null
+++ b/dom/camera/FallbackCameraPlatform.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
+#define DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
+
+#include <inttypes.h>
+#include <string.h>
+
+typedef struct {
+  int32_t id;
+  int32_t score;
+  int32_t rect[4];
+  int32_t left_eye[2];
+  int32_t right_eye[2];
+  int32_t mouth[2];
+} camera_face_t;
+
+typedef struct {
+  uint32_t number_of_faces;
+  camera_face_t* faces;
+} camera_frame_metadata_t;
+
+namespace android {
+  enum camcorder_quality {
+    CAMCORDER_QUALITY_LOW,
+    CAMCORDER_QUALITY_HIGH,
+    CAMCORDER_QUALITY_QCIF,
+    CAMCORDER_QUALITY_CIF,
+    CAMCORDER_QUALITY_480P,
+    CAMCORDER_QUALITY_720P,
+    CAMCORDER_QUALITY_1080P,
+    CAMCORDER_QUALITY_QVGA,
+    CAMCORDER_QUALITY_VGA,
+    CAMCORDER_QUALITY_LIST_START = CAMCORDER_QUALITY_LOW,
+    CAMCORDER_QUALITY_LIST_END = CAMCORDER_QUALITY_VGA
+  };
+
+  enum output_format {
+    OUTPUT_FORMAT_THREE_GPP,
+    OUTPUT_FORMAT_MPEG_4
+  };
+
+  enum video_encoder {
+    VIDEO_ENCODER_H263,
+    VIDEO_ENCODER_H264,
+    VIDEO_ENCODER_MPEG_4_SP
+  };
+
+  enum audio_encoder {
+    AUDIO_ENCODER_AMR_WB,
+    AUDIO_ENCODER_AMR_NB,
+    AUDIO_ENCODER_AAC
+  };
+
+  template <class T>
+  class sp MOZ_FINAL
+  {
+  public:
+    sp()
+      : mPtr(nullptr)
+    { }
+
+    sp(T *aPtr)
+      : mPtr(aPtr)
+    { }
+
+    virtual ~sp()         { }
+    T* get() const        { return mPtr; }
+    void clear()          { mPtr = nullptr; }
+    T* operator->() const { return get(); }
+
+  private:
+    nsRefPtr<T> mPtr;
+  };
+
+  typedef uint64_t nsecs_t;
+
+  enum error_t {
+    OK = 0,
+    UNKNOWN_ERROR,
+    INVALID_OPERATION
+  };
+
+  enum camera_msg_t {
+    CAMERA_MSG_SHUTTER,
+    CAMERA_MSG_COMPRESSED_IMAGE
+  };
+
+  class String8 MOZ_FINAL
+  {
+  public:
+    String8()                  { }
+    String8(const char* aData) { mData.AssignASCII(aData); }
+    virtual ~String8()         { }
+    const char* string() const { return mData.Data(); }
+
+  private:
+    nsCString mData;
+  };
+
+  enum camera_facing_t {
+    CAMERA_FACING_BACK,
+    CAMERA_FACING_FRONT
+  };
+
+  struct CameraInfo {
+    camera_facing_t facing;
+  };
+
+  class Camera MOZ_FINAL : public nsISupports
+  {
+  public:
+    NS_DECL_ISUPPORTS;
+
+    void disconnect()                         { }
+    String8 getParameters()                   { return String8(); }
+    int setParameters(const String8& aParams) { return UNKNOWN_ERROR; }
+    int storeMetaDataInBuffers(bool aEnabled) { return UNKNOWN_ERROR; }
+    int autoFocus()                           { return UNKNOWN_ERROR; }
+    int cancelAutoFocus()                     { return UNKNOWN_ERROR; }
+    int takePicture(uint32_t flags)           { return UNKNOWN_ERROR; }
+    int startPreview()                        { return UNKNOWN_ERROR; }
+    int stopPreview()                         { return UNKNOWN_ERROR; }
+    int startRecording()                      { return UNKNOWN_ERROR; }
+    int stopRecording()                       { return UNKNOWN_ERROR; }
+    int startFaceDetection()                  { return UNKNOWN_ERROR; }
+    int stopFaceDetection()                   { return UNKNOWN_ERROR; }
+    static int32_t getNumberOfCameras()       { return 2; }
+
+    static int getCameraInfo(int32_t aDevice, CameraInfo* aInfo)
+    {
+      switch (aDevice) {
+        case 0:
+          aInfo->facing = CAMERA_FACING_BACK;
+          break;
+        case 1:
+          aInfo->facing = CAMERA_FACING_FRONT;
+          break;
+        default:
+          return UNKNOWN_ERROR;
+      }
+      return OK;
+    }
+
+  protected:
+    Camera()          { }
+    virtual ~Camera() { }
+
+  private:
+    Camera(const Camera&) = delete;
+    Camera& operator=(const Camera&) = delete;
+  };
+
+  class CameraParameters MOZ_FINAL
+  {
+  public:
+    static const char KEY_PREVIEW_SIZE[];
+    static const char KEY_SUPPORTED_PREVIEW_SIZES[];
+    static const char KEY_PREVIEW_FPS_RANGE[];
+    static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
+    static const char KEY_PREVIEW_FORMAT[];
+    static const char KEY_SUPPORTED_PREVIEW_FORMATS[];
+    static const char KEY_PREVIEW_FRAME_RATE[];
+    static const char KEY_SUPPORTED_PREVIEW_FRAME_RATES[];
+    static const char KEY_PICTURE_SIZE[];
+    static const char KEY_SUPPORTED_PICTURE_SIZES[];
+    static const char KEY_PICTURE_FORMAT[];
+    static const char KEY_SUPPORTED_PICTURE_FORMATS[];
+    static const char KEY_JPEG_THUMBNAIL_WIDTH[];
+    static const char KEY_JPEG_THUMBNAIL_HEIGHT[];
+    static const char KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[];
+    static const char KEY_JPEG_THUMBNAIL_QUALITY[];
+    static const char KEY_JPEG_QUALITY[];
+    static const char KEY_ROTATION[];
+    static const char KEY_GPS_LATITUDE[];
+    static const char KEY_GPS_LONGITUDE[];
+    static const char KEY_GPS_ALTITUDE[];
+    static const char KEY_GPS_TIMESTAMP[];
+    static const char KEY_GPS_PROCESSING_METHOD[];
+    static const char KEY_WHITE_BALANCE[];
+    static const char KEY_SUPPORTED_WHITE_BALANCE[];
+    static const char KEY_EFFECT[];
+    static const char KEY_SUPPORTED_EFFECTS[];
+    static const char KEY_ANTIBANDING[];
+    static const char KEY_SUPPORTED_ANTIBANDING[];
+    static const char KEY_SCENE_MODE[];
+    static const char KEY_SUPPORTED_SCENE_MODES[];
+    static const char KEY_FLASH_MODE[];
+    static const char KEY_SUPPORTED_FLASH_MODES[];
+    static const char KEY_FOCUS_MODE[];
+    static const char KEY_SUPPORTED_FOCUS_MODES[];
+    static const char KEY_MAX_NUM_FOCUS_AREAS[];
+    static const char KEY_FOCUS_AREAS[];
+    static const char KEY_FOCAL_LENGTH[];
+    static const char KEY_HORIZONTAL_VIEW_ANGLE[];
+    static const char KEY_VERTICAL_VIEW_ANGLE[];
+    static const char KEY_EXPOSURE_COMPENSATION[];
+    static const char KEY_MAX_EXPOSURE_COMPENSATION[];
+    static const char KEY_MIN_EXPOSURE_COMPENSATION[];
+    static const char KEY_EXPOSURE_COMPENSATION_STEP[];
+    static const char KEY_AUTO_EXPOSURE_LOCK[];
+    static const char KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[];
+    static const char KEY_AUTO_WHITEBALANCE_LOCK[];
+    static const char KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[];
+    static const char KEY_MAX_NUM_METERING_AREAS[];
+    static const char KEY_METERING_AREAS[];
+    static const char KEY_ZOOM[];
+    static const char KEY_MAX_ZOOM[];
+    static const char KEY_ZOOM_RATIOS[];
+    static const char KEY_ZOOM_SUPPORTED[];
+    static const char KEY_SMOOTH_ZOOM_SUPPORTED[];
+    static const char KEY_FOCUS_DISTANCES[];
+    static const char KEY_VIDEO_SIZE[];
+    static const char KEY_SUPPORTED_VIDEO_SIZES[];
+    static const char KEY_MAX_NUM_DETECTED_FACES_HW[];
+    static const char KEY_MAX_NUM_DETECTED_FACES_SW[];
+    static const char KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[];
+    static const char KEY_VIDEO_FRAME_FORMAT[];
+    static const char KEY_RECORDING_HINT[];
+    static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
+    static const char KEY_VIDEO_STABILIZATION[];
+    static const char KEY_VIDEO_STABILIZATION_SUPPORTED[];
+    static const char KEY_LIGHTFX[];
+  };
+
+  class MediaProfiles MOZ_FINAL
+  {
+  public:
+    static MediaProfiles* getInstance() {
+      if (!sMediaProfiles) {
+        sMediaProfiles = new MediaProfiles();
+      }
+      return sMediaProfiles;
+    }
+
+    bool hasCamcorderProfile(int aCameraId, camcorder_quality aQuality) const {
+      switch (aQuality) {
+        case CAMCORDER_QUALITY_LOW:
+        case CAMCORDER_QUALITY_HIGH:
+        case CAMCORDER_QUALITY_QVGA:
+        case CAMCORDER_QUALITY_VGA:
+          return true;
+        default:
+          break;
+      }
+      return false;
+    }
+
+    int getCamcorderProfileParamByName(const char* aParameter, int aCameraId, camcorder_quality aQuality) const {
+      switch (aQuality) {
+        case CAMCORDER_QUALITY_LOW:
+        case CAMCORDER_QUALITY_QVGA:
+          if (strcmp(aParameter, "vid.width") == 0) {
+            return 320;
+          } else if (strcmp(aParameter, "vid.height") == 0) {
+            return 240;
+          }
+          return 0;
+        case CAMCORDER_QUALITY_HIGH:
+        case CAMCORDER_QUALITY_VGA:
+          if (strcmp(aParameter, "vid.width") == 0) {
+            return 640;
+          } else if (strcmp(aParameter, "vid.height") == 0) {
+            return 480;
+          }
+          return 0;
+        default:
+          break;
+      }
+      return -1;
+    }
+
+  protected:
+    MediaProfiles()          { }
+    virtual ~MediaProfiles() { }
+
+  private:
+    MediaProfiles(const MediaProfiles&) = delete;
+    MediaProfiles& operator=(const MediaProfiles&) = delete;
+
+    static MediaProfiles* sMediaProfiles;
+  };
+}
+
+#endif
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -17,35 +17,34 @@
 #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 "Layers.h"
+#ifdef MOZ_WIDGET_GONK
+#include <media/mediaplayer.h>
+#include <media/MediaProfiles.h>
+#include "GrallocImages.h"
+#endif
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 #include "nsThread.h"
-#include <media/MediaProfiles.h>
 #include "mozilla/FileUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "nsAlgorithm.h"
-#include <media/mediaplayer.h>
 #include "nsPrintfCString.h"
-#include "nsIObserverService.h"
-#include "nsIVolume.h"
-#include "nsIVolumeService.h"
 #include "AutoRwLock.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkRecorderProfiles.h"
-#include "GrallocImages.h"
 #include "CameraCommon.h"
 #include "GonkCameraParameters.h"
 #include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace mozilla::ipc;
@@ -66,17 +65,19 @@ nsGonkCameraControl::nsGonkCameraControl
   , mLastThumbnailSize({0, 0})
   , mPreviewFps(30)
   , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
   , mFlashSupported(false)
   , mLuminanceSupported(false)
   , mAutoFlashModeOverridden(false)
   , mSeparateVideoAndPreviewSizesSupported(false)
   , mDeferConfigUpdate(0)
+#ifdef MOZ_WIDGET_GONK
   , mRecorder(nullptr)
+#endif
   , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
   , mVideoFile(nullptr)
   , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mImageContainer = LayerManager::CreateImageContainer();
 }
@@ -1160,17 +1161,19 @@ nsresult
 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                         const StartRecordingOptions* aOptions)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
   NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
+#ifdef MOZ_WIDGET_GONK
   NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
+#endif
 
   /**
    * 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.
@@ -1207,26 +1210,28 @@ nsGonkCameraControl::StartRecordingImpl(
     }
   } else {
     rv = SetupRecording(fd, 0, 0, 0);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+#ifdef MOZ_WIDGET_GONK
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     // important: we MUST destroy the recorder if start() fails!
     mRecorder = nullptr;
     // put the flash back to the 'auto' state
     if (mAutoFlashModeOverridden) {
       SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
     }
     return NS_ERROR_FAILURE;
   }
+#endif
 
   OnRecorderStateChange(CameraControlListener::kRecorderStarted);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopRecordingImpl()
 {
@@ -1250,16 +1255,17 @@ nsGonkCameraControl::StopRecordingImpl()
     }
 
   private:
     nsRefPtr<DeviceStorageFile> mFile;
   };
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
+#ifdef MOZ_WIDGET_GONK
   // nothing to do if we have no mRecorder
   if (!mRecorder) {
     return NS_OK;
   }
 
   mRecorder->stop();
   mRecorder = nullptr;
   OnRecorderStateChange(CameraControlListener::kRecorderStopped);
@@ -1272,16 +1278,19 @@ nsGonkCameraControl::StopRecordingImpl()
       if (NS_FAILED(rv)) {
         DOM_CAMERA_LOGE("Failed to set flash mode (0x%x)\n", rv);
       }
     }
   }
 
   // notify DeviceStorage that the new video file is closed and ready
   return NS_DispatchToMainThread(new RecordingComplete(mVideoFile));
+#else
+  return NS_OK;
+#endif
 }
 
 nsresult
 nsGonkCameraControl::ResumeContinuousFocusImpl()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   RETURN_IF_NO_CAMERA_HW();
 
@@ -1692,16 +1701,17 @@ nsGonkCameraControl::SetVideoConfigurati
       return rv;
     }
   }
 
   mPreviewFps = fps;
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 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());
@@ -1838,24 +1848,26 @@ nsGonkCameraControl::OnRecorderEvent(int
       DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
       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);
 }
+#endif
 
 nsresult
 nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
                                     uint64_t aMaxFileSizeBytes,
                                     uint64_t aMaxVideoLengthMs)
 {
   RETURN_IF_NO_CAMERA_HW();
 
+#ifdef MOZ_WIDGET_GONK
   // choosing a size big enough to hold the params
   const size_t SIZE = 256;
   char buffer[SIZE];
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
   mRecorder = new GonkRecorder();
   CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE);
@@ -1906,16 +1918,17 @@ nsGonkCameraControl::SetupRecording(int 
                       NS_ERROR_INVALID_ARG);
 
   CHECK_SETARG_RETURN(mRecorder->setListener(new GonkRecorderListener(this)),
                       NS_ERROR_FAILURE);
 
   // recording API needs file descriptor of output file
   CHECK_SETARG_RETURN(mRecorder->setOutputFile(aFd, 0, 0), NS_ERROR_FAILURE);
   CHECK_SETARG_RETURN(mRecorder->prepare(), NS_ERROR_FAILURE);
+#endif
 
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopInternal()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
@@ -2041,28 +2054,30 @@ void
 nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
 {
   CameraControlImpl::OnRateLimitPreview(aLimit);
 }
 
 void
 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
 {
+#ifdef MOZ_WIDGET_GONK
   nsRefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
 
   GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get());
 
   GrallocImage::GrallocData data;
   data.mGraphicBuffer = aBuffer;
   data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width,
                           mCurrentConfiguration.mPreviewSize.height);
   videoImage->SetData(data);
 
   OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
                     mCurrentConfiguration.mPreviewSize.height);
+#endif
 }
 
 void
 nsGonkCameraControl::OnSystemError(CameraControlListener::SystemContext aWhere,
                                    nsresult aError)
 {
   if (aWhere == CameraControlListener::kSystemService) {
     StopInternal();
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -14,28 +14,34 @@
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERACONTROL_H
 #define DOM_CAMERA_GONKCAMERACONTROL_H
 
 #include "base/basictypes.h"
 #include "nsRefPtrHashtable.h"
-#include <media/MediaProfiles.h>
 #include "mozilla/ReentrantMonitor.h"
 #include "DeviceStorage.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
-#include "GonkRecorder.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkCameraParameters.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <media/MediaProfiles.h>
+#include <camera/Camera.h>
+#include "GonkRecorder.h"
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
+
 namespace android {
   class GonkCameraHardware;
-  class MediaProfiles;
   class GonkRecorder;
   class GonkCameraSource;
 }
 
 namespace mozilla {
 
 namespace layers {
   class TextureClient;
@@ -51,17 +57,19 @@ public:
   nsGonkCameraControl(uint32_t aCameraId);
 
   void OnAutoFocusComplete(bool aSuccess);
   void OnFacesDetected(camera_frame_metadata_t* aMetaData);
   void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
   void OnTakePictureError();
   void OnRateLimitPreview(bool aLimit);
   void OnNewPreviewFrame(layers::TextureClient* aBuffer);
+#ifdef MOZ_WIDGET_GONK
   void OnRecorderEvent(int msg, int ext1, int ext2);
+#endif
   void OnSystemError(CameraControlListener::SystemContext aWhere, nsresult aError);
 
   // See ICameraControl.h for getter/setter return values.
   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;
@@ -172,17 +180,19 @@ protected:
   bool                      mLuminanceSupported;
   bool                      mAutoFlashModeOverridden;
   bool                      mSeparateVideoAndPreviewSizesSupported;
   Atomic<uint32_t>          mDeferConfigUpdate;
   GonkCameraParameters      mParams;
 
   nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
 
+#ifdef MOZ_WIDGET_GONK
   nsRefPtr<android::GonkRecorder> mRecorder;
+#endif
   // Touching mRecorder happens inside this monitor because the destructor
   // can run on any thread, and we need to be able to clean up properly if
   // GonkCameraControl goes away.
   ReentrantMonitor          mRecorderMonitor;
 
   // Supported recorder profiles
   nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
 
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -12,52 +12,63 @@
  * 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 "TestGonkCameraHardware.h"
 
+#ifdef MOZ_WIDGET_GONK
 #include <binder/IPCThreadState.h>
 #include <sys/system_properties.h>
+#include "GonkNativeWindow.h"
+#endif
 
 #include "base/basictypes.h"
 #include "nsDebug.h"
 #include "mozilla/layers/TextureClient.h"
 #include "CameraPreferences.h"
 #include "mozilla/RefPtr.h"
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
 #include "GonkBufferQueueProducer.h"
 #endif
 #include "GonkCameraControl.h"
-#include "GonkNativeWindow.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace android;
 
+#ifndef MOZ_WIDGET_GONK
+NS_IMPL_ISUPPORTS0(GonkCameraHardware);
+NS_IMPL_ISUPPORTS0(android::Camera);
+#endif
+
 GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera)
   : mCameraId(aCameraId)
   , mClosing(false)
   , mNumFrames(0)
+#ifdef MOZ_WIDGET_GONK
   , mCamera(aCamera)
+#endif
   , mTarget(aTarget)
+  , mRawSensorOrientation(0)
   , mSensorOrientation(0)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
 }
 
 void
 GonkCameraHardware::OnRateLimitPreview(bool aLimit)
 {
   ::OnRateLimitPreview(mTarget, aLimit);
 }
 
+#ifdef MOZ_WIDGET_GONK
 void
 GonkCameraHardware::OnNewFrame()
 {
   if (mClosing) {
     return;
   }
   RefPtr<TextureClient> buffer = mNativeWindow->getCurrentBuffer();
   if (!buffer) {
@@ -145,22 +156,24 @@ GonkCameraHardware::postDataTimestamp(ns
       DOM_CAMERA_LOGW("Listener unable to process. Drop a recording frame.");
       mCamera->releaseRecordingFrame(aDataPtr);
     }
   } else {
     DOM_CAMERA_LOGW("No listener was set. Drop a recording frame.");
     mCamera->releaseRecordingFrame(aDataPtr);
   }
 }
+#endif
 
 nsresult
 GonkCameraHardware::Init()
 {
   DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this);
 
+#ifdef MOZ_WIDGET_GONK
   CameraInfo info;
   int rv = Camera::getCameraInfo(mCameraId, &info);
   if (rv != 0) {
     DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId);
     return NS_ERROR_NOT_INITIALIZED;
    }
 
   mRawSensorOrientation = info.orientation;
@@ -179,18 +192,16 @@ GonkCameraHardware::Init()
     mSensorOrientation += offset;
     mSensorOrientation %= 360;
   }
   DOM_CAMERA_LOGI("Sensor orientation: base=%d, offset=%d, final=%d\n", info.orientation, offset, mSensorOrientation);
 
   // Disable shutter sound in android CameraService because gaia camera app will play it
   mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
 
-#if defined(MOZ_WIDGET_GONK)
-
 #if ANDROID_VERSION >= 21
   sp<IGraphicBufferProducer> producer;
   sp<IGonkGraphicBufferConsumer> consumer;
   GonkBufferQueue::createBufferQueue(&producer, &consumer);
   static_cast<GonkBufferQueueProducer*>(producer.get())->setSynchronousMode(false);
   mNativeWindow = new GonkNativeWindow(consumer, GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
   mCamera->setPreviewTarget(producer);
 #elif ANDROID_VERSION >= 19
@@ -226,21 +237,23 @@ sp<GonkCameraHardware>
 GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId)
 {
   sp<Camera> camera;
 
   nsCString test;
   CameraPreferences::GetPref("camera.control.test.enabled", test);
 
   if (!test.EqualsASCII("hardware")) {
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+#ifdef MOZ_WIDGET_GONK
+#if ANDROID_VERSION >= 18
     camera = Camera::connect(aCameraId, /* clientPackageName */String16("gonk.camera"), Camera::USE_CALLING_UID);
 #else
     camera = Camera::connect(aCameraId);
 #endif
+#endif
 
     if (camera.get() == nullptr) {
       return nullptr;
     }
   }
 
   sp<GonkCameraHardware> cameraHardware;
   if (test.EqualsASCII("hardware")) {
@@ -265,31 +278,35 @@ GonkCameraHardware::Close()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
 
   mClosing = true;
   if (mCamera.get()) {
     mCamera->stopPreview();
     mCamera->disconnect();
   }
+  mCamera.clear();
+#ifdef MOZ_WIDGET_GONK
   if (mNativeWindow.get()) {
     mNativeWindow->abandon();
   }
-  mCamera.clear();
   mNativeWindow.clear();
 
   // Ensure that ICamera's destructor is actually executed
   IPCThreadState::self()->flushCommands();
+#endif
 }
 
 GonkCameraHardware::~GonkCameraHardware()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
   mCamera.clear();
+#ifdef MOZ_WIDGET_GONK
   mNativeWindow.clear();
+#endif
 }
 
 int
 GonkCameraHardware::GetSensorOrientation(uint32_t aType)
 {
   DOM_CAMERA_LOGI("%s\n", __func__);
 
   switch (aType) {
@@ -365,36 +382,38 @@ GonkCameraHardware::CancelTakePicture()
 
 int
 GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
 {
   const String8 s = aParams.Flatten();
   return mCamera->setParameters(s);
 }
 
+nsresult
+GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
+{
+  const String8 s = mCamera->getParameters();
+  return aParams.Unflatten(s);
+}
+
+#ifdef MOZ_WIDGET_GONK
 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);
 }
+#endif
 
 int
 GonkCameraHardware::StartPreview()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   return mCamera->startPreview();
 }
 
@@ -421,26 +440,28 @@ GonkCameraHardware::StartRecording()
 int
 GonkCameraHardware::StopRecording()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mCamera->stopRecording();
   return OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 int
 GonkCameraHardware::SetListener(const sp<GonkCameraListener>& aListener)
 {
   mListener = aListener;
   return OK;
 }
 
 void
 GonkCameraHardware::ReleaseRecordingFrame(const sp<IMemory>& aFrame)
 {
   mCamera->releaseRecordingFrame(aFrame);
 }
+#endif
 
 int
 GonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled)
 {
   return mCamera->storeMetaDataInBuffers(aEnabled);
 }
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -12,39 +12,51 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERAHWMGR_H
 #define DOM_CAMERA_GONKCAMERAHWMGR_H
 
+#include "GonkCameraControl.h"
+#include "CameraCommon.h"
+#include "GonkCameraParameters.h"
+#include "mozilla/ReentrantMonitor.h"
+
+#ifdef MOZ_WIDGET_GONK
 #include <binder/IMemory.h>
 #include <camera/Camera.h>
 #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"
+#else
+#include "FallbackCameraPlatform.h"
+#endif
 
 namespace mozilla {
   class nsGonkCameraControl;
   class GonkCameraParameters;
 }
 
 namespace android {
 
-class GonkCameraHardware : public GonkNativeWindowNewFrameCallback
-                         , public CameraListener
+class GonkCameraHardware
+#ifdef MOZ_WIDGET_GONK
+  : public GonkNativeWindowNewFrameCallback
+  , public CameraListener
+#else
+  : public nsISupports
+#endif
 {
+#ifndef MOZ_WIDGET_GONK
+  NS_DECL_ISUPPORTS
+#endif
+
 protected:
   GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera);
   virtual ~GonkCameraHardware();
 
   // Initialize the AOSP camera interface.
   //
   // Return values:
   //  - NS_OK on success;
@@ -52,23 +64,25 @@ protected:
   virtual nsresult Init();
 
 public:
   static sp<GonkCameraHardware> Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId);
   virtual void Close();
 
   virtual void OnRateLimitPreview(bool aLimit);
 
+#ifdef MOZ_WIDGET_GONK
   // derived from GonkNativeWindowNewFrameCallback
   virtual void OnNewFrame() MOZ_OVERRIDE;
 
   // derived from CameraListener
   virtual void notify(int32_t aMsgType, int32_t ext1, int32_t ext2);
   virtual void postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, camera_frame_metadata_t* metadata);
   virtual void postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr);
+#endif
 
   /**
    * The physical orientation of the camera sensor: 0, 90, 180, or 270.
    *
    * For example, suppose a device has a naturally tall screen. The
    * back-facing camera sensor is mounted in landscape. You are looking at
    * the screen. If the top side of the camera sensor is aligned with the
    * right edge of the screen in natural orientation, the value should be
@@ -98,33 +112,37 @@ public:
   virtual int      CancelAutoFocus();
   virtual int      StartFaceDetection();
   virtual int      StopFaceDetection();
   virtual int      TakePicture();
   virtual void     CancelTakePicture();
   virtual int      StartPreview();
   virtual void     StopPreview();
   virtual int      PushParameters(const mozilla::GonkCameraParameters& aParams);
+  virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
+#ifdef MOZ_WIDGET_GONK
   virtual int      PushParameters(const CameraParameters& aParams);
-  virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
   virtual void     PullParameters(CameraParameters& aParams);
+  virtual int      SetListener(const sp<GonkCameraListener>& aListener);
+  virtual void     ReleaseRecordingFrame(const sp<IMemory>& aFrame);
+#endif
   virtual int      StartRecording();
   virtual int      StopRecording();
-  virtual int      SetListener(const sp<GonkCameraListener>& aListener);
-  virtual void     ReleaseRecordingFrame(const sp<IMemory>& aFrame);
   virtual int      StoreMetaDataInBuffers(bool aEnabled);
 
 protected:
   uint32_t                      mCameraId;
   bool                          mClosing;
   uint32_t                      mNumFrames;
   sp<Camera>                    mCamera;
   mozilla::nsGonkCameraControl* mTarget;
+#ifdef MOZ_WIDGET_GONK
   sp<GonkNativeWindow>          mNativeWindow;
   sp<GonkCameraListener>        mListener;
+#endif
   int                           mRawSensorOrientation;
   int                           mSensorOrientation;
 
 private:
   GonkCameraHardware(const GonkCameraHardware&) = delete;
   GonkCameraHardware& operator=(const GonkCameraHardware&) = delete;
 };
 
--- a/dom/camera/GonkCameraManager.cpp
+++ b/dom/camera/GonkCameraManager.cpp
@@ -10,24 +10,27 @@
  * 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 "ICameraControl.h"
-
-#include <camera/Camera.h>
-
 #include "CameraCommon.h"
 #include "GonkCameraControl.h"
 #include "CameraPreferences.h"
 #include "TestGonkCameraControl.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <camera/Camera.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 using namespace mozilla;
 
 // From ICameraControl, gonk-specific management functions
 nsresult
 ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
 {
   aDeviceCount = android::Camera::getNumberOfCameras();
   return NS_OK;
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -10,22 +10,22 @@
  * 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 "CameraPreferences.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 #include "mozilla/Hal.h"
 #include "nsDataHashtable.h"
+#include "nsPrintfCString.h"
 
 using namespace mozilla;
 using namespace android;
 
 /* static */ bool
 GonkCameraParameters::IsLowMemoryPlatform()
 {
   bool testIsLowMem = false;
@@ -47,189 +47,246 @@ GonkCameraParameters::IsLowMemoryPlatfor
       return true;
     }
   }
 
   return false;
 }
 
 const char*
-GonkCameraParameters::Parameters::FindVendorSpecificKey(const char* aPotentialKeys[],
-                                                        size_t aPotentialKeyCount)
+GonkCameraParameters::FindVendorSpecificKey(const char* aPotentialKeys[],
+                                            size_t aPotentialKeyCount)
 {
   const char* val;
 
   for (size_t i = 0; i < aPotentialKeyCount; ++i) {
-    get(aPotentialKeys[i], val);
+    GetImpl(aPotentialKeys[i], val);
     if (val) {
       // We received a value (potentially an empty-string one),
       // which indicates that this key exists.
       return aPotentialKeys[i];
     }
   }
 
   return nullptr;
 }
 
+/* static */ PLDHashOperator
+GonkCameraParameters::EnumerateFlatten(const nsACString& aKey,
+                                       nsCString* aValue,
+                                       void* aUserArg)
+{
+  nsCString* data = static_cast<nsCString*>(aUserArg);
+  if (!data->IsEmpty()) {
+    data->Append(';');
+  }
+  data->Append(aKey);
+  data->Append('=');
+  data->Append(*aValue);
+  return PL_DHASH_NEXT;
+}
+
+String8
+GonkCameraParameters::Flatten() const
+{
+  MutexAutoLock lock(mLock);
+  nsCString data;
+  mParams.EnumerateRead(EnumerateFlatten, static_cast<void*>(&data));
+  return String8(data.Data());
+}
+
+nsresult
+GonkCameraParameters::Unflatten(const String8& aFlatParameters)
+{
+  MutexAutoLock lock(mLock);
+  mParams.Clear();
+
+  const char* data = aFlatParameters.string();
+  while (data && *data) {
+    const char* pos = strchr(data, '=');
+    if (!pos) {
+      break;
+    }
+
+    nsAutoCString key(data, pos - data);
+    data = pos + 1;
+
+    nsCString* value;
+    pos = strchr(data, ';');
+    if (pos) {
+      value = new nsCString(data, pos - data);
+      data = pos + 1;
+    } else {
+      value = new nsCString(data);
+      data = nullptr;
+    }
+
+    mParams.Put(key, value);
+  }
+
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  // We call Initialize() once when the parameter set is first loaded,
+  // to set up any constant values this class requires internally,
+  // e.g. the exposure compensation step and limits.
+  return Initialize();
+}
+
 const char*
-GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
+GonkCameraParameters::GetTextKey(uint32_t aKey)
 {
   switch (aKey) {
     case CAMERA_PARAM_PREVIEWSIZE:
-      return KEY_PREVIEW_SIZE;
+      return CameraParameters::KEY_PREVIEW_SIZE;
     case CAMERA_PARAM_PREVIEWFORMAT:
-      return KEY_PREVIEW_FORMAT;
+      return CameraParameters::KEY_PREVIEW_FORMAT;
     case CAMERA_PARAM_PREVIEWFRAMERATE:
-      return KEY_PREVIEW_FRAME_RATE;
+      return CameraParameters::KEY_PREVIEW_FRAME_RATE;
     case CAMERA_PARAM_EFFECT:
-      return KEY_EFFECT;
+      return CameraParameters::KEY_EFFECT;
     case CAMERA_PARAM_WHITEBALANCE:
-      return KEY_WHITE_BALANCE;
+      return CameraParameters::KEY_WHITE_BALANCE;
     case CAMERA_PARAM_SCENEMODE:
-      return KEY_SCENE_MODE;
+      return CameraParameters::KEY_SCENE_MODE;
     case CAMERA_PARAM_FLASHMODE:
-      return KEY_FLASH_MODE;
+      return CameraParameters::KEY_FLASH_MODE;
     case CAMERA_PARAM_FOCUSMODE:
-      return KEY_FOCUS_MODE;
+      return CameraParameters::KEY_FOCUS_MODE;
     case CAMERA_PARAM_ZOOM:
-      return KEY_ZOOM;
+      return CameraParameters::KEY_ZOOM;
     case CAMERA_PARAM_METERINGAREAS:
-      return KEY_METERING_AREAS;
+      return CameraParameters::KEY_METERING_AREAS;
     case CAMERA_PARAM_FOCUSAREAS:
-      return KEY_FOCUS_AREAS;
+      return CameraParameters::KEY_FOCUS_AREAS;
     case CAMERA_PARAM_FOCALLENGTH:
-      return KEY_FOCAL_LENGTH;
+      return CameraParameters::KEY_FOCAL_LENGTH;
     case CAMERA_PARAM_FOCUSDISTANCENEAR:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_FOCUSDISTANCEFAR:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_EXPOSURECOMPENSATION:
-      return KEY_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_THUMBNAILQUALITY:
-      return KEY_JPEG_THUMBNAIL_QUALITY;
+      return CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY;
     case CAMERA_PARAM_PICTURE_SIZE:
-      return KEY_PICTURE_SIZE;
+      return CameraParameters::KEY_PICTURE_SIZE;
     case CAMERA_PARAM_PICTURE_FILEFORMAT:
-      return KEY_PICTURE_FORMAT;
+      return CameraParameters::KEY_PICTURE_FORMAT;
     case CAMERA_PARAM_PICTURE_ROTATION:
-      return KEY_ROTATION;
+      return CameraParameters::KEY_ROTATION;
     case CAMERA_PARAM_PICTURE_DATETIME:
-      // Not every platform defines a KEY_EXIF_DATETIME;
+      // Not every platform defines a CameraParameters::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";
     case CAMERA_PARAM_VIDEOSIZE:
-      return KEY_VIDEO_SIZE;
+      return CameraParameters::KEY_VIDEO_SIZE;
     case CAMERA_PARAM_ISOMODE:
       if (!mVendorSpecificKeyIsoMode) {
         const char* isoModeKeys[] = {
           "iso",
           "sony-iso"
         };
         mVendorSpecificKeyIsoMode =
           FindVendorSpecificKey(isoModeKeys, MOZ_ARRAY_LENGTH(isoModeKeys));
       }
       return mVendorSpecificKeyIsoMode;
     case CAMERA_PARAM_LUMINANCE:
       return "luminance-condition";
     case CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE:
-      // Not every platform defines KEY_QC_HDR_NEED_1X;
+      // Not every platform defines CameraParameters::QC_HDR_NEED_1X;
       // for those that don't, we use the raw string key.
       return "hdr-need-1x";
     case CAMERA_PARAM_RECORDINGHINT:
-      return KEY_RECORDING_HINT;
+      return CameraParameters::KEY_RECORDING_HINT;
     case CAMERA_PARAM_PICTURE_QUALITY:
-      return KEY_JPEG_QUALITY;
+      return CameraParameters::KEY_JPEG_QUALITY;
     case CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO:
-      return KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO;
+      return CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO;
     case CAMERA_PARAM_METERINGMODE:
-      // Not every platform defines KEY_AUTO_EXPOSURE.
+      // Not every platform defines CameraParameters::AUTO_EXPOSURE.
       return "auto-exposure";
 
     case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
-      return KEY_SUPPORTED_PREVIEW_SIZES;
+      return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
-      return KEY_SUPPORTED_PICTURE_SIZES;
+      return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES;
     case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
-      return KEY_SUPPORTED_VIDEO_SIZES;
+      return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
-      return KEY_SUPPORTED_PICTURE_FORMATS;
+      return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS;
     case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
-      return KEY_SUPPORTED_WHITE_BALANCE;
+      return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE;
     case CAMERA_PARAM_SUPPORTED_SCENEMODES:
-      return KEY_SUPPORTED_SCENE_MODES;
+      return CameraParameters::KEY_SUPPORTED_SCENE_MODES;
     case CAMERA_PARAM_SUPPORTED_EFFECTS:
-      return KEY_SUPPORTED_EFFECTS;
+      return CameraParameters::KEY_SUPPORTED_EFFECTS;
     case CAMERA_PARAM_SUPPORTED_FLASHMODES:
-      return KEY_SUPPORTED_FLASH_MODES;
+      return CameraParameters::KEY_SUPPORTED_FLASH_MODES;
     case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
-      return KEY_SUPPORTED_FOCUS_MODES;
+      return CameraParameters::KEY_SUPPORTED_FOCUS_MODES;
     case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
-      return KEY_MAX_NUM_FOCUS_AREAS;
+      return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS;
     case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
-      return KEY_MAX_NUM_METERING_AREAS;
+      return CameraParameters::KEY_MAX_NUM_METERING_AREAS;
     case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
-      return KEY_MIN_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
-      return KEY_MAX_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
-      return KEY_EXPOSURE_COMPENSATION_STEP;
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP;
     case CAMERA_PARAM_SUPPORTED_ZOOM:
-      return KEY_ZOOM_SUPPORTED;
+      return CameraParameters::KEY_ZOOM_SUPPORTED;
     case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
-      return KEY_ZOOM_RATIOS;
+      return CameraParameters::KEY_ZOOM_RATIOS;
     case CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES:
-      return KEY_MAX_NUM_DETECTED_FACES_HW;
+      return CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW;
     case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
-      return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
+      return CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
     case CAMERA_PARAM_SUPPORTED_ISOMODES:
       if (!mVendorSpecificKeySupportedIsoModes) {
         const char* supportedIsoModesKeys[] = {
           "iso-values",
           "sony-iso-values"
         };
         mVendorSpecificKeySupportedIsoModes =
           FindVendorSpecificKey(supportedIsoModesKeys,
                                 MOZ_ARRAY_LENGTH(supportedIsoModesKeys));
       }
       return mVendorSpecificKeySupportedIsoModes;
     case CAMERA_PARAM_SUPPORTED_METERINGMODES:
-      // Not every platform defines KEY_SUPPORTED_AUTO_EXPOSURE.
+      // Not every platform defines CameraParameters::SUPPORTED_AUTO_EXPOSURE.
       return "auto-exposure-values";
     default:
       DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey);
       return nullptr;
   }
 }
 
 GonkCameraParameters::GonkCameraParameters()
-  : mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock"))
+  : mLock("mozilla::camera::GonkCameraParameters")
   , mDirty(false)
   , mInitialized(false)
   , mExposureCompensationStep(0.0)
+  , mVendorSpecificKeyIsoMode(nullptr)
+  , mVendorSpecificKeySupportedIsoModes(nullptr)
 {
   MOZ_COUNT_CTOR(GonkCameraParameters);
-  if (!mLock) {
-    MOZ_CRASH("Out of memory getting new PRRWLock");
-  }
 }
 
 GonkCameraParameters::~GonkCameraParameters()
 {
   MOZ_COUNT_DTOR(GonkCameraParameters);
   mIsoModeMap.Clear();
-  MOZ_ASSERT(mLock, "mLock missing in ~GonkCameraParameters()");
-  if (mLock) {
-    PR_DestroyRWLock(mLock);
-    mLock = nullptr;
-  }
 }
 
 nsresult
 GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut)
 {
   nsCString* s;
   if (mIsoModeMap.Get(aIso, &s)) {
     if (!s) {
@@ -273,27 +330,27 @@ GonkCameraParameters::MapIsoFromGonk(con
 
 // Any members that need to be initialized on the first parameter pull
 // need to get handled in here.
 nsresult
 GonkCameraParameters::Initialize()
 {
   nsresult rv;
 
-  rv = GetImpl(Parameters::KEY_EXPOSURE_COMPENSATION_STEP, mExposureCompensationStep);
+  rv = GetImpl(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, mExposureCompensationStep);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize exposure compensation step size");
     mExposureCompensationStep = 0.0;
   }
-  rv = GetImpl(Parameters::KEY_MIN_EXPOSURE_COMPENSATION, mExposureCompensationMinIndex);
+  rv = GetImpl(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, mExposureCompensationMinIndex);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize minimum exposure compensation index");
     mExposureCompensationMinIndex = 0;
   }
-  rv = GetImpl(Parameters::KEY_MAX_EXPOSURE_COMPENSATION, mExposureCompensationMaxIndex);
+  rv = GetImpl(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, mExposureCompensationMaxIndex);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize maximum exposure compensation index");
     mExposureCompensationMaxIndex = 0;
   }
 
   rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
   if (NS_FAILED(rv)) {
     // zoom is not supported
@@ -420,19 +477,19 @@ GonkCameraParameters::SetTranslated(uint
 
   nsresult rv;
 
   switch (aKey) {
     case CAMERA_PARAM_THUMBNAILSIZE:
       // This is a special case--for some reason the thumbnail size
       // is accessed as two separate values instead of a tuple.
       // XXXmikeh - make this restore the original values on error
-      rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
+      rv = SetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
       if (NS_SUCCEEDED(rv)) {
-        rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
+        rv = SetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
       }
       break;
 
     case CAMERA_PARAM_VIDEOSIZE:
       // "record-size" is probably deprecated in later ICS;
       // might need to set "video-size" instead of "record-size";
       // for the time being, set both. See bug 795332.
       rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
@@ -456,24 +513,24 @@ nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize)
 {
   nsresult rv;
 
   if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
     int width;
     int height;
 
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
+    rv = GetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (width < 0) {
       return NS_ERROR_NOT_AVAILABLE;
     }
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
+    rv = GetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (height < 0) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     aSize.width = static_cast<uint32_t>(width);
@@ -567,37 +624,37 @@ GonkCameraParameters::GetTranslated(uint
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition)
 {
   MOZ_ASSERT(aKey == CAMERA_PARAM_PICTURE_LOCATION);
 
   // Add any specified location information -- we don't care if these fail.
   if (!isnan(aPosition.latitude)) {
     DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aPosition.latitude);
-    SetImpl(Parameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get());
+    SetImpl(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_LATITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_LATITUDE);
   }
   if (!isnan(aPosition.longitude)) {
     DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aPosition.longitude);
-    SetImpl(Parameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get());
+    SetImpl(CameraParameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_LONGITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_LONGITUDE);
   }
   if (!isnan(aPosition.altitude)) {
     DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aPosition.altitude);
-    SetImpl(Parameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get());
+    SetImpl(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_ALTITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_ALTITUDE);
   }
   if (!isnan(aPosition.timestamp)) {
     DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aPosition.timestamp);
-    SetImpl(Parameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get());
+    SetImpl(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_TIMESTAMP);
+    ClearImpl(CameraParameters::KEY_GPS_TIMESTAMP);
   }
 
   return NS_OK;
 }
 
 // Handle int64_ts
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue)
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -13,24 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERAPARAMETERS_H
 #define DOM_CAMERA_GONKCAMERAPARAMETERS_H
 
 #include <math.h>
-#include "camera/CameraParameters.h"
 #include "nsTArray.h"
 #include "nsString.h"
-#include "AutoRwLock.h"
-#include "nsPrintfCString.h"
+#include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "ICameraControl.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <camera/CameraParameters.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 namespace mozilla {
 
 class GonkCameraParameters
 {
 public:
   GonkCameraParameters();
   virtual ~GonkCameraParameters();
 
@@ -39,166 +43,198 @@ public:
   // for the life of their operation. Not doing so was the cause of
   // bug 928856, which was -painful- to track down.
   //
   // Return values:
   //  - see return values for GetTranslated() and SetTranslated() below.
   template<class T> nsresult
   Set(uint32_t aKey, const T& aValue)
   {
-    RwLockAutoEnterWrite lock(mLock);
+    MutexAutoLock lock(mLock);
     nsresult rv = SetTranslated(aKey, aValue);
     mDirty = mDirty || NS_SUCCEEDED(rv);
     return rv;
   }
 
   template<class T> nsresult
   Get(uint32_t aKey, T& aValue)
   {
-    RwLockAutoEnterRead lock(mLock);
+    MutexAutoLock lock(mLock);
     return GetTranslated(aKey, aValue);
   }
 
   bool
   TestAndClearDirtyFlag()
   {
     bool dirty;
 
-    RwLockAutoEnterWrite lock(mLock);
+    MutexAutoLock lock(mLock);
     dirty = mDirty;
     mDirty = false;
     return dirty;
   }
 
-  android::String8
-  Flatten() const
-  {
-    RwLockAutoEnterRead lock(mLock);
-    return mParams.flatten();
-  }
-
-  nsresult
-  Unflatten(const android::String8& aFlatParameters)
-  {
-    RwLockAutoEnterWrite lock(mLock);
-    mParams.unflatten(aFlatParameters);
-    if (mInitialized) {
-      return NS_OK;
-    }
-
-    // We call Initialize() once when the parameter set is first loaded,
-    // to set up any constant values this class requires internally,
-    // e.g. the exposure compensation step and limits.
-    return Initialize();
-  }
+  android::String8 Flatten() const;
+  nsresult Unflatten(const android::String8& aFlatParameters);
 
 protected:
-  PRRWLock* mLock;
+  mutable Mutex mLock;
   bool mDirty;
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationStep;
   int32_t mExposureCompensationMinIndex;
   int32_t mExposureCompensationMaxIndex;
+  const char* mVendorSpecificKeyIsoMode;
+  const char* mVendorSpecificKeySupportedIsoModes;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
   nsTArray<nsString> mSceneModes;
   nsTArray<nsString> mMeteringModes;
   nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
+  nsClassHashtable<nsCStringHashKey, nsCString> mParams;
 
-  // This subclass of android::CameraParameters just gives
-  // all of the AOSP getters and setters the same signature.
-  class Parameters : public android::CameraParameters
+  static PLDHashOperator EnumerateFlatten(const nsACString& aKey, nsCString* aValue, void* aUserArg);
+
+  nsresult SetImpl(const char* aKey, const char* aValue)
+  {
+    nsCString key(aKey);
+    mParams.Put(key, new nsCString(aValue));
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, int aValue)
+  {
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendInt(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, double aValue)
   {
-  public:
-    Parameters()
-      : mVendorSpecificKeyIsoMode(nullptr)
-      , mVendorSpecificKeySupportedIsoModes(nullptr)
-    { }
-    virtual ~Parameters() { }
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendFloat(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
 
-    using android::CameraParameters::set;
-    using android::CameraParameters::get;
-    using android::CameraParameters::TRUE;
-    using android::CameraParameters::FALSE;
+  nsresult SetImpl(const char* aKey, float aValue)
+  {
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendFloat(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, bool aValue)
+  {
+    nsCString key(aKey);
+    mParams.Put(key, new nsCString(aValue ? "true" : "false"));
+    return NS_OK;
+  }
 
-    void set(const char* aKey, float aValue)      { setFloat(aKey, aValue); }
-    void set(const char* aKey, double aValue)     { setFloat(aKey, aValue); }
-    void set(const char* aKey, bool aValue)       { set(aKey, aValue ? TRUE : FALSE); }
-    void get(const char* aKey, float& aRet)       { aRet = getFloat(aKey); }
-    void get(const char* aKey, double& aRet)      { aRet = getFloat(aKey); }
-    void get(const char* aKey, const char*& aRet) { aRet = get(aKey); }
-    void get(const char* aKey, int& aRet)         { aRet = getInt(aKey); }
+  nsresult GetImpl(const char* aKey, const char*& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    if (!mParams.Get(key, &value)) {
+      aRet = nullptr;
+      return NS_ERROR_FAILURE;
+    }
+    aRet = value->Data();
+    return NS_OK;
+  }
 
-    void
-    get(const char* aKey, bool& aRet)
-    {
-      const char* value = get(aKey);
-      aRet = value ? strcmp(value, TRUE) == 0 : false;
+  nsresult GetImpl(const char* aKey, float& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToFloat(&rv);
+    } else {
+      aRet = 0.0;
     }
+    return rv;
+  }
 
-    void remove(const char* aKey)                 { android::CameraParameters::remove(aKey); }
-
-    const char* GetTextKey(uint32_t aKey);
+  nsresult GetImpl(const char* aKey, double& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToFloat(&rv);
+    } else {
+      aRet = 0.0;
+    }
+    return rv;
+  }
 
-  protected:
-    const char* FindVendorSpecificKey(const char* aPotentialKeys[], size_t aPotentialKeyCount);
+  nsresult GetImpl(const char* aKey, int& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToInteger(&rv);
+    } else {
+      aRet = 0.0;
+    }
+    return rv;
+  }
 
-    const char* mVendorSpecificKeyIsoMode;
-    const char* mVendorSpecificKeySupportedIsoModes;
-  };
+  nsresult GetImpl(const char* aKey, bool& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    if (!mParams.Get(key, &value)) {
+      aRet = false;
+      return NS_ERROR_FAILURE;
+    }
+    aRet = value->EqualsLiteral("true");
+    return NS_OK;
+  }
 
-  Parameters mParams;
+  const char* GetTextKey(uint32_t aKey);
+  const char* FindVendorSpecificKey(const char* aPotentialKeys[], size_t aPotentialKeyCount);
 
   // The *Impl() templates handle converting the parameter keys from
   // their enum values to string types, if necessary. These are the
   // bottom layer accessors to mParams.
   //
   // Return values:
   //  - NS_OK on success;
   //  - NS_ERROR_NOT_IMPLEMENTED if the numeric 'aKey' value is invalid.
   template<typename T> nsresult
   SetImpl(uint32_t aKey, const T& aValue)
   {
-    const char* key = mParams.GetTextKey(aKey);
+    const char* key = GetTextKey(aKey);
     NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
-
-    mParams.set(key, aValue);
-    return NS_OK;
+    return SetImpl(key, aValue);
   }
 
   template<typename T> nsresult
   GetImpl(uint32_t aKey, T& aValue)
   {
-    const char* key = mParams.GetTextKey(aKey);
+    const char* key = GetTextKey(aKey);
     NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
-
-    mParams.get(key, aValue);
-    return NS_OK;
-  }
-
-  template<class T> nsresult
-  SetImpl(const char* aKey, const T& aValue)
-  {
-    mParams.set(aKey, aValue);
-    return NS_OK;
-  }
-
-  template<class T> nsresult
-  GetImpl(const char* aKey, T& aValue)
-  {
-    mParams.get(aKey, aValue);
-    return NS_OK;
+    return GetImpl(key, aValue);
   }
 
   nsresult
   ClearImpl(const char* aKey)
   {
-    mParams.remove(aKey);
+    nsCString key(aKey);
+    mParams.Remove(key);
     return NS_OK;
   }
 
   // The *Translated() functions allow us to handle special cases;
   // for example, where the thumbnail size setting is exposed as an
   // ICameraControl::Size object, but is handled by the AOSP layer
   // as two separate parameters.
   //
--- a/dom/camera/GonkRecorderProfiles.cpp
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -10,22 +10,24 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkRecorderProfiles.h"
-#include <media/MediaProfiles.h>
 #include "nsMimeTypes.h"
-#include "GonkRecorder.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include "GonkRecorder.h"
+#endif
+
 using namespace mozilla;
 using namespace android;
 
 namespace mozilla {
 
 struct ProfileConfig {
   const char* name;
   int quality;
@@ -369,20 +371,21 @@ GonkRecorderProfile::GetAll(uint32_t aCa
 {
   ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
   if (!profiles) {
     return NS_ERROR_FAILURE;
   }
 
   aProfiles.Clear();
   profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
-  
+
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 nsresult
 GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
 {
   static const size_t SIZE = 256;
   char buffer[SIZE];
 
   // set all the params
   CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
@@ -420,8 +423,9 @@ GonkRecorderProfile::ConfigureRecorder(a
 
   GonkRecorderProfile* profile;
   if (!profiles->Get(aProfileName, &profile)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   return profile->ConfigureRecorder(aRecorder);
 }
+#endif
--- a/dom/camera/GonkRecorderProfiles.h
+++ b/dom/camera/GonkRecorderProfiles.h
@@ -1,16 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_GONK_RECORDER_PROFILES_H
 #define DOM_CAMERA_GONK_RECORDER_PROFILES_H
 
+#ifdef MOZ_WIDGET_GONK
 #include <media/MediaProfiles.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 #include "ICameraControl.h"
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #ifndef CHECK_SETARG_RETURN
 #define CHECK_SETARG_RETURN(x, rv)      \
   do {                                  \
     if (x) {                            \
@@ -104,37 +109,41 @@ typedef nsRefPtrHashtable<nsStringHashKe
 
 class GonkRecorderProfile
   : public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
 {
 public:
   static nsresult GetAll(uint32_t aCameraId,
                          nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles);
 
+#ifdef MOZ_WIDGET_GONK
   // Configures the specified recorder using the specified profile.
   //
   // Return values:
   //  - NS_OK on success;
   //  - NS_ERROR_INVALID_ARG if the profile isn't supported;
   //  - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile.
   static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder,
                                     uint32_t aCameraId,
                                     const nsAString& aProfileName);
+#endif
 
 protected:
   GonkRecorderProfile(uint32_t aCameraId,
                       int aQuality);
 
   int GetProfileParameter(const char* aParameter);
 
   bool Translate(android::output_format aContainer, nsAString& aContainerName);
   bool GetMimeType(android::output_format aContainer, nsAString& aMimeType);
   bool IsValid() const { return mIsValid; };
 
+#ifdef MOZ_WIDGET_GONK
   nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
+#endif
   static already_AddRefed<GonkRecorderProfile> CreateProfile(uint32_t aCameraId,
                                                              int aQuality);
   static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
   static PLDHashOperator Enumerate(const nsAString& aProfileName,
                                    GonkRecorderProfile* aProfile,
                                    void* aUserArg);
 
   uint32_t mCameraId;
--- a/dom/camera/TestGonkCameraHardware.cpp
+++ b/dom/camera/TestGonkCameraHardware.cpp
@@ -26,16 +26,20 @@
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsICameraTestHardware.h"
 
 using namespace android;
 using namespace mozilla;
 using namespace mozilla::dom;
 
+#ifndef MOZ_WIDGET_GONK
+NS_IMPL_ISUPPORTS_INHERITED0(TestGonkCameraHardware, GonkCameraHardware);
+#endif
+
 static void
 CopyFaceFeature(int32_t (&aDst)[2], bool aExists, const DOMPoint* aSrc)
 {
   if (aExists && aSrc) {
     aDst[0] = static_cast<int32_t>(aSrc->X());
     aDst[1] = static_cast<int32_t>(aSrc->Y());
   } else {
     aDst[0] = -2000;
@@ -628,16 +632,17 @@ TestGonkCameraHardware::PullParameters(G
     return rv;
   }
 
   String8 s(NS_LossyConvertUTF16toASCII(as).get());
   aParams.Unflatten(s);
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 int
 TestGonkCameraHardware::PushParameters(const CameraParameters& aParams)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   String8 s = aParams.flatten();
   nsresult rv = WaitWhileRunningOnMainThread(new PushParametersDelegate(this, &s));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return UNKNOWN_ERROR;
@@ -653,16 +658,17 @@ TestGonkCameraHardware::PullParameters(C
   nsresult rv = WaitWhileRunningOnMainThread(new PullParametersDelegate(this, &as));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     as.Truncate();
   }
 
   String8 s(NS_LossyConvertUTF16toASCII(as).get());
   aParams.unflatten(s);
 }
+#endif
 
 int
 TestGonkCameraHardware::StartRecording()
 {
   class Delegate : public ControlMessage
   {
   public:
     Delegate(TestGonkCameraHardware* aTestHw)
--- a/dom/camera/TestGonkCameraHardware.h
+++ b/dom/camera/TestGonkCameraHardware.h
@@ -21,32 +21,38 @@
 #include "nsAutoPtr.h"
 #include "nsIDOMEventListener.h"
 #include "mozilla/CondVar.h"
 
 namespace mozilla {
 
 class TestGonkCameraHardware : public android::GonkCameraHardware
 {
+#ifndef MOZ_WIDGET_GONK
+  NS_DECL_ISUPPORTS_INHERITED
+#endif
+
 public:
   virtual nsresult Init() MOZ_OVERRIDE;
   virtual int AutoFocus() MOZ_OVERRIDE;
   virtual int StartFaceDetection() MOZ_OVERRIDE;
   virtual int StopFaceDetection() MOZ_OVERRIDE;
   virtual int TakePicture() MOZ_OVERRIDE;
   virtual void CancelTakePicture() MOZ_OVERRIDE;
   virtual int StartPreview() MOZ_OVERRIDE;
   virtual void StopPreview() MOZ_OVERRIDE;
   virtual int PushParameters(const mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
   virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
   virtual int StartRecording() MOZ_OVERRIDE;
   virtual int StopRecording() MOZ_OVERRIDE;
   virtual int StoreMetaDataInBuffers(bool aEnabled) MOZ_OVERRIDE;
+#ifdef MOZ_WIDGET_GONK
   virtual int PushParameters(const android::CameraParameters& aParams) MOZ_OVERRIDE;
   virtual void PullParameters(android::CameraParameters& aParams) MOZ_OVERRIDE;
+#endif
 
   TestGonkCameraHardware(mozilla::nsGonkCameraControl* aTarget,
                          uint32_t aCameraId,
                          const android::sp<android::Camera>& aCamera);
 
 protected:
   virtual ~TestGonkCameraHardware();
 
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+if CONFIG['MOZ_B2G_CAMERA']:
     MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'CameraCommon.h',
     'CameraPreferences.h',
     'DOMCameraManager.h',
 ]
 
@@ -31,23 +31,31 @@ if CONFIG['MOZ_B2G_CAMERA']:
 
     XPIDL_MODULE = 'dom_camera'
 
     UNIFIED_SOURCES += [
         'GonkCameraControl.cpp',
         'GonkCameraHwMgr.cpp',
         'GonkCameraManager.cpp',
         'GonkCameraParameters.cpp',
-        'GonkCameraSource.cpp',
-        'GonkRecorder.cpp',
         'GonkRecorderProfiles.cpp',
         'TestGonkCameraControl.cpp',
         'TestGonkCameraHardware.cpp',
     ]
 
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+        UNIFIED_SOURCES += [
+            'GonkCameraSource.cpp',
+            'GonkRecorder.cpp',
+        ]
+    else:
+        UNIFIED_SOURCES += [
+            'FallbackCameraPlatform.cpp',
+        ]
+
     EXTRA_COMPONENTS += [
         'CameraTestHardware.js',
         'CameraTestHardware.manifest',
     ]
 else:
     UNIFIED_SOURCES += [
         'FallbackCameraControl.cpp',
         'FallbackCameraManager.cpp',
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -63,22 +63,28 @@ function CameraTestSuite() {
   this.waitPreviewStarted = this._waitPreviewStarted.bind(this);
   this.waitParameterPush = this._waitParameterPush.bind(this);
   this.initJsHw = this._initJsHw.bind(this);
   this.getCamera = this._getCamera.bind(this);
   this.setLowMemoryPlatform = this._setLowMemoryPlatform.bind(this);
   this.logError = this._logError.bind(this);
   this.expectedError = this._expectedError.bind(this);
   this.expectedRejectGetCamera = this._expectedRejectGetCamera.bind(this);
+  this.expectedRejectConfigure = this._expectedRejectConfigure.bind(this);
   this.expectedRejectAutoFocus = this._expectedRejectAutoFocus.bind(this);
   this.expectedRejectTakePicture = this._expectedRejectTakePicture.bind(this);
+  this.expectedRejectStartRecording = this._expectedRejectStartRecording.bind(this);
+  this.expectedRejectStopRecording = this._expectedRejectStopRecording.bind(this);
   this.rejectGetCamera = this._rejectGetCamera.bind(this);
+  this.rejectConfigure = this._rejectConfigure.bind(this);
   this.rejectRelease = this._rejectRelease.bind(this);
   this.rejectAutoFocus = this._rejectAutoFocus.bind(this);
   this.rejectTakePicture = this._rejectTakePicture.bind(this);
+  this.rejectStartRecording = this._rejectStartRecording.bind(this);
+  this.rejectStopRecording = this._rejectStopRecording.bind(this);
   this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
 
   var self = this;
   this._window.addEventListener('beforeunload', function() {
     if (isDefinedObj(self.viewfinder)) {
       self.viewfinder.mozSrcObject = null;
     }
 
@@ -90,30 +96,45 @@ function CameraTestSuite() {
     }
   });
 }
 
 CameraTestSuite.prototype = {
   camera: null,
   hw: null,
   _lowMemSet: false,
+  _reloading: false,
 
   /* Returns a promise which is resolved when the test suite is ready
      to be executing individual test cases. One may provide the expected
      hardware type here if desired; the default is to use the JS test
      hardware. Use '' for the native emulated camera hardware. */
   _setup: function(hwType) {
+    /* Depending on how we run the mochitest, we may not have the necessary
+       permissions yet. If we do need to request them, then we have to reload
+       the window to ensure the reconfiguration propogated properly. */
+    if (!SpecialPowers.hasPermission("camera", document)) {
+      info("requesting camera permission");
+      this._reloading = true;
+      SpecialPowers.addPermission("camera", true, document);
+      window.location.reload();
+      return Promise.reject();
+    }
+
+    info("has camera permission");
     if (!isDefined(hwType)) {
       hwType = 'hardware';
     }
 
     this._hwType = hwType;
     return new Promise(function(resolve, reject) {
-      SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
-        resolve();
+      SpecialPowers.pushPrefEnv({'set': [['camera.control.test.permission', true]]}, function() {
+        SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
+          resolve();
+        });
       });
     });
   },
 
   /* Returns a promise which is resolved when all of the SpecialPowers
      parameters that were set while testing are flushed. This includes
      camera.control.test.enabled and camera.control.test.is_low_memory. */
   _teardown: function() {
@@ -153,16 +174,20 @@ CameraTestSuite.prototype = {
     this._tests.push({
       name: aName,
       cb: aCb
     });
   },
 
   /* Execute all test cases (after setup is called). */
   _run: function() {
+    if (this._reloading) {
+      return;
+    }
+
     var test = this._tests.shift();
     var self = this;
     if (test) {
       info(test.name + ' started');
 
       function runNextTest() {
         self.run();
       }
@@ -343,28 +368,40 @@ CameraTestSuite.prototype = {
      when a test case does not expect a particular call
      to fail but otherwise does not require any special
      handling of that situation beyond failing the test
      case and logging why.*/
   _rejectGetCamera: function(e) {
     return this.logError('get camera failed', e);
   },
 
+  _rejectConfigure: function(e) {
+    return this.logError('set configuration failed', e);
+  },
+
   _rejectRelease: function(e) {
     return this.logError('release camera failed', e);
   },
 
   _rejectAutoFocus: function(e) {
     return this.logError('auto focus failed', e);
   },
 
   _rejectTakePicture: function(e) {
     return this.logError('take picture failed', e);
   },
 
+  _rejectStartRecording: function(e) {
+    return this.logError('start recording failed', e);
+  },
+
+  _rejectStopRecording: function(e) {
+    return this.logError('stop recording failed', e);
+  },
+
   _rejectPreviewStarted: function(e) {
     return this.logError('preview start failed', e);
   },
 
   /* The success handlers below are intended to be used
      when a test case does not expect a particular call
      to succed but otherwise does not require any special
      handling of that situation beyond failing the test
@@ -379,18 +416,30 @@ CameraTestSuite.prototype = {
 
   _expectedRejectGetCamera: function(p) {
     /* Copy handle to ensure it gets released at the end
        of the test case */
     self.camera = p.camera;
     return this.expectedError('expected get camera to fail');
   },
 
+  _expectedRejectConfigure: function(p) {
+    return this.expectedError('expected set configuration to fail');
+  },
+
   _expectedRejectAutoFocus: function(p) {
     return this.expectedError('expected auto focus to fail');
   },
 
   _expectedRejectTakePicture: function(p) {
     return this.expectedError('expected take picture to fail');
   },
+
+  _expectedRejectStartRecording: function(p) {
+    return this.expectedError('expected start recording to fail');
+  },
+
+  _expectedRejectStopRecording: function(p) {
+    return this.expectedError('expected stop recording to fail');
+  },
 };
 
 ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
--- a/dom/camera/test/mochitest.ini
+++ b/dom/camera/test/mochitest.ini
@@ -1,17 +1,21 @@
 [DEFAULT]
 support-files = camera_common.js
 
 [test_camera.html]
+skip-if = toolkit != 'gonk'
 [test_camera_2.html]
+skip-if = toolkit != 'gonk'
 [test_camera_3.html]
+skip-if = toolkit != 'gonk'
 [test_camera_hardware_init_failure.html]
 [test_camera_hardware_failures.html]
 [test_bug975472.html]
 [test_camera_fake_parameters.html]
 [test_camera_hardware_face_detection.html]
 [test_camera_hardware_auto_focus_moving_cb.html]
 [test_bug1022766.html]
 [test_bug1037322.html]
 [test_bug1099390.html]
 [test_bug1104913.html]
+skip-if = toolkit != 'gonk'
 [test_camera_bad_initial_config.html]
--- a/dom/camera/test/test_bug1037322.html
+++ b/dom/camera/test/test_bug1037322.html
@@ -7,83 +7,63 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 320,
-    height: 240
-  }
-};
+var suite = new CameraTestSuite();
 
-function onError(e) {
-  ok(false, "Error: " + JSON.stringify(e));
-}
-
-var Camera = {
-  cameraObj: null,
+suite.test('bug-1037322', function() {
+  var cameraManager = navigator.mozCameras;
+  var whichCamera = cameraManager.getListOfCameras()[0];
 
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
+  var postConfig = {
+    mode: 'picture',
+    recorderProfile: 'low',
+    previewSize: {
+      width: 320,
+      height: 240
+    }
+  };
 
-  start: function test_start() {
-    function setConfig_onSuccess(cfg) {
-      // Check our specific configuration
-      ok(cfg.mode === config.mode, "Configured mode = " + cfg.mode);
-      ok(cfg.previewSize.width === config.previewSize.width &&
-         cfg.previewSize.height === config.previewSize.height,
-         "Configured preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
-      ok(cfg.recorderProfile === config.recorderProfile,
-         "Configured recorder profile = '" + cfg.recorderProfile + "'");
-
-      SimpleTest.finish();
-    }
+  function resolveGetCamera(p) {
+    suite.camera = p.camera;
 
-    function getCamera_onSuccess(d) {
-      var camera = d.camera;
-      var cfg = d.configuration;
-      Camera.cameraObj = camera;
-      Camera.viewfinder.mozSrcObject = camera;
-      Camera.viewfinder.play();
+    // Check the default configuration
+    var cfg = p.configuration;
+    ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
+    ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
+       "Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
+    ok(cfg.recorderProfile === "default",
+       "Initial recorder profile = '" + cfg.recorderProfile + "'");
+  }
 
-      // Check the default configuration
-      ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
-      ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
-         "Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
-      ok(cfg.recorderProfile === "default",
-         "Initial recorder profile = '" + cfg.recorderProfile + "'");
+  function configure(p) {
+    // Apply our specific configuration
+    return suite.camera.setConfiguration(postConfig);
+  }
 
-      // Apply our specific configuration
-      camera.setConfiguration(config).then(setConfig_onSuccess, onError);
-    }
-
-    var cfg = {
-      mode: 'unspecified',
-    };
-    navigator.mozCameras.getCamera(whichCamera, cfg).then(getCamera_onSuccess, onError);
+  function resolveConfigure(cfg) {
+    // Check our specific configuration
+    ok(cfg.mode === postConfig.mode, "Configured mode = " + cfg.mode);
+    ok(cfg.previewSize.width === postConfig.previewSize.width &&
+       cfg.previewSize.height === postConfig.previewSize.height,
+       "Configured preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
+    ok(cfg.recorderProfile === postConfig.recorderProfile,
+       "Configured recorder profile = '" + cfg.recorderProfile + "'");
   }
-}
 
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  if (Camera.cameraObj) {
-    Camera.cameraObj.release();
-    Camera.cameraObj = null;
-  }
+  return cameraManager.getCamera(whichCamera, {mode: 'unspecified'})
+    .then(resolveGetCamera, suite.rejectGetCamera)
+    .then(configure)
+    .then(resolveConfigure, suite.rejectConfigure);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_bug1099390.html
+++ b/dom/camera/test/test_bug1099390.html
@@ -7,102 +7,49 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 352,
-    height: 288
-  }
-};
-
-function onError(e) {
-  ok(false, "Error " + e);
-}
-
-var Camera = {
-  _cameraObj: null,
+var suite = new CameraTestSuite();
 
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
-
-  release: function release() {
-    viewfinder.mozSrcObject = null;
-    if (Camera._cameraObj) {
-      Camera._cameraObj.release();
-      Camera._cameraObj = null;
-    }
-  },
-
-  test: function test(cam) {
-    var gotCloseEvent = false;
-    var gotReleasePromise = false;
-
-    function gotAll() {
-      var all = gotCloseEvent && gotReleasePromise;
-      if (all) {
-        info("Got all expected notifications");
-      }
-      return all;
-    };
+suite.test('bug-1099390', function() {
+  function release(p) {
+    return new Promise(function(resolve, reject) {
+      var gotCloseEvent = false;
+      var gotReleasePromise = false;
 
-    var onClosed = function(e) {
-      cam.removeEventListener('close', onClosed);
-      ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
-      ok(e.reason === "HardwareReleased", "'close' event reason is: " + e.reason);
-      gotCloseEvent = true;
-      if (gotAll()) {
-        SimpleTest.finish();
-      }
-    };
+      var onClosed = function(e) {
+        suite.camera.removeEventListener('close', onClosed);
+        ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
+        ok(e.reason === "HardwareReleased", "'close' event reason is: " + e.reason);
+        gotCloseEvent = true;
+        if (gotReleasePromise) {
+          resolve();
+        }
+      };
 
-    cam.addEventListener('close', onClosed);
-
-    var onResolve = function() {
-      ok(!gotReleasePromise, "gotReleasePromise was " + gotReleasePromise);
-      gotReleasePromise = true;
-      if (gotAll()) {
-        SimpleTest.finish();
-      }
-    };
-
-    cam.release().then(onResolve, onError);
-  }, // test()
+      suite.camera.addEventListener('close', onClosed);
 
-  start: function start() {
-    function onSuccess(d) {
-      Camera._cameraObj = d.camera;
-      var cam = d.camera;
-
-      var onPreviewStateChange = function(e) {
-        if (e.newState === 'started') {
-          cam.removeEventListener('previewstatechange', onPreviewStateChange);
-          Camera.test(cam);
+      suite.camera.release().then(function(p) {
+        ok(true, "released camera");
+        gotReleasePromise = true;
+        if (gotCloseEvent) {
+          resolve();
         }
-      }; // onPreviewStateChange
-      cam.addEventListener('previewstatechange', onPreviewStateChange);
-    }; // onSuccess()
+      }).catch(reject);
+    });
+  }
 
-    navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
-  }, // start()
-}
-
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.release();
+  return suite.getCamera()
+    .then(release, suite.rejectGetCamera);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_bug975472.html
+++ b/dom/camera/test/test_bug975472.html
@@ -7,244 +7,177 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-const Cr = Components.results;
+var suite = new CameraTestSuite();
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 320,
-    height: 240
-  }
-};
-var options = {
-  rotation: 0,
-  position: {
-    latitude: 43.645687,
-    longitude: -79.393661
-  },
-  dateTime: Date.now()
-};
-
-function onError(e) {
-  ok(false, "Error " + e);
-}
-function next() {
-  Camera.nextTest();
+function cameraRelease(p) {
+  return suite.camera.release();
 }
 
-// The array of tests
-var tests = [
-  {
-    key: "release-after-release",
-    func: function testAutoFocus(camera) {
-      function onSuccess(success) {
-        ok(true, "release() succeeded");
-        next();
-      }
-      function onError(error) {
-        ok(false, "release() failed with: " + error);
-      }
-      camera.release().then(onSuccess, onError);
+suite.test('release-after-release', function() {
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(cameraRelease, suite.rejectRelease)
+    .catch(suite.rejectRelease);
+});
+
+suite.test('set-picture-size-after-release', function() {
+  function setPictureSize(p) {
+    try {
+      suite.camera.setPictureSize({ width: 0, height: 0 });
+      ok(false, "SetPictureSize() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "setPictureSize() failed with: " + e.name);
     }
-  },
-  {
-    key: "set-picture-size-after-release",
-    func: function testSetPictureSize(camera) {
-      try {
-        camera.setPictureSize({ width: 0, height: 0 });
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "setPictureSize() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "set-thumbnail-size-after-release",
-    func: function testSetThumbnailSize(camera) {
-      try {
-        camera.setThumbnailSize({ width: 0, height: 0 });
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "setThumbnailSize() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "get-sensor-angle-after-release",
-    func: function testGetSensorAngle(camera) {
-      ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
-      next();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(setPictureSize, suite.rejectRelease);
+});
+
+suite.test('set-thumbnail-size-after-release', function() {
+  function setThumbnailSize(p) {
+    try {
+      suite.camera.setThumbnailSize({ width: 0, height: 0 });
+      ok(false, "SetThumbnailSize() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "setThumbnailSize() failed with: " + e.name);
     }
-  },
-  {
-    key: "resume-preview-after-release",
-    func: function testResumePreview(camera) {
-      try {
-        camera.resumePreview();
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "resumePreview() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "auto-focus-after-release",
-    func: function testAutoFocus(camera) {
-      function onSuccess(success) {
-        ok(false, "autoFocus() succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "autoFocus() failed with: " + error.name);
-        next();
-      }
-      camera.autoFocus().then(onSuccess, onError);
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(setThumbnailSize, suite.rejectRelease);
+});
+
+suite.test('get-sensor-angle-after-release', function() {
+  function getSensorAngle(p) {
+    ok(suite.camera.sensorAngle == 0, "camera.sensorAngle = " + suite.camera.sensorAngle);
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(getSensorAngle, suite.rejectRelease);
+});
+
+suite.test('resume-preview-after-release', function() {
+  function resumePreview(p) {
+    try {
+      suite.camera.resumePreview();
+      ok(false, "resumePreview() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "resumePreview() failed with: " + e.name);
     }
-  },
-  {
-    key: "take-picture-after-release",
-    func: function testTakePicture(camera) {
-      function onSuccess(picture) {
-        ok(false, "takePicture() succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "takePicture() failed with: " + error.name);
-        next();
-      }
-      camera.takePicture(null).then(onSuccess, onError);
-    }
-  },
-  {
-    key: "start-recording-after-release",
-    func: function testStartRecording(camera) {
-      function onSuccess(picture) {
-        ok(false, "startRecording() process succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "startRecording() failed with: " + error.name);
-        next();
-      }
-      var recordingOptions = {
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(resumePreview, suite.rejectRelease);
+});
+
+suite.test('auto-focus-after-release', function() {
+  function autoFocus(p) {
+    return suite.camera.autoFocus();
+  }
+
+  function rejectAutoFocus(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "autoFocus() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(autoFocus, suite.rejectRelease)
+    .then(suite.expectedRejectAutoFocus, rejectAutoFocus);
+});
+
+suite.test('take-picture-after-release', function() {
+  function takePicture(p) {
+    return suite.camera.takePicture(null);
+  }
+
+  function rejectTakePicture(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(takePicture, suite.rejectRelease)
+    .then(suite.expectedRejectTakePicture, rejectTakePicture);
+});
+
+suite.test('start-recording-after-release', function() {
+  function startRecording(p) {
+    return suite.camera.startRecording(
+      {
         profile: 'high',
         rotation: 0
-      };
-      camera.startRecording(recordingOptions,
-                            navigator.getDeviceStorage('videos'),
-                            'bug975472.mp4').then(onSuccess, onError);
-    }
-  },
-  {
-    key: "stop-recording-after-release",
-    func: function testStopRecording(camera) {
-      try {
-        camera.stopRecording();
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "stopRecording() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "set-configuration-after-release",
-    func: function testSetConfiguration(camera) {
-      function onSuccess(picture) {
-        ok(false, "setConfiguration() process succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "setConfiguration() failed with: " + error.name);
-        next();
-      }
-      camera.setConfiguration(config).then(onSuccess, onError);
-    }
-  },
-];
-
-var testGenerator = function() {
-  for (var i = 0; i < tests.length; ++i ) {
-    yield tests[i];
+      },
+      navigator.getDeviceStorage('videos'),
+      'bug975472.mp4'
+    );
   }
-}();
 
-var Camera = {
-  cameraObj: null,
-  _otherPictureSize: null,
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
-  onCameraReady: function () {
-    Camera.nextTest = function() {
-      try {
-        var t = testGenerator.next();
-        info("test: " + t.key);
-        t.func(Camera.cameraObj);
-      } catch(e) {
-        if (e instanceof StopIteration) {
-          SimpleTest.finish();
-        } else {
-          throw e;
-        }
-      }
-    };
-    // Release the camera hardware, and call all of the asynchronous methods
-    // to make sure they properly handle being in this state.
-    Camera.cameraObj.release();
-    next();
-  },
-  release: function release() {
-    cameraObj = null;
-  },
-  start: function run_test() {
-    function onSuccess(d) {
-      var camera = d.camera;
-      Camera.cameraObj = camera;
-      var onPreviewStateChange = function(e) {
-        if (e.newState === 'started') {
-          info("viewfinder is ready and playing");
-          Camera.cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
-          Camera.onCameraReady();
-        }
-      };
-      camera.addEventListener('previewstatechange', onPreviewStateChange);
-      Camera.viewfinder.mozSrcObject = camera;
-      Camera.viewfinder.play();
-      ok(camera.capabilities.pictureSizes.length > 0,
-        "capabilities.pictureSizes.length = " +
-        camera.capabilities.pictureSizes.length);
-      Camera._otherPictureSize = camera.capabilities.pictureSizes.slice(-1)[0];
-      camera.setPictureSize(camera.capabilities.pictureSizes[0]);
-      options.pictureSize = Camera._otherPictureSize;
-      options.fileFormat = camera.capabilities.fileFormats[0];
-      info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
-    };
-    navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
+  function rejectStartRecording(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
   }
-}
 
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  Camera.cameraObj.release();
-  Camera.cameraObj = null;
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(startRecording, suite.rejectRelease)
+    .then(suite.expectedRejectStartRecording, rejectStartRecording);
 });
 
-Camera.start();
+suite.test('stop-recording-after-release', function() {
+  function stopRecording(p) {
+    try {
+      suite.camera.stopRecording();
+      ok(false, "stopRecording() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "stopRecording() failed with: " + e.name);
+    }
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(stopRecording, suite.rejectRelease);
+});
+
+suite.test('set-configuration-after-release', function() {
+  function configure(p) {
+    return suite.camera.setConfiguration(null);
+  }
+
+  function rejectConfigure(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(configure, suite.rejectRelease)
+    .then(suite.expectedRejectConfigure, rejectConfigure);
+});
+
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_camera_bad_initial_config.html
+++ b/dom/camera/test/test_camera_bad_initial_config.html
@@ -7,52 +7,38 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'foobar',
-};
+var suite = new CameraTestSuite();
 
-var Camera = {
-  cameraObj: null,
-
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
+suite.test('bad-initial-config', function() {
+  function getCamera() {
+    var whichCamera = navigator.mozCameras.getListOfCameras()[0];
+    var config = {
+      mode: 'picture',
+      recorderProfile: 'foobar',
+    };
 
-  start: function test_start() {
-    function getCamera_onSuccess(d) {
-      ok(false, "Get camera should have failed");
-      SimpleTest.finish();
-    }
-
-    function getCamera_onError(e) {
-      ok(true, "Get camera failed as expected: " + JSON.stringify(e));
-      SimpleTest.finish();
-    }
+    return navigator.mozCameras.getCamera(whichCamera, config);
+  }
 
-    navigator.mozCameras.getCamera(whichCamera, config).then(getCamera_onSuccess, getCamera_onError);
+  function rejectGetCamera(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "getCamera() failed with: " + error.name);
+    return Promise.resolve();
   }
-}
-
-SimpleTest.waitForExplicitFinish();
 
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  if (Camera.cameraObj) {
-    Camera.cameraObj.release();
-    Camera.cameraObj = null;
-  }
+  return getCamera()
+    .then(suite.expectedRejectGetCamera, rejectGetCamera);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -386,17 +386,17 @@ MediaDevice::MediaDevice(MediaEngineSour
   : mHasFacingMode(false)
   , mSource(aSource) {
   mSource->GetName(mName);
   mSource->GetUUID(mID);
 }
 
 VideoDevice::VideoDevice(MediaEngineVideoSource* aSource)
   : MediaDevice(aSource) {
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (mName.EqualsLiteral("back")) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::Environment;
   } else if (mName.EqualsLiteral("front")) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::User;
   }
 #endif // MOZ_B2G_CAMERA
@@ -1700,17 +1700,17 @@ MediaManager::GetUserMedia(
     if (isLoop &&
         (src == dom::MediaSourceEnum::Window ||
          src == dom::MediaSourceEnum::Application ||
          src == dom::MediaSourceEnum::Screen)) {
        privileged = false;
     }
   }
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
   // XXX No full support for picture in Desktop yet (needs proper UI)
   if (privileged ||
       (fake && !Preferences::GetBool("media.navigator.permission.fake"))) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -625,16 +625,16 @@ private:
   nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_MEDIAMANAGER_H
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -26,17 +26,17 @@ GetUserMediaLog()
 #include "nsITabSource.h"
 #include "MediaTrackConstraints.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #include "AndroidBridge.h"
 #endif
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
 #include "ICameraControl.h"
 #include "MediaEngineGonkVideoSource.h"
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
@@ -57,33 +57,35 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
 {
 #ifndef MOZ_B2G_CAMERA
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
   }
 #else
+#ifdef MOZ_WIDGET_GONK
   AsyncLatencyLogger::Get()->AddRef();
 #endif
+#endif
   // XXX
   gFarendObserver = new AudioOutputObserver();
 
   NS_NewNamedThread("AudioGUM", getter_AddRefs(mThread));
   MOZ_ASSERT(mThread);
 }
 
 void
 MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (aMediaSource != dom::MediaSourceEnum::Camera) {
     // only supports camera sources
     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
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -250,17 +250,17 @@ public:
 
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
     AsyncLatencyLogger::Get()->Release();
 #endif
     gFarendObserver = nullptr;
   }
 
   nsCOMPtr<nsIThread> mThread;
 
   Mutex mMutex;
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -31,17 +31,17 @@ if CONFIG['MOZ_WEBRTC']:
         '/dom/base',
         '/dom/camera',
         '/media/libyuv/include',
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/signaling/src/common/browser_logging',
         '/media/webrtc/trunk',
     ]
     # Gonk camera source.
-    if CONFIG['MOZ_B2G_CAMERA']:
+    if CONFIG['MOZ_B2G_CAMERA'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         EXPORTS += [
             'GonkCameraImage.h',
             'MediaEngineGonkVideoSource.h',
         ]
         UNIFIED_SOURCES += [
             'GonkCameraImage.cpp',
             'MediaEngineGonkVideoSource.cpp',
         ]
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -196,17 +196,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'winspool',
     ]
 
 if CONFIG['MOZ_B2G_BT_BLUEZ'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     OS_LIBS += [
         'dbus',
     ]
 
-if CONFIG['MOZ_B2G_CAMERA']:
+if CONFIG['MOZ_B2G_CAMERA'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     OS_LIBS += [
         'stagefright',
         'stagefright_omx',
     ]
 
 if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['OS_TARGET'] != 'Android':
     OS_LIBS += [
         'rt',