Image Capture: MediaStreamTrack::applyConstraints()

This CL adds MediaStreamTrack.applyConstraints()
method behind a flag (that is implied if ImageCapture
is on).  The method essentially derives to
ImageCapture::setMediaTrackConstraints(); this one
prepares a mojo::PhotoSettings and applies it via
mojo interface.

Only simple Constrains are supported now, i.e.,
{zoom : 3.14} is supported, but {zoom : { ideal : 100}}
or {zoom : { exact : 27}} are not (TBD later).

LayoutTests are added where appropriate.

Also a cleanup: methods that return Promises are not
supposed to throw exceptions; so I removed an unused
ExceptionState from some ImageCapture methods.

applyConstraints() when called without the optional
argument |constraints|, and is supposed to do... nothing?
Filed Spec bug:
https://github.com/w3c/mediacapture-main/issues/438

BUG=700607

Review-Url: https://codereview.chromium.org/2757673005
Cr-Commit-Position: refs/heads/master@{#457771}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-init.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-init.https-expected.txt
index ec0c1bf7..56f960e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-init.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-init.https-expected.txt
@@ -28,7 +28,7 @@
 PASS MediaStreamTrack interface: operation getCapabilities() 
 PASS MediaStreamTrack interface: operation getConstraints() 
 PASS MediaStreamTrack interface: operation getSettings() 
-FAIL MediaStreamTrack interface: operation applyConstraints(MediaTrackConstraints) assert_own_property: interface prototype object missing non-static operation expected property "applyConstraints" missing
+PASS MediaStreamTrack interface: operation applyConstraints(MediaTrackConstraints) 
 PASS MediaStreamTrack must be primary interface of track 
 PASS Stringification of track 
 PASS MediaStreamTrack interface: track must inherit property "kind" with the proper type (0) 
@@ -46,8 +46,8 @@
 PASS MediaStreamTrack interface: track must inherit property "getCapabilities" with the proper type (12) 
 PASS MediaStreamTrack interface: track must inherit property "getConstraints" with the proper type (13) 
 PASS MediaStreamTrack interface: track must inherit property "getSettings" with the proper type (14) 
-FAIL MediaStreamTrack interface: track must inherit property "applyConstraints" with the proper type (15) assert_inherits: property "applyConstraints" not found in prototype chain
-FAIL MediaStreamTrack interface: calling applyConstraints(MediaTrackConstraints) on track with too few arguments must throw TypeError assert_inherits: property "applyConstraints" not found in prototype chain
+PASS MediaStreamTrack interface: track must inherit property "applyConstraints" with the proper type (15) 
+PASS MediaStreamTrack interface: calling applyConstraints(MediaTrackConstraints) on track with too few arguments must throw TypeError 
 PASS EventTarget interface: track must inherit property "addEventListener" with the proper type (0) 
 PASS EventTarget interface: calling addEventListener(DOMString,EventListener,boolean) on track with too few arguments must throw TypeError 
 PASS EventTarget interface: track must inherit property "removeEventListener" with the proper type (1) 
diff --git a/third_party/WebKit/LayoutTests/fast/imagecapture/MediaStreamTrack-applyConstraints.html b/third_party/WebKit/LayoutTests/fast/imagecapture/MediaStreamTrack-applyConstraints.html
new file mode 100644
index 0000000..7751813
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/imagecapture/MediaStreamTrack-applyConstraints.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+<body>
+<canvas id='canvas' width=10 height=10/>
+</body>
+<script>
+
+// This test verifies that MediaStreamTrack.applyConstraints() exists and that,
+// when called with no parameters, returns a Promise that is resolved. This
+// might not make sense: https://github.com/w3c/mediacapture-main/issues/438 .
+// Other tests go deeper.
+promise_test(function(t) {
+  var canvas = document.getElementById('canvas');
+  var context = canvas.getContext("2d");
+  context.fillStyle = "red";
+  context.fillRect(0, 0, 10, 10);
+
+  var stream = canvas.captureStream();
+  assert_equals(stream.getAudioTracks().length, 0);
+  assert_equals(stream.getVideoTracks().length, 1);
+
+  var videoTrack = stream.getVideoTracks()[0];
+  assert_equals(typeof videoTrack.applyConstraints, 'function');
+
+  return videoTrack.applyConstraints();
+}, 'MediaStreamTrack.applyConstraints()');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html
new file mode 100644
index 0000000..06b47dc7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/mojo-helpers.js"></script>
+<script src="resources/mock-imagecapture.js"></script>
+<body>
+<canvas id='canvas' width=10 height=10/>
+</body>
+<script>
+
+const meteringModeNames = ["none", "manual", "single-shot", "continuous"];
+const fillLightModeNames = ["none", "off", "auto", "flash", "torch"];
+
+// This test verifies that we can all MediaStreamTrack.applyConstraints(), with
+// a mock Mojo service implementation.
+
+async_test(function(t) {
+  var canvas = document.getElementById('canvas');
+  var context = canvas.getContext("2d");
+  context.fillStyle = "red";
+  context.fillRect(0, 0, 10, 10);
+
+  const constraints = { advanced : [{ whiteBalanceMode : "manual",
+                                      exposureMode     : "continuous",
+                                      focusMode        : "single-shot",
+
+                                      exposureCompensation : 133.77,
+                                      colorTemperature     : 6000,
+                                      iso                  : 120.0,
+
+                                      brightness           : 3,
+                                      contrast             : 4,
+                                      saturation           : 5,
+                                      sharpness            : 6,
+
+                                      zoom                 : 3.141592
+                                      // TODO: torch https://crbug.com/700607.
+                                    }]};
+
+  var theMock = null;
+  mockImageCaptureReady
+    .then(mock => {
+      theMock = mock;
+      var stream = canvas.captureStream();
+      var videoTrack = stream.getVideoTracks()[0];
+
+      return videoTrack.applyConstraints(constraints);
+    })
+    .catch(error => {
+      assert_unreached("Error creating MockImageCapture: " + error.message);
+    })
+    .then(function() {
+      assert_equals(constraints.advanced[0].whiteBalanceMode,
+                    meteringModeNames[theMock.options().white_balance_mode],
+                    'whiteBalanceMode');
+      assert_equals(constraints.advanced[0].exposureMode,
+                    meteringModeNames[theMock.options().exposure_mode],
+                    'exposureMode');
+      assert_equals(constraints.advanced[0].focusMode,
+                    meteringModeNames[theMock.options().focus_mode],
+                    'focusMode');
+
+      assert_equals(constraints.advanced[0].exposureCompensation,
+                    theMock.options().exposure_compensation,
+                    'exposure_compensation');
+      assert_equals(constraints.advanced[0].colorTemperature,
+                    theMock.options().color_temperature, 'color_temperature');
+      assert_equals(constraints.advanced[0].iso, theMock.options().iso, 'iso');
+
+      assert_equals(constraints.advanced[0].brightness,
+                    theMock.options().brightness, 'brightness value');
+      assert_equals(constraints.advanced[0].contrast,
+                    theMock.options().contrast, 'constrast value');
+      assert_equals(constraints.advanced[0].saturation,
+                    theMock.options().saturation, 'saturation value');
+      assert_equals(constraints.advanced[0].sharpness,
+                    theMock.options().sharpness, 'sharpness value');
+
+      t.done();
+    })
+    .catch(error => {
+      assert_unreached("applyConstraints(): " + error.message);
+    })
+
+}, 'exercises MediaStreamTrack.applyConstraints(constraints)');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index d01d81c..86f3ce9 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -4114,6 +4114,7 @@
     getter onmute
     getter onunmute
     getter readyState
+    method applyConstraints
     method clone
     method constructor
     method getCapabilities
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index 06046a5..74672df 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -16,6 +16,7 @@
 #include "modules/imagecapture/PhotoSettings.h"
 #include "modules/mediastream/MediaStreamTrack.h"
 #include "modules/mediastream/MediaTrackCapabilities.h"
+#include "modules/mediastream/MediaTrackConstraintSet.h"
 #include "platform/WaitableEvent.h"
 #include "platform/mojo/MojoHelper.h"
 #include "public/platform/InterfaceProvider.h"
@@ -114,9 +115,7 @@
   DCHECK(!hasEventListeners());
 }
 
-ScriptPromise ImageCapture::getPhotoCapabilities(
-    ScriptState* scriptState,
-    ExceptionState& exceptionState) {
+ScriptPromise ImageCapture::getPhotoCapabilities(ScriptState* scriptState) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
   ScriptPromise promise = resolver->promise();
 
@@ -124,7 +123,6 @@
     resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
     return promise;
   }
-
   m_serviceRequests.insert(resolver);
 
   // m_streamTrack->component()->source()->id() is the renderer "name" of the
@@ -140,8 +138,7 @@
 }
 
 ScriptPromise ImageCapture::setOptions(ScriptState* scriptState,
-                                       const PhotoSettings& photoSettings,
-                                       ExceptionState& exceptionState) {
+                                       const PhotoSettings& photoSettings) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
   ScriptPromise promise = resolver->promise();
 
@@ -155,12 +152,11 @@
     resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
     return promise;
   }
-
   m_serviceRequests.insert(resolver);
 
   // TODO(mcasas): should be using a mojo::StructTraits instead.
-  media::mojom::blink::PhotoSettingsPtr settings =
-      media::mojom::blink::PhotoSettings::New();
+  auto settings = media::mojom::blink::PhotoSettings::New();
+
   settings->has_zoom = photoSettings.hasZoom();
   if (settings->has_zoom)
     settings->zoom = photoSettings.zoom();
@@ -225,8 +221,7 @@
   return promise;
 }
 
-ScriptPromise ImageCapture::takePhoto(ScriptState* scriptState,
-                                      ExceptionState& exceptionState) {
+ScriptPromise ImageCapture::takePhoto(ScriptState* scriptState) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
   ScriptPromise promise = resolver->promise();
 
@@ -235,7 +230,6 @@
         InvalidStateError, "The associated Track is in an invalid state."));
     return promise;
   }
-
   if (!m_service) {
     resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
     return promise;
@@ -254,8 +248,7 @@
   return promise;
 }
 
-ScriptPromise ImageCapture::grabFrame(ScriptState* scriptState,
-                                      ExceptionState& exceptionState) {
+ScriptPromise ImageCapture::grabFrame(ScriptState* scriptState) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
   ScriptPromise promise = resolver->promise();
 
@@ -289,6 +282,88 @@
   return m_capabilities;
 }
 
+// TODO(mcasas): make the implementation fully Spec compliant, see inside the
+// method, https://crbug.com/700607.
+void ImageCapture::setMediaTrackConstraints(
+    ScriptPromiseResolver* resolver,
+    const MediaTrackConstraintSet& constraints) {
+  if (!m_service) {
+    resolver->reject(DOMException::create(NotFoundError, kNoServiceError));
+    return;
+  }
+  m_serviceRequests.insert(resolver);
+
+  auto settings = media::mojom::blink::PhotoSettings::New();
+
+  // TODO(mcasas): support other Mode types beyond simple string i.e. the
+  // equivalents of "sequence<DOMString>"" or "ConstrainDOMStringParameters".
+  settings->has_white_balance_mode = constraints.hasWhiteBalanceMode() &&
+                                     constraints.whiteBalanceMode().isString();
+  if (settings->has_white_balance_mode) {
+    settings->white_balance_mode =
+        parseMeteringMode(constraints.whiteBalanceMode().getAsString());
+  }
+  settings->has_exposure_mode =
+      constraints.hasExposureMode() && constraints.exposureMode().isString();
+  if (settings->has_exposure_mode) {
+    settings->exposure_mode =
+        parseMeteringMode(constraints.exposureMode().getAsString());
+  }
+
+  settings->has_focus_mode =
+      constraints.hasFocusMode() && constraints.focusMode().isString();
+  if (settings->has_focus_mode) {
+    settings->focus_mode =
+        parseMeteringMode(constraints.focusMode().getAsString());
+  }
+
+  // TODO(mcasas): support ConstrainDoubleRange where applicable.
+  settings->has_exposure_compensation =
+      constraints.hasExposureCompensation() &&
+      constraints.exposureCompensation().isDouble();
+  if (settings->has_exposure_compensation) {
+    settings->exposure_compensation =
+        constraints.exposureCompensation().getAsDouble();
+  }
+  settings->has_color_temperature = constraints.hasColorTemperature() &&
+                                    constraints.colorTemperature().isDouble();
+  if (settings->has_color_temperature)
+    settings->color_temperature = constraints.colorTemperature().getAsDouble();
+  settings->has_iso = constraints.hasIso() && constraints.iso().isDouble();
+  if (settings->has_iso)
+    settings->iso = constraints.iso().getAsDouble();
+
+  settings->has_brightness =
+      constraints.hasBrightness() && constraints.brightness().isDouble();
+  if (settings->has_brightness)
+    settings->brightness = constraints.brightness().getAsDouble();
+  settings->has_contrast =
+      constraints.hasContrast() && constraints.contrast().isDouble();
+  if (settings->has_contrast)
+    settings->contrast = constraints.contrast().getAsDouble();
+  settings->has_saturation =
+      constraints.hasSaturation() && constraints.saturation().isDouble();
+  if (settings->has_saturation)
+    settings->saturation = constraints.saturation().getAsDouble();
+  settings->has_sharpness =
+      constraints.hasSharpness() && constraints.sharpness().isDouble();
+  if (settings->has_sharpness)
+    settings->sharpness = constraints.sharpness().getAsDouble();
+
+  settings->has_zoom = constraints.hasZoom() && constraints.zoom().isDouble();
+  if (settings->has_zoom)
+    settings->zoom = constraints.zoom().getAsDouble();
+
+  // TODO(mcasas): add |torch| when the mojom interface is updated,
+  // https://crbug.com/700607.
+
+  m_service->SetOptions(m_streamTrack->component()->source()->id(),
+                        std::move(settings),
+                        convertToBaseCallback(WTF::bind(
+                            &ImageCapture::onSetOptions, wrapPersistent(this),
+                            wrapPersistent(resolver))));
+}
+
 ImageCapture::ImageCapture(ExecutionContext* context, MediaStreamTrack* track)
     : ContextLifecycleObserver(context), m_streamTrack(track) {
   DCHECK(m_streamTrack);
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
index 84de0da..fd53209 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
@@ -20,6 +20,7 @@
 
 class ExceptionState;
 class MediaStreamTrack;
+class MediaTrackConstraintSet;
 class PhotoSettings;
 class ScriptPromiseResolver;
 class WebImageCaptureFrameGrabber;
@@ -51,16 +52,19 @@
 
   MediaStreamTrack* videoStreamTrack() const { return m_streamTrack.get(); }
 
-  ScriptPromise getPhotoCapabilities(ScriptState*, ExceptionState&);
+  ScriptPromise getPhotoCapabilities(ScriptState*);
 
-  ScriptPromise setOptions(ScriptState*, const PhotoSettings&, ExceptionState&);
+  ScriptPromise setOptions(ScriptState*, const PhotoSettings&);
 
-  ScriptPromise takePhoto(ScriptState*, ExceptionState&);
+  ScriptPromise takePhoto(ScriptState*);
 
-  ScriptPromise grabFrame(ScriptState*, ExceptionState&);
+  ScriptPromise grabFrame(ScriptState*);
 
   MediaTrackCapabilities& getMediaTrackCapabilities();
 
+  void setMediaTrackConstraints(ScriptPromiseResolver*,
+                                const MediaTrackConstraintSet&);
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
index 9b654ba..230944a 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
@@ -15,8 +15,8 @@
 ] interface ImageCapture {
     readonly attribute MediaStreamTrack videoStreamTrack;
 
-    [CallWith=ScriptState, RaisesException] Promise<PhotoCapabilities> getPhotoCapabilities();
-    [CallWith=ScriptState, RaisesException] Promise<void> setOptions(PhotoSettings photoSettings);
-    [CallWith=ScriptState, RaisesException] Promise<Blob> takePhoto();
-    [CallWith=ScriptState, RaisesException] Promise<ImageBitmap> grabFrame();
+    [CallWith=ScriptState] Promise<PhotoCapabilities> getPhotoCapabilities();
+    [CallWith=ScriptState] Promise<void> setOptions(PhotoSettings photoSettings);
+    [CallWith=ScriptState] Promise<Blob> takePhoto();
+    [CallWith=ScriptState] Promise<ImageBitmap> grabFrame();
 };
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
index 9c077a3..a93e097 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
@@ -27,6 +27,8 @@
 
 #include <memory>
 #include "bindings/core/v8/ExceptionMessages.h"
+#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/dom/ExecutionContext.h"
@@ -36,6 +38,7 @@
 #include "modules/mediastream/MediaConstraintsImpl.h"
 #include "modules/mediastream/MediaStream.h"
 #include "modules/mediastream/MediaTrackCapabilities.h"
+#include "modules/mediastream/MediaTrackConstraints.h"
 #include "modules/mediastream/MediaTrackSettings.h"
 #include "modules/mediastream/UserMediaController.h"
 #include "platform/mediastream/MediaStreamCenter.h"
@@ -273,6 +276,30 @@
   }
 }
 
+ScriptPromise MediaStreamTrack::applyConstraints(
+    ScriptState* scriptState,
+    const MediaTrackConstraints& constraints) {
+  ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+  ScriptPromise promise = resolver->promise();
+
+  // |constraints| is an optional argument, which is strange.
+  // TODO(mcasas): remove this provision if |constraints| is not optional:
+  // https://github.com/w3c/mediacapture-main/issues/438
+  if (!constraints.hasAdvanced()) {
+    resolver->resolve();
+    return promise;
+  }
+
+  if (!m_imageCapture) {
+    resolver->reject(DOMException::create(
+        NotSupportedError, "Track type not currently supported"));
+    return promise;
+  }
+
+  m_imageCapture->setMediaTrackConstraints(resolver, constraints.advanced()[0]);
+  return promise;
+}
+
 bool MediaStreamTrack::ended() const {
   return m_stopped || (m_readyState == MediaStreamSource::ReadyStateEnded);
 }
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
index 8c19b95d..7fe1b26 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
@@ -26,7 +26,9 @@
 #ifndef MediaStreamTrack_h
 #define MediaStreamTrack_h
 
+#include <memory>
 #include "bindings/core/v8/ActiveScriptWrappable.h"
+#include "bindings/core/v8/ScriptPromise.h"
 #include "core/dom/ContextLifecycleObserver.h"
 #include "modules/EventTargetModules.h"
 #include "modules/ModulesExport.h"
@@ -34,7 +36,6 @@
 #include "platform/mediastream/MediaStreamSource.h"
 #include "public/platform/WebMediaConstraints.h"
 #include "wtf/Forward.h"
-#include <memory>
 
 namespace blink {
 
@@ -76,14 +77,14 @@
   void stopTrack(ExceptionState&);
   virtual MediaStreamTrack* clone(ScriptState*);
 
-  void getConstraints(MediaTrackConstraints&);
-
   // This function is called when constrains have been successfully applied.
   // Called from UserMediaRequest when it succeeds. It is not IDL-exposed.
   void setConstraints(const WebMediaConstraints&);
 
   void getCapabilities(MediaTrackCapabilities&);
+  void getConstraints(MediaTrackConstraints&);
   void getSettings(MediaTrackSettings&);
+  ScriptPromise applyConstraints(ScriptState*, const MediaTrackConstraints&);
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(mute);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(unmute);
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
index 1ff409c..810313db 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
@@ -53,4 +53,5 @@
     [RuntimeEnabled=MediaTrackCapabilities] MediaTrackCapabilities getCapabilities();
     [RuntimeEnabled=MediaConstraints] MediaTrackConstraints getConstraints();
     [RuntimeEnabled=MediaGetSettings] MediaTrackSettings getSettings();
+    [RuntimeEnabled=MediaTrackApplyConstraints, CallWith=ScriptState] Promise<void> applyConstraints(optional MediaTrackConstraints constraints);
 };
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl b/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl
index 1a2ec0ee..44e0038 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl
+++ b/third_party/WebKit/Source/modules/mediastream/MediaTrackConstraintSet.idl
@@ -33,6 +33,24 @@
     [RuntimeEnabled=MediaConstraints,MediaCaptureDepth] ConstrainDouble depthFar;
     [RuntimeEnabled=MediaConstraints,MediaCaptureDepth] ConstrainDouble focalLengthX;
     [RuntimeEnabled=MediaConstraints,MediaCaptureDepth] ConstrainDouble focalLengthY;
+    // W3C Image Capture API
+    // https://w3c.github.io/mediacapture-image/#mediatrackconstraintset-section
+    // TODO(mcasas) move out when partial dictionaries are supported
+    // http://crbug.com/579896.
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDOMString whiteBalanceMode;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDOMString exposureMode;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDOMString focusMode;
+    // TODO(mcasas): add support for ConstrainPoint2D pointsOfInterest, see
+    // https://crbug.com/700607
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    exposureCompensation;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    colorTemperature;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    iso;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    brightness;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    contrast;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    saturation;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    sharpness;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainDouble    zoom;
+    [RuntimeEnabled=MediaConstraints,ImageCapture] ConstrainBoolean   torch;
     // The "mandatory" and "_optional" members are retained for conformance
     // with https://www.w3.org/TR/2013/WD-mediacapture-streams-20130903/
     Dictionary mandatory;
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 0fc20208..aca93d0 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -564,6 +564,11 @@
       status: "experimental",
     },
     {
+      name: "MediaTrackApplyConstraints",
+      implied_by: ["ImageCapture"],
+      status: "experimental",
+    },
+    {
       name: "MediaTrackCapabilities",
       implied_by: ["ImageCapture"],
       status: "experimental",