Bug 965425 - expose ISO setting, r=dhylands,jst
authorMike Habicher <mikeh@mozilla.com>
Fri, 07 Mar 2014 01:00:43 -0500
changeset 183432 26779ad44626f8566f970247758465a4fabded16
parent 183431 f9e1959b21c68003a4cdb373afe2d97664e6414f
child 183433 4fdc4bd13f0ddd1c1f89dbcb06654c9c32ac3963
push id5439
push userffxbld
push dateMon, 17 Mar 2014 23:08:15 +0000
treeherdermozilla-aurora@c0befb3c8038 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, jst
bugs965425
milestone30.0a1
Bug 965425 - expose ISO setting, r=dhylands,jst
dom/camera/DOMCameraCapabilities.cpp
dom/camera/DOMCameraCapabilities.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/camera/GonkCameraParameters.cpp
dom/camera/GonkCameraParameters.h
dom/camera/ICameraControl.h
dom/camera/test/test_camera.html
dom/camera/test/test_camera_fake_parameters.html
dom/webidl/CameraCapabilities.webidl
dom/webidl/CameraControl.webidl
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -129,16 +129,19 @@ CameraCapabilities::Populate(ICameraCont
   LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
 
   rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, mFlashModes);
   LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
 
   rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes);
   LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
 
+  rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, mIsoModes);
+  LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
+
   rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
   LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
 
   int32_t areas;
   rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
   LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
   mMaxFocusAreas = areas < 0 ? 0 : areas;
 
@@ -273,10 +276,16 @@ CameraCapabilities::ExposureCompensation
 }
 
 JS::Value
 CameraCapabilities::RecorderProfiles(JSContext* aCx) const
 {
   return mRecorderProfiles;
 }
 
+void
+CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval) const
+{
+  retval = mIsoModes;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/camera/DOMCameraCapabilities.h
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -54,32 +54,34 @@ public:
   void GetFocusModes(nsTArray<nsString>& aRetVal) const;
   void GetZoomRatios(nsTArray<double>& aRetVal) const;
   uint32_t MaxFocusAreas() const;
   uint32_t MaxMeteringAreas() const;
   double MinExposureCompensation() const;
   double MaxExposureCompensation() const;
   double ExposureCompensationStep() const;
   JS::Value RecorderProfiles(JSContext* cx) const;
+  void GetIsoModes(nsTArray<nsString>& aRetVal) const;
 
 protected:
   nsresult TranslateToDictionary(ICameraControl* aCameraControl,
                                  uint32_t aKey, nsTArray<CameraSize>& aSizes);
 
   nsTArray<CameraSize> mPreviewSizes;
   nsTArray<CameraSize> mPictureSizes;
   nsTArray<CameraSize> mThumbnailSizes;
   nsTArray<CameraSize> mVideoSizes;
 
   nsTArray<nsString> mFileFormats;
   nsTArray<nsString> mWhiteBalanceModes;
   nsTArray<nsString> mSceneModes;
   nsTArray<nsString> mEffects;
   nsTArray<nsString> mFlashModes;
   nsTArray<nsString> mFocusModes;
+  nsTArray<nsString> mIsoModes;
 
   nsTArray<double> mZoomRatios;
 
   uint32_t mMaxFocusAreas;
   uint32_t mMaxMeteringAreas;
 
   double mMinExposureCompensation;
   double mMaxExposureCompensation;
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -394,16 +394,29 @@ nsDOMCameraControl::GetFocusMode(nsStrin
 }
 void
 nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
 {
   MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
 }
 
+void
+nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv)
+{
+  MOZ_ASSERT(mCameraControl);
+  aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode);
+}
+void
+nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv)
+{
+  MOZ_ASSERT(mCameraControl);
+  aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode);
+}
+
 double
 nsDOMCameraControl::GetZoom(ErrorResult& aRv)
 {
   MOZ_ASSERT(mCameraControl);
 
   double zoom;
   aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom);
   return zoom;
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -74,16 +74,18 @@ public:
   double GetFocalLength(ErrorResult& aRv);
   double GetFocusDistanceNear(ErrorResult& aRv);
   double GetFocusDistanceOptimum(ErrorResult& aRv);
   double GetFocusDistanceFar(ErrorResult& aRv);
   void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
   double GetExposureCompensation(ErrorResult& aRv);
   int32_t SensorAngle();
   already_AddRefed<dom::CameraCapabilities> Capabilities();
+  void GetIsoMode(nsString& aMode, ErrorResult& aRv);
+  void SetIsoMode(const nsAString& aMode, ErrorResult& aRv);
 
   // Unsolicited event handlers.
   already_AddRefed<dom::CameraShutterCallback> GetOnShutter();
   void SetOnShutter(dom::CameraShutterCallback* aCb);
   already_AddRefed<dom::CameraClosedCallback> GetOnClosed();
   void SetOnClosed(dom::CameraClosedCallback* aCb);
   already_AddRefed<dom::CameraRecorderStateChange> GetOnRecorderStateChange();
   void SetOnRecorderStateChange(dom::CameraRecorderStateChange* aCb);
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -72,16 +72,20 @@ GonkCameraParameters::Parameters::GetTex
       // Not every platform defines a KEY_EXIF_DATETIME;
       // for those that don't, we use the raw string key, and if the platform
       // doesn't support it, it will be ignored.
       //
       // See bug 832494.
       return "exif-datetime";
     case CAMERA_PARAM_VIDEOSIZE:
       return KEY_VIDEO_SIZE;
+    case CAMERA_PARAM_ISOMODE:
+      // Not every platform defines KEY_ISO_MODE;
+      // for those that don't, we use the raw string key.
+      return "iso";
 
     case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
       return KEY_SUPPORTED_PREVIEW_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
       return KEY_SUPPORTED_PICTURE_SIZES;
     case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
       return KEY_SUPPORTED_VIDEO_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
@@ -107,16 +111,20 @@ GonkCameraParameters::Parameters::GetTex
     case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
       return KEY_EXPOSURE_COMPENSATION_STEP;
     case CAMERA_PARAM_SUPPORTED_ZOOM:
       return KEY_ZOOM_SUPPORTED;
     case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
       return KEY_ZOOM_RATIOS;
     case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
       return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
+    case CAMERA_PARAM_SUPPORTED_ISOMODES:
+      // Not every platform defines KEY_SUPPORTED_ISO_MODES;
+      // for those that don't, we use the raw string key.
+      return "iso-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"))
@@ -133,16 +141,53 @@ GonkCameraParameters::~GonkCameraParamet
 {
   MOZ_COUNT_DTOR(GonkCameraParameters);
   if (mLock) {
     PR_DestroyRWLock(mLock);
     mLock = nullptr;
   }
 }
 
+nsresult
+GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut)
+{
+  if (aIso.EqualsASCII("hjr")) {
+    aIsoOut = "ISO_HJR";
+  } else if (aIso.EqualsASCII("auto")) {
+    aIsoOut = "auto";
+  } else {
+    nsAutoCString v = NS_LossyConvertUTF16toASCII(aIso);
+    unsigned int iso;
+    if (sscanf(v.get(), "%u", &iso) != 1) {
+      return NS_ERROR_FAILURE;
+    }
+    aIsoOut = nsPrintfCString("ISO%u", iso);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+GonkCameraParameters::MapIsoFromGonk(const char* aIso, nsAString& aIsoOut)
+{
+  if (strcmp(aIso, "ISO_HJR") == 0) {
+    aIsoOut.AssignASCII("hjr");
+  } else if (strcmp(aIso, "auto") == 0) {
+    aIsoOut.AssignASCII("auto");
+  } else {
+    unsigned int iso;
+    if (sscanf(aIso, "ISO%u", &iso) != 1) {
+      return NS_ERROR_FAILURE;
+    }
+    aIsoOut.AppendInt(iso);
+  }
+
+  return NS_OK;
+}
+
 // 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(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin);
@@ -156,37 +201,63 @@ GonkCameraParameters::Initialize()
     mExposureCompensationStep = 0;
   }
   rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
   if (NS_FAILED(rv)) {
     // zoom is not supported
     mZoomRatios.Clear();
   }
 
+  // The return code from GetListAsArray() doesn't matter. If it fails,
+  // the isoModes array will be empty, and the subsequent loop won't
+  // execute.
+  nsTArray<nsCString> isoModes;
+  GetListAsArray(CAMERA_PARAM_SUPPORTED_ISOMODES, isoModes);
+  for (uint32_t i = 0; i < isoModes.Length(); ++i) {
+    nsString v;
+    rv = MapIsoFromGonk(isoModes[i].get(), v);
+    if (NS_SUCCEEDED(rv)) {
+      *mIsoModes.AppendElement() = v;
+    }
+  }
+
   mInitialized = true;
   return NS_OK;
 }
 
 // Handle nsAStrings
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
 {
+  if (aKey == CAMERA_PARAM_ISOMODE) {
+    nsAutoCString v;
+    nsresult rv = MapIsoToGonk(aValue, v);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    return SetImpl(aKey, v.get());
+  }
+
   return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
 {
   const char* val;
   nsresult rv = GetImpl(aKey, val);
   if (NS_FAILED(rv)) {
     return rv;
   }
-  aValue.AssignASCII(val);
-  return NS_OK;
+  if (aKey == CAMERA_PARAM_ISOMODE) {
+    rv = MapIsoFromGonk(val, aValue);
+  } else {
+    aValue.AssignASCII(val);
+  }
+  return rv;
 }
 
 // Handle ICameraControl::Sizes
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize)
 {
   if (aSize.width > INT_MAX || aSize.height > INT_MAX) {
     // AOSP can only handle signed ints.
@@ -351,42 +422,57 @@ GonkCameraParameters::SetTranslated(uint
   }
   return NS_OK;
 }
 
 // Handle int64_ts
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue)
 {
-  if (aKey == CAMERA_PARAM_PICTURE_DATETIME) {
-    // Add the non-GPS timestamp.  The EXIF date/time field is formatted as
-    // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
-    // is meant to be stored as a local time.  Since we are given seconds from
-    // Epoch GMT, we use localtime_r() to handle the conversion.
-    time_t time = aValue;
-    if (time != aValue) {
-      DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue);
-      return NS_ERROR_INVALID_ARG;
-    }
+  switch (aKey) {
+    case CAMERA_PARAM_PICTURE_DATETIME:
+      {
+        // Add the non-GPS timestamp.  The EXIF date/time field is formatted as
+        // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
+        // is meant to be stored as a local time.  Since we are given seconds from
+        // Epoch GMT, we use localtime_r() to handle the conversion.
+        time_t time = aValue;
+        if (time != aValue) {
+          DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue);
+          return NS_ERROR_INVALID_ARG;
+        }
+
+        struct tm t;
+        if (!localtime_r(&time, &t)) {
+          DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
+          return NS_ERROR_FAILURE;
+        }
 
-    struct tm t;
-    if (!localtime_r(&time, &t)) {
-      DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
-      return NS_ERROR_FAILURE;
-    }
+        char dateTime[20];
+        if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
+          DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
+          return NS_ERROR_FAILURE;
+        }
+
+        DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
+
+        return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime);
+      }
 
-    char dateTime[20];
-    if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
-      DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
-      return NS_ERROR_FAILURE;
-    }
+    case CAMERA_PARAM_ISOMODE:
+      {
+        if (aValue > INT32_MAX) {
+          DOM_CAMERA_LOGW("Can't set ISO mode = %lld, too big\n", aValue);
+          return NS_ERROR_INVALID_ARG;
+        }
 
-    DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
-
-    return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime);
+        nsString s;
+        s.AppendInt(aValue);
+        return SetTranslated(CAMERA_PARAM_ISOMODE, s);
+      }
   }
 
   // You can't actually pass 64-bit parameters to Gonk. :(
   int32_t v = static_cast<int32_t>(aValue);
   if (static_cast<int64_t>(v) != aValue) {
     return NS_ERROR_INVALID_ARG;;
   }
   return SetImpl(aKey, v);
@@ -604,16 +690,27 @@ ParseItem(const char* aStart, const char
     aItem->AssignASCII(aStart, aEnd - aStart);
   } else {
     aItem->AssignASCII(aStart);
   }
   return NS_OK;
 }
 
 nsresult
+ParseItem(const char* aStart, const char* aEnd, nsACString* aItem)
+{
+  if (aEnd) {
+    aItem->AssignASCII(aStart, aEnd - aStart);
+  } else {
+    aItem->AssignASCII(aStart);
+  }
+  return NS_OK;
+}
+
+nsresult
 ParseItem(const char* aStart, const char* aEnd, double* aItem)
 {
   if (sscanf(aStart, "%lf", aItem) == 1) {
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
@@ -672,25 +769,30 @@ GonkCameraParameters::GetListAsArray(uin
   }
 
   return NS_OK;
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
 {
+  if (aKey == CAMERA_PARAM_SUPPORTED_ISOMODES) {
+    aValues = mIsoModes;
+    return NS_OK;
+  }
+
   return GetListAsArray(aKey, aValues);
 }
 
 nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<double>& aValues)
 {
   if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) {
     aValues.Clear();
-    for (int i = 0; i < mZoomRatios.Length(); ++i) {
+    for (uint32_t i = 0; i < mZoomRatios.Length(); ++i) {
       *aValues.AppendElement() = mZoomRatios[i] / 100.0;
     }
     return NS_OK;
   }
 
   return GetListAsArray(aKey, aValues);
 }
 
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -1,10 +1,10 @@
 /*
- * Copyright (C) 2013 Mozilla Foundation
+ * Copyright (C) 2013-2014 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
@@ -90,16 +90,17 @@ protected:
   PRRWLock* mLock;
   bool mDirty;
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationMin;
   double mExposureCompensationStep;
   nsTArray<int> mZoomRatios;
+  nsTArray<nsString> mIsoModes;
 
   // This subclass of android::CameraParameters just gives
   // all of the AOSP getters and setters the same signature.
   class Parameters : public android::CameraParameters
   {
   public:
     using android::CameraParameters::set;
 
@@ -171,15 +172,17 @@ protected:
   nsresult SetTranslated(uint32_t aKey, const int& aValue);
   nsresult GetTranslated(uint32_t aKey, int& aValue);
   nsresult SetTranslated(uint32_t aKey, const uint32_t& aValue);
   nsresult GetTranslated(uint32_t aKey, uint32_t& aValue);
   nsresult GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues);
   nsresult GetTranslated(uint32_t aKey, nsTArray<double>& aValues);
 
   template<class T> nsresult GetListAsArray(uint32_t aKey, nsTArray<T>& aArray);
+  nsresult MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut);
+  nsresult MapIsoFromGonk(const char* aIso, nsAString& aIsoOut);
 
   nsresult Initialize();
 };
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -45,16 +45,17 @@ enum {
   CAMERA_PARAM_FOCUSDISTANCENEAR,
   CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
   CAMERA_PARAM_FOCUSDISTANCEFAR,
   CAMERA_PARAM_EXPOSURECOMPENSATION,
   CAMERA_PARAM_PICTURESIZE,
   CAMERA_PARAM_THUMBNAILSIZE,
   CAMERA_PARAM_THUMBNAILQUALITY,
   CAMERA_PARAM_SENSORANGLE,
+  CAMERA_PARAM_ISOMODE,
 
   // supported features
   CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
   CAMERA_PARAM_SUPPORTED_PICTURESIZES,
   CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
   CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
   CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
   CAMERA_PARAM_SUPPORTED_SCENEMODES,
@@ -63,17 +64,18 @@ enum {
   CAMERA_PARAM_SUPPORTED_FOCUSMODES,
   CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
   CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
   CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
   CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
   CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
   CAMERA_PARAM_SUPPORTED_ZOOM,
   CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
-  CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
+  CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES,
+  CAMERA_PARAM_SUPPORTED_ISOMODES
 };
 
 class ICameraControl
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
 
   static nsresult GetNumberOfCameras(int32_t& aDeviceCount);
--- a/dom/camera/test/test_camera.html
+++ b/dom/camera/test/test_camera.html
@@ -29,17 +29,17 @@ var config = {
 };
 
 function onError(e) {
   ok(false, "Error" + JSON.stringify(e));
 }
 
 var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
                      'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
-                     'recorderProfiles', 'zoomRatios'];
+                     'recorderProfiles', 'zoomRatios', 'isoModes'];
 
 var Camera = {
   cameraObj: null,
   _recording: false,
   _currentTest: null,
   _autoFocusSupported: 0,
   _manuallyFocused: false,
   _flashmodes: null,
@@ -146,18 +146,20 @@ var Camera = {
     Camera.getPictureSizes();
     Camera.getPreviewSizes();
     Camera.getFileFormats();
     Camera.getFocusModes();
     Camera.getZoomRatios();
     ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
     ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
     ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
-    info("zoom ratios length = " + Camera._zoomRatios.length);
+    ok(camcap.isoModes.length == 0, "ISO modes length = " + camcap.isoModes.length);
 
+    // The emulator doesn't support zoom, so these parameters will be very constrained
+    ok(Camera._zoomRatios.length == 1, "zoom ratios length = " + Camera._zoomRatios.length);
     if (Camera._zoomRatios.length > 0) {
       Camera._zoomRatios.forEach(function(element, index) {
         info("zoom[" + index + "] = " + element + "x");
         Camera.setZoom(element);
         ok(Camera.getZoom() === element, "zoom[" + index + "] = " + element + "x");
       });
 
       var zoom = Camera._zoomRatios[0] - 0.1;
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -22,30 +22,40 @@ var initialConfig = {
     width: 352,
     height: 288
   }
 };
 
 var cameraObj = null;
 
 // Shorthand functions
+function onError(e) {
+  ok(false, "Error" + JSON.stringify(e));
+}
+
 function end() {
   CameraTest.end();
 }
 function next() {
-  CameraTest.next();
+  if (cameraObj) {
+    cameraObj.release(
+      function success() {
+        CameraTest.next();
+      },
+      onError
+    );
+    cameraObj = null;
+  } else {
+    CameraTest.next();
+  }
 }
 function run() {
   CameraTest.run();
 }
 
-function onError(e) {
-  ok(false, "Error" + JSON.stringify(e));
-}
-
 // The array of tests
 var tests = [
   {
     key: "fake-zoom",
     prep: function setupFakeZoom(test) {
       test.setFakeParameters("zoom-ratios=100,150,200,300,400;max-zoom=4", function() {
         run();
       });
@@ -81,28 +91,58 @@ var tests = [
         ok(cam.zoom === cap.zoomRatios[0],
           zoom + "x zoom rounded down to: " + cap.zoomRatios[0] +
           "x, cam.zoom = " + cam.zoom + "x");
       }
 
       next();
     }
   },
+  {
+    key: "fake-iso",
+    prep: function setupFakeIso(test) {
+      // we should recognize 'auto', 'hjr', and numeric modes; anything else
+      // from the driver is ignored, which this test also verifies.
+      test.setFakeParameters(
+        "iso=auto;iso-values=auto,ISO_HJR,ISO100,foo,ISObar,ISO200,ISO400,ISO800,ISO1600",
+        function () {
+        run();
+      });
+    },
+    test: function testFakeIso(cam, cap) {
+      // value 'foo' should be missing
+      ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
+      ok(cap.isoModes.indexOf("foo") == -1, "Unknown ISO mode 'foo' is ignored");
+      ok(cap.isoModes.indexOf("ISObar") == -1, "Unknown ISO mode 'ISObar' is ignored");
+      ok(cap.isoModes.indexOf("bar") == -1, "Unknown ISO mode 'bar' is ignored");
+
+      // test individual ISO modes
+      cap.isoModes.forEach(function(iso, index) {
+        cam.iso = iso;
+        ok(cam.iso === iso,
+          "ISO[" + index + "] = " + iso + ", cam.iso = " + cam.iso);
+      });
+
+      next();
+    }
+  },
 ];
 
 var testGenerator = function() {
   for (var i = 0; i < tests.length; ++i ) {
     yield tests[i];
   }
 }();
 
 window.addEventListener('beforeunload', function() {
   document.getElementById('viewfinder').mozSrcObject = null;
-  cameraObj.release();
-  cameraObj = null;
+  if (cameraObj) {
+    cameraObj.release();
+    cameraObj = null;
+  }
 });
 
 CameraTest.begin("hardware", function(test) {
   function onError(error) {
     ok(false, "getCamera() failed with: " + error);
     end();
   }
 
--- a/dom/webidl/CameraCapabilities.webidl
+++ b/dom/webidl/CameraCapabilities.webidl
@@ -25,9 +25,11 @@ interface CameraCapabilities
   [Constant, Cached] readonly attribute unsigned long maxFocusAreas;
   [Constant, Cached] readonly attribute unsigned long maxMeteringAreas;
 
   [Constant, Cached] readonly attribute double minExposureCompensation;
   [Constant, Cached] readonly attribute double maxExposureCompensation;
   [Constant, Cached] readonly attribute double exposureCompensationStep;
 
   [Constant, Cached] readonly attribute any recorderProfiles;
+
+  [Constant, Cached] readonly attribute sequence<DOMString> isoModes;
 };
--- a/dom/webidl/CameraControl.webidl
+++ b/dom/webidl/CameraControl.webidl
@@ -226,16 +226,21 @@ interface CameraControl : MediaStream
      acceptable values must range from minExposureCompensation
      to maxExposureCompensation in steps of stepExposureCompensation;
      invalid values will be rounded to the nearest valid value. */
   [Throws]
   void setExposureCompensation(optional double compensation);
   [Throws]
   readonly attribute unrestricted double exposureCompensation;
 
+  /* one of the values chosen from capabilities.isoModes; default
+     value is "auto" if supported. */
+  [Throws]
+  attribute DOMString       isoMode;
+
   /* the function to call on the camera's shutter event, to trigger
      a shutter sound and/or a visual shutter indicator. */
   attribute CameraShutterCallback? onShutter;
 
   /* the function to call when the camera hardware is closed
      by the underlying framework, e.g. when another app makes a more
      recent call to get the camera. */
   attribute CameraClosedCallback? onClosed;