Bug 987954 - Change camera control size/region attributes to methods to remove direct JS_*() calls, r=bz,mikeh
☠☠ backed out by 33a615530ab1 ☠ ☠
authorAndrew Osmond <aosmond@gmail.com>
Fri, 25 Apr 2014 16:28:17 -0400
changeset 180833 27b41f71ccd4475f9fac798e9604b4b5271d5ce8
parent 180832 a73dece39b0129e53040f29eed4f377ddf387dee
child 180834 d353282fc0a6f001e30003c136ede5525dd03f87
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersbz, mikeh
bugs987954
milestone31.0a1
Bug 987954 - Change camera control size/region attributes to methods to remove direct JS_*() calls, r=bz,mikeh
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/camera/test/test_bug975472.html
dom/camera/test/test_camera_fake_parameters.html
dom/webidl/CameraControl.webidl
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -219,134 +219,94 @@ nsDOMCameraControl::WrapObject(JSContext
 }
 
 bool
 nsDOMCameraControl::IsWindowStillActive()
 {
   return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
 }
 
-// JS-to-native helpers
 // Setter for weighted regions: { top, bottom, left, right, weight }
 nsresult
-nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
+nsDOMCameraControl::Set(uint32_t aKey, const Optional<Sequence<CameraRegion> >& aValue, uint32_t aLimit)
 {
   if (aLimit == 0) {
     DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
     return NS_OK;
   }
 
-  if (!aValue.isObject()) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  uint32_t length = 0;
-
-  JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
-  if (!JS_GetArrayLength(aCx, regions, &length)) {
-    return NS_ERROR_FAILURE;
-  }
+  nsTArray<ICameraControl::Region> regionArray;
+  if (aValue.WasPassed()) {
+    const Sequence<CameraRegion>& regions = aValue.Value();
+    uint32_t length = regions.Length();
 
-  DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
-  if (length > aLimit) {
-    length = aLimit;
-  }
-
-  nsTArray<ICameraControl::Region> regionArray;
-  regionArray.SetCapacity(length);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    JS::Rooted<JS::Value> v(aCx);
-
-    if (!JS_GetElement(aCx, regions, i, &v)) {
-      return NS_ERROR_FAILURE;
+    DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
+    if (length > aLimit) {
+      length = aLimit;
     }
 
-    CameraRegion region;
-    if (!region.Init(aCx, v)) {
-      return NS_ERROR_FAILURE;
-    }
+    // aLimit supplied by camera library provides sane ceiling (i.e. <10)
+    regionArray.SetCapacity(length);
+
+    for (uint32_t i = 0; i < length; ++i) {
+      ICameraControl::Region* r = regionArray.AppendElement();
+      const CameraRegion &region = regions[i];
+      r->top = region.mTop;
+      r->left = region.mLeft;
+      r->bottom = region.mBottom;
+      r->right = region.mRight;
+      r->weight = region.mWeight;
 
-    ICameraControl::Region* r = regionArray.AppendElement();
-    r->top = region.mTop;
-    r->left = region.mLeft;
-    r->bottom = region.mBottom;
-    r->right = region.mRight;
-    r->weight = region.mWeight;
-
-    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
-      i,
-      r->top,
-      r->left,
-      r->bottom,
-      r->right,
-      r->weight
-    );
+      DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
+        i,
+        r->top,
+        r->left,
+        r->bottom,
+        r->right,
+        r->weight
+      );
+    }
+  } else {
+    DOM_CAMERA_LOGI("%s:%d : clear regions\n", __func__, __LINE__);
   }
   return mCameraControl->Set(aKey, regionArray);
 }
 
 // Getter for weighted regions: { top, bottom, left, right, weight }
 nsresult
-nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
+nsDOMCameraControl::Get(uint32_t aKey, nsTArray<CameraRegion>& aValue)
 {
   nsTArray<ICameraControl::Region> regionArray;
 
   nsresult rv = mCameraControl->Get(aKey, regionArray);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
-  if (!array) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
   uint32_t length = regionArray.Length();
   DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
+  aValue.SetLength(length);
 
   for (uint32_t i = 0; i < length; ++i) {
-    ICameraControl::Region* r = &regionArray[i];
-    JS::Rooted<JS::Value> v(aCx);
-
-    JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
-    if (!o) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    ICameraControl::Region& r = regionArray[i];
+    CameraRegion& v = aValue[i];
+    v.mTop = r.top;
+    v.mLeft = r.left;
+    v.mBottom = r.bottom;
+    v.mRight = r.right;
+    v.mWeight = r.weight;
 
-    DOM_CAMERA_LOGI("top=%d\n", r->top);
-    v = INT_TO_JSVAL(r->top);
-    if (!JS_SetProperty(aCx, o, "top", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("left=%d\n", r->left);
-    v = INT_TO_JSVAL(r->left);
-    if (!JS_SetProperty(aCx, o, "left", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
-    v = INT_TO_JSVAL(r->bottom);
-    if (!JS_SetProperty(aCx, o, "bottom", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("right=%d\n", r->right);
-    v = INT_TO_JSVAL(r->right);
-    if (!JS_SetProperty(aCx, o, "right", v)) {
-      return NS_ERROR_FAILURE;
-    }
-    DOM_CAMERA_LOGI("weight=%d\n", r->weight);
-    v = INT_TO_JSVAL(r->weight);
-    if (!JS_SetProperty(aCx, o, "weight", v)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (!JS_SetElement(aCx, array, i, o)) {
-      return NS_ERROR_FAILURE;
-    }
+    DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
+      i,
+      v.mTop,
+      v.mLeft,
+      v.mBottom,
+      v.mRight,
+      v.mWeight
+    );
   }
 
-  *aValue = JS::ObjectValue(*array);
   return NS_OK;
 }
 
 void
 nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv)
 {
   MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
@@ -435,43 +395,37 @@ nsDOMCameraControl::GetZoom(ErrorResult&
 
 void
 nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv)
 {
   MOZ_ASSERT(mCameraControl);
   aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
 }
 
-/* attribute jsval meteringAreas; */
-JS::Value
-nsDOMCameraControl::GetMeteringAreas(JSContext* cx, ErrorResult& aRv)
+void
+nsDOMCameraControl::GetMeteringAreas(nsTArray<CameraRegion>& aAreas, ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> areas(cx);
-  aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, areas.address());
-  return areas;
+  aRv = Get(CAMERA_PARAM_METERINGAREAS, aAreas);
+}
+void
+nsDOMCameraControl::SetMeteringAreas(const Optional<Sequence<CameraRegion> >& aMeteringAreas, ErrorResult& aRv)
+{
+  aRv = Set(CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
+            mCurrentConfiguration->mMaxMeteringAreas);
 }
 
 void
-nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle<JS::Value> aMeteringAreas, ErrorResult& aRv)
+nsDOMCameraControl::GetFocusAreas(nsTArray<CameraRegion>& aAreas, ErrorResult& aRv)
 {
-  aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
-            mCurrentConfiguration->mMaxMeteringAreas);
-}
-
-JS::Value
-nsDOMCameraControl::GetFocusAreas(JSContext* cx, ErrorResult& aRv)
-{
-  JS::Rooted<JS::Value> value(cx);
-  aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, value.address());
-  return value;
+  aRv = Get(CAMERA_PARAM_FOCUSAREAS, aAreas);
 }
 void
-nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle<JS::Value> aFocusAreas, ErrorResult& aRv)
+nsDOMCameraControl::SetFocusAreas(const Optional<Sequence<CameraRegion> >& aFocusAreas, ErrorResult& aRv)
 {
-  aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
+  aRv = Set(CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
             mCurrentConfiguration->mMaxFocusAreas);
 }
 
 static nsresult
 GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize)
 {
   JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
   if (!o) {
@@ -488,17 +442,17 @@ GetSize(JSContext* aCx, JS::Value* aValu
   if (!JS_SetProperty(aCx, o, "height", v)) {
     return NS_ERROR_FAILURE;
   }
 
   *aValue = JS::ObjectValue(*o);
   return NS_OK;
 }
 
-/* attribute any pictureSize */
+/* attribute any pictureSize, deprecated */
 JS::Value
 nsDOMCameraControl::GetPictureSize(JSContext* cx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> value(cx);
 
   ICameraControl::Size size;
   aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size);
   if (aRv.Failed()) {
@@ -516,17 +470,36 @@ nsDOMCameraControl::SetPictureSize(JSCon
     aRv = NS_ERROR_FAILURE;
     return;
   }
 
   ICameraControl::Size s = { size.mWidth, size.mHeight };
   aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
 }
 
-/* attribute any thumbnailSize */
+void
+nsDOMCameraControl::GetPictureSize(CameraSize& aSize, ErrorResult& aRv)
+{
+  ICameraControl::Size size;
+  aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  aSize.mWidth = size.width;
+  aSize.mHeight = size.height;
+}
+void
+nsDOMCameraControl::SetPictureSize(const CameraSize& aSize, ErrorResult& aRv)
+{
+  ICameraControl::Size s = { aSize.mWidth, aSize.mHeight };
+  aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
+}
+
+/* attribute any thumbnailSize, deprecated */
 JS::Value
 nsDOMCameraControl::GetThumbnailSize(JSContext* aCx, ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> value(aCx);
 
   ICameraControl::Size size;
   aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
   if (aRv.Failed()) {
@@ -544,16 +517,35 @@ nsDOMCameraControl::SetThumbnailSize(JSC
     aRv = NS_ERROR_FAILURE;
     return;
   }
 
   ICameraControl::Size s = { size.mWidth, size.mHeight };
   aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
 }
 
+void
+nsDOMCameraControl::GetThumbnailSize(CameraSize& aSize, ErrorResult& aRv)
+{
+  ICameraControl::Size size;
+  aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  aSize.mWidth = size.width;
+  aSize.mHeight = size.height;
+}
+void
+nsDOMCameraControl::SetThumbnailSize(const CameraSize& aSize, ErrorResult& aRv)
+{
+  ICameraControl::Size s = { aSize.mWidth, aSize.mHeight };
+  aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
+}
+
 double
 nsDOMCameraControl::GetFocalLength(ErrorResult& aRv)
 {
   MOZ_ASSERT(mCameraControl);
 
   double focalLength;
   aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength);
   return focalLength;
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -23,16 +23,18 @@ class nsPIDOMWindow;
 class nsIDOMBlob;
 
 namespace mozilla {
 
 namespace dom {
   class CameraCapabilities;
   class CameraPictureOptions;
   class CameraStartRecordingOptions;
+  class CameraRegion;
+  class CameraSize;
   template<typename T> class Optional;
 }
 class ErrorResult;
 class StartRecordingHelper;
 
 // Main camera control.
 class nsDOMCameraControl MOZ_FINAL : public DOMMediaStream
 {
@@ -65,20 +67,16 @@ public:
   void GetSceneMode(nsString& aMode, ErrorResult& aRv);
   void SetSceneMode(const nsAString& aMode, ErrorResult& aRv);
   void GetFlashMode(nsString& aMode, ErrorResult& aRv);
   void SetFlashMode(const nsAString& aMode, ErrorResult& aRv);
   void GetFocusMode(nsString& aMode, ErrorResult& aRv);
   void SetFocusMode(const nsAString& aMode, ErrorResult& aRv);
   double GetZoom(ErrorResult& aRv);
   void SetZoom(double aZoom, ErrorResult& aRv);
-  JS::Value GetMeteringAreas(JSContext* aCx, ErrorResult& aRv);
-  void SetMeteringAreas(JSContext* aCx, JS::Handle<JS::Value> aAreas, ErrorResult& aRv);
-  JS::Value GetFocusAreas(JSContext* aCx, ErrorResult& aRv);
-  void SetFocusAreas(JSContext* aCx, JS::Handle<JS::Value> aAreas, ErrorResult& aRv);
   JS::Value GetPictureSize(JSContext* aCx, ErrorResult& aRv);
   void SetPictureSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv);
   JS::Value GetThumbnailSize(JSContext* aCx, ErrorResult& aRv);
   void SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv);
   double GetFocalLength(ErrorResult& aRv);
   double GetFocusDistanceNear(ErrorResult& aRv);
   double GetFocusDistanceOptimum(ErrorResult& aRv);
   double GetFocusDistanceFar(ErrorResult& aRv);
@@ -103,16 +101,24 @@ public:
   dom::CameraFaceDetectionCallback* GetOnFacesDetected();
   void SetOnFacesDetected(dom::CameraFaceDetectionCallback* aCb);
 
   // Methods.
   void SetConfiguration(const dom::CameraConfiguration& aConfiguration,
                         const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
                         const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
                         ErrorResult& aRv);
+  void GetMeteringAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
+  void SetMeteringAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
+  void GetFocusAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
+  void SetFocusAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
+  void GetPictureSize(dom::CameraSize& aSize, ErrorResult& aRv);
+  void SetPictureSize(const dom::CameraSize& aSize, ErrorResult& aRv);
+  void GetThumbnailSize(dom::CameraSize& aSize, ErrorResult& aRv);
+  void SetThumbnailSize(const dom::CameraSize& aSize, ErrorResult& aRv);
   void AutoFocus(dom::CameraAutoFocusCallback& aOnSuccess,
                  const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
                  ErrorResult& aRv);
   void StartFaceDetection(ErrorResult& aRv);
   void StopFaceDetection(ErrorResult& aRv);
   void TakePicture(const dom::CameraPictureOptions& aOptions,
                    dom::CameraTakePictureCallback& aOnSuccess,
                    const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
@@ -173,18 +179,18 @@ protected:
 
   nsresult NotifyRecordingStatusChange(const nsString& aMsg);
 
   nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control
 
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
-  nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
-  nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
+  nsresult Set(uint32_t aKey, const dom::Optional<dom::Sequence<dom::CameraRegion> >& aValue, uint32_t aLimit);
+  nsresult Get(uint32_t aKey, nsTArray<dom::CameraRegion>& aValue);
 
   nsRefPtr<DOMCameraConfiguration>              mCurrentConfiguration;
   nsRefPtr<dom::CameraCapabilities>             mCapabilities;
 
   // solicited camera control event handlers
   nsRefPtr<dom::GetCameraCallback>              mGetCameraOnSuccessCb;
   nsRefPtr<dom::CameraErrorCallback>            mGetCameraOnErrorCb;
   nsRefPtr<dom::CameraAutoFocusCallback>        mAutoFocusOnSuccessCb;
--- a/dom/camera/test/test_bug975472.html
+++ b/dom/camera/test/test_bug975472.html
@@ -50,24 +50,24 @@ var tests = [
         ok(false, "release() failed with: " + error);
       }
       camera.release(onSuccess, onError);
     }
   },
   {
     key: "set-picture-size-after-release",
     func: function testSetPictureSize(camera) {
-      camera.pictureSize = { width: 0, height: 0 };
+      camera.setPictureSize({ width: 0, height: 0 });
       next();
     }
   },
   {
     key: "set-thumbnail-size-after-release",
     func: function testSetThumbnailSize(camera) {
-      camera.thumbnailSize = { width: 0, height: 0 };
+      camera.setThumbnailSize({ width: 0, height: 0 });
       next();
     }
   },
   {
     key: "get-sensor-angle-after-release",
     func: function testGetSensorAngle(camera) {
       ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
       next();
@@ -186,17 +186,17 @@ var Camera = {
     function onSuccess(camera, config) {
       Camera.cameraObj = camera;
       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.pictureSize = camera.capabilities.pictureSizes[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());
       Camera.cameraObj.onPreviewStateChange = function(state) {
         if (state === 'started') {
           info("viewfinder is ready and playing");
           Camera.cameraObj.onPreviewStateChange = null;
           Camera.onCameraReady();
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -137,16 +137,84 @@ var tests = [
         cam.iso = iso;
         ok(cam.iso === iso,
           "ISO[" + index + "] = " + iso + ", cam.iso = " + cam.iso);
       });
 
       next();
     }
   },
+  {
+    key: "fake-metering-areas",
+    prep: function setupFakeMeteringAreas(test) {
+      test.setFakeParameters("max-num-metering-areas=1", function () {
+        run();
+      });
+    },
+    test: function testFakeMeteringAreas(cam, cap) {
+      ok(cap.maxMeteringAreas == 1, "maxMeteringAreas = " + cap.maxMeteringAreas);
+      cam.setMeteringAreas([
+        {top: -500, bottom: 500, left: -500, right: 500, weight: 100}
+      ]);
+      areas = cam.getMeteringAreas();
+      ok(areas.length == 1, "areas length = " + areas.length);
+      ok(areas[0].top == -500, "area[0] top = " + areas[0].top);
+      ok(areas[0].bottom == 500, "area[0] bottom = " + areas[0].bottom);
+      ok(areas[0].left == -500, "area[0] left = " + areas[0].left);
+      ok(areas[0].right == 500, "area[0] right = " + areas[0].right);
+      ok(areas[0].weight == 100, "area[0] weight = " + areas[0].weight);
+      cam.setMeteringAreas([
+        {top: -501, bottom: 502, left: -503, right: 504, weight: 105},
+        {top: -500, bottom: 500, left: -500, right: 500, weight: 100}
+      ]);
+      areas = cam.getMeteringAreas();
+      ok(areas.length == 1, "areas length = " + areas.length);
+      ok(areas[0].top == -501, "area[0] top = " + areas[0].top);
+      ok(areas[0].bottom == 502, "area[0] bottom = " + areas[0].bottom);
+      ok(areas[0].left == -503, "area[0] left = " + areas[0].left);
+      ok(areas[0].right == 504, "area[0] right = " + areas[0].right);
+      ok(areas[0].weight == 105, "area[0] weight = " + areas[0].weight);
+
+      next();
+    },
+  },
+  {
+    key: "fake-focus-areas",
+    prep: function setupFakeFocusAreas(test) {
+      test.setFakeParameters("max-num-focus-areas=1", function () {
+        run();
+      });
+    },
+    test: function testFakeFocusAreas(cam, cap) {
+      ok(cap.maxFocusAreas == 1, "maxFocusAreas = " + cap.maxFocusAreas);
+      cam.setFocusAreas([
+        {top: -500, bottom: 500, left: -500, right: 500, weight: 100}
+      ]);
+      areas = cam.getFocusAreas();
+      ok(areas.length == 1, "areas length = " + areas.length);
+      ok(areas[0].top == -500, "area[0] top = " + areas[0].top);
+      ok(areas[0].bottom == 500, "area[0] bottom = " + areas[0].bottom);
+      ok(areas[0].left == -500, "area[0] left = " + areas[0].left);
+      ok(areas[0].right == 500, "area[0] right = " + areas[0].right);
+      ok(areas[0].weight == 100, "area[0] weight = " + areas[0].weight);
+      cam.setFocusAreas([
+        {top: -501, bottom: 502, left: -503, right: 504, weight: 105},
+        {top: -500, bottom: 500, left: -500, right: 500, weight: 100}
+      ]);
+      areas = cam.getFocusAreas();
+      ok(areas.length == 1, "areas length = " + areas.length);
+      ok(areas[0].top == -501, "area[0] top = " + areas[0].top);
+      ok(areas[0].bottom == 502, "area[0] bottom = " + areas[0].bottom);
+      ok(areas[0].left == -503, "area[0] left = " + areas[0].left);
+      ok(areas[0].right == 504, "area[0] right = " + areas[0].right);
+      ok(areas[0].weight == 105, "area[0] weight = " + areas[0].weight);
+
+      next();
+    },
+  },
 ];
 
 var testGenerator = function() {
   for (var i = 0; i < tests.length; ++i ) {
     yield tests[i];
   }
 }();
 
--- a/dom/webidl/CameraControl.webidl
+++ b/dom/webidl/CameraControl.webidl
@@ -165,39 +165,43 @@ interface CameraControl : MediaStream
       {
           top: -1000,
           left: -1000,
           bottom: 1000,
           right: 1000,
           weight: 1000
       }
 
-      'top', 'left', 'bottom', and 'right' all range from -1000 at
-      the top-/leftmost of the sensor to 1000 at the bottom-/rightmost
-      of the sensor.
+     'top', 'left', 'bottom', and 'right' all range from -1000 at
+     the top-/leftmost of the sensor to 1000 at the bottom-/rightmost
+     of the sensor.
 
-      objects missing one or more of these properties will be ignored;
-      if the array contains more than capabilities.maxMeteringAreas,
-      extra areas will be ignored.
+     objects missing one or more of these properties will be ignored;
+     if the array contains more than capabilities.maxMeteringAreas,
+     extra areas will be ignored.
 
-      this attribute can be set to null to allow the camera to determine
-      where to perform light metering. */
+     if this setter is called with no arguments, the camera will
+     determine metering areas on its own. */
   [Throws]
-  attribute any             meteringAreas;
+  sequence<CameraRegion> getMeteringAreas();
+  [Throws]
+  void setMeteringAreas(optional sequence<CameraRegion> meteringAreas);
 
   /* an array of one or more objects that define where the camera will
      perform auto-focusing, with the same definition as meteringAreas.
 
      if the array contains more than capabilities.maxFocusAreas, extra
      areas will be ignored.
 
-     this attribute can be set to null to allow the camera to determine
-     where to focus. */
+     if this setter is called with no arguments, the camera will
+     determine focus areas on its own. */
   [Throws]
-  attribute any             focusAreas;
+  sequence<CameraRegion> getFocusAreas();
+  [Throws]
+  void setFocusAreas(optional sequence<CameraRegion> focusAreas);
 
   /* focal length in millimetres */
   [Throws]
   readonly attribute double focalLength;
 
   /* the distances in metres to where the image subject appears to be
      in focus.  'focusDistanceOptimum' is where the subject will appear
      sharpest; the difference between 'focusDistanceFar' and
@@ -240,31 +244,43 @@ interface CameraControl : MediaStream
      the recording process encountered an error, or because one of the
      recording limits (see CameraStartRecordingOptions) was reached. */
   attribute CameraRecorderStateChange? onRecorderStateChange;
 
   /* the function to call when the viewfinder stops or starts,
      useful for synchronizing other UI elements. */
   attribute CameraPreviewStateChange? onPreviewStateChange;
 
-  /* the size of the picture to be returned by a call to takePicture();
+  /* the attribute is deprecated in favour of get/setPictureSize.
+
+     the size of the picture to be returned by a call to takePicture();
      an object with 'height' and 'width' properties that corresponds to
      one of the options returned by capabilities.pictureSizes. */
   [Throws]
   attribute any              pictureSize;
+  [Throws]
+  CameraSize getPictureSize();
+  [Throws]
+  void setPictureSize(optional CameraSize size);
 
-  /* the size of the thumbnail to be included in the picture returned
+  /* the attribute is deprecated in favour of get/setThumbnailSize.
+
+     the size of the thumbnail to be included in the picture returned
      by a call to takePicture(), assuming the chosen fileFormat supports
      one; an object with 'height' and 'width' properties that corresponds
      to one of the options returned by capabilities.pictureSizes.
 
      this setting should be considered a hint: the implementation will
      respect it when possible, and override it if necessary. */
   [Throws]
-  attribute any             thumbnailSize;
+  attribute any              thumbnailSize;
+  [Throws]
+  CameraSize getThumbnailSize();
+  [Throws]
+  void setThumbnailSize(optional CameraSize size);
 
   /* the angle, in degrees, that the image sensor is mounted relative
      to the display; e.g. if 'sensorAngle' is 270 degrees (or -90 degrees),
      then the preview stream needs to be rotated +90 degrees to have the
      same orientation as the real world. */
   readonly attribute long   sensorAngle;
 
   /* tell the camera to attempt to focus the image */