Image Capture: add getPhotoSettings() method
This CL adds a method getPhotoSettings() to catch up with
the idl [1], and also adds tests for it.
ToT code calls the two mojo methods (getPhotoState() and SetOptions())
and distinguishes how to Resolve() the Promises via the Blink's
|service_requests_| map. Instead of extending this mechanism, this CL
binds to the OnMojo...() methods one of 4 new callbacks (WTF::Function):
- ResolveWithNothing()
- ResolveWithPhotoSettings()
- ResolveWithPhotoCapabilities()
- ResolveWithMediaTrackConstraints()
So that the different sequences to mojo and back are now:
getPhotoCapabilities --> OnMojoGetPhotoState --> ResolveWithPhotoCapabilities
getPhotoSettings --> OnMojoGetPhotoState --> ResolveWithPhotoSettings
setOptions --> OnMojoSetOptions --> OnMojoGetPhotoState --> ResolveWithNothing
SetMediaTrackConstraints --> OnMojoSetOptions --> OnMojoGetPhotoState --> ResolveWithMediaTrackConstraints
(note that the "set" versions have a cycle of setting and
a cycle of current state readback).
[1] https://github.com/w3c/mediacapture-image/issues/167
Bug: 732521
Change-Id: Id5a3ee3b58cedd289a18fa1c721be390776c6543
Reviewed-on: https://chromium-review.googlesource.com/534754
Reviewed-by: Kent Tamura <tkent@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Miguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#480447}
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 687c325..0cd11af 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -23,6 +23,7 @@
#if defined(OS_WIN)
// These tests are flaky on WebRTC Windows bots: https://crbug.com/633242.
#define MAYBE_GetPhotoCapabilities DISABLED_GetPhotoCapabilities
+#define MAYBE_GetPhotoSettings DISABLED_GetPhotoSettings
#define MAYBE_TakePhoto DISABLED_TakePhoto
#define MAYBE_GrabFrame DISABLED_GrabFrame
#define MAYBE_GetTrackCapabilities DISABLED_GetTrackCapabilities
@@ -30,6 +31,7 @@
#define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
#else
#define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
+#define MAYBE_GetPhotoSettings GetPhotoSettings
#define MAYBE_TakePhoto TakePhoto
#define MAYBE_GrabFrame GrabFrame
#define MAYBE_GetTrackCapabilities GetTrackCapabilities
@@ -122,6 +124,10 @@
ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetPhotoCapabilities()"));
}
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_GetPhotoSettings) {
+ embedded_test_server()->StartAcceptingConnections();
+ ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetPhotoSettings()"));
+}
IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureBrowserTest, MAYBE_TakePhoto) {
embedded_test_server()->StartAcceptingConnections();
ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhoto()"));
diff --git a/content/test/data/media/image_capture_test.html b/content/test/data/media/image_capture_test.html
index da8da8c8..ff7bce2 100644
--- a/content/test/data/media/image_capture_test.html
+++ b/content/test/data/media/image_capture_test.html
@@ -35,6 +35,25 @@
});
}
+// Runs an ImageCapture.getPhotoSettings().
+function testCreateAndGetPhotoSettings() {
+ navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
+ .then(stream => {
+ assertEquals('video', stream.getVideoTracks()[0].kind);
+ return new ImageCapture(stream.getVideoTracks()[0]);
+ })
+ .then(capturer => {
+ return capturer.getPhotoSettings();
+ })
+ .then(settings => {
+ // There's nothing to check here since |settings| vary per device.
+ reportTestSuccess();
+ })
+ .catch(err => {
+ return failTest(err.toString());
+ });
+}
+
// Runs an ImageCapture.takePhoto().
function testCreateAndTakePhoto() {
navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
diff --git a/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html b/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html
new file mode 100644
index 0000000..a6935363
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/imagecapture/getPhotoSettings.html
@@ -0,0 +1,47 @@
+<!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 fillLightModeNames = ["off", "auto", "flash"];
+
+// This test verifies that ImageCapture can call getPhotoSettings(), with a
+// mock Mojo interface implementation.
+async_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();
+
+ var mock_state;
+
+ mockImageCaptureReady
+ .then(mock => {
+ mock_state = mock.state();
+ return new ImageCapture(stream.getVideoTracks()[0]);
+ },
+ error => {
+ assert_unreached("Error creating MockImageCapture: " + error);
+ })
+ .then(capturer => {
+ return capturer.getPhotoSettings();
+ })
+ .then(settings => {
+ assert_equals(settings.imageWidth, mock_state.width.current, 'width');
+ assert_equals(settings.imageHeight, mock_state.height.current, 'height');
+ // TODO(mcasas): check the remaining two entries https://crbug.com/732521.
+
+ t.done();
+ })
+ .catch(error => {
+ assert_unreached("Error during getPhotoSettings(): " + error.message);
+ });
+}, 'exercises ImageCapture.getPhotoSettings()');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
index b743349..30a219a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3157,6 +3157,7 @@
getter track
method constructor
method getPhotoCapabilities
+ method getPhotoSettings
method grabFrame
method setOptions
method takePhoto
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index ff81a8a..720f354 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3086,6 +3086,7 @@
getter track
method constructor
method getPhotoCapabilities
+ method getPhotoSettings
method grabFrame
method setOptions
method takePhoto
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index 9b7e867..44f56e6 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -3819,6 +3819,7 @@
getter track
method constructor
method getPhotoCapabilities
+ method getPhotoSettings
method grabFrame
method setOptions
method takePhoto
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 f1136b1..ae8e3a87 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -3819,6 +3819,7 @@
getter track
method constructor
method getPhotoCapabilities
+ method getPhotoSettings
method grabFrame
method setOptions
method takePhoto
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index 1fabf58..fa0cd82 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -13,7 +13,6 @@
#include "modules/EventTargetModules.h"
#include "modules/imagecapture/MediaSettingsRange.h"
#include "modules/imagecapture/PhotoCapabilities.h"
-#include "modules/imagecapture/PhotoSettings.h"
#include "modules/mediastream/MediaStreamTrack.h"
#include "modules/mediastream/MediaTrackCapabilities.h"
#include "modules/mediastream/MediaTrackConstraints.h"
@@ -128,7 +127,10 @@
resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
return promise;
}
- service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+ service_requests_.insert(resolver);
+
+ auto resolver_cb = WTF::Bind(&ImageCapture::ResolveWithPhotoCapabilities,
+ WrapPersistent(this));
// m_streamTrack->component()->source()->id() is the renderer "name" of the
// camera;
@@ -136,9 +138,36 @@
// scriptState->getExecutionContext()->getSecurityOrigin()->toString()
service_->GetPhotoState(
stream_track_->Component()->Source()->Id(),
- ConvertToBaseCallback(
- WTF::Bind(&ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
- WrapPersistent(resolver), false /* trigger_take_photo */)));
+ ConvertToBaseCallback(WTF::Bind(
+ &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+ WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+ false /* trigger_take_photo */)));
+ return promise;
+}
+
+ScriptPromise ImageCapture::getPhotoSettings(ScriptState* script_state) {
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
+ ScriptPromise promise = resolver->Promise();
+
+ if (!service_) {
+ resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
+ return promise;
+ }
+ service_requests_.insert(resolver);
+
+ auto resolver_cb =
+ WTF::Bind(&ImageCapture::ResolveWithPhotoSettings, WrapPersistent(this));
+
+ // m_streamTrack->component()->source()->id() is the renderer "name" of the
+ // camera;
+ // TODO(mcasas) consider sending the security origin as well:
+ // scriptState->getExecutionContext()->getSecurityOrigin()->toString()
+ service_->GetPhotoState(
+ stream_track_->Component()->Source()->Id(),
+ ConvertToBaseCallback(WTF::Bind(
+ &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+ WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+ false /* trigger_take_photo */)));
return promise;
}
@@ -158,7 +187,7 @@
resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
return promise;
}
- service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+ service_requests_.insert(resolver);
// TODO(mcasas): should be using a mojo::StructTraits instead.
auto settings = media::mojom::blink::PhotoSettings::New();
@@ -211,11 +240,15 @@
settings->fill_light_mode = ParseFillLightMode(fill_light_mode);
}
+ auto resolver_cb =
+ WTF::Bind(&ImageCapture::ResolveWithNothing, WrapPersistent(this));
+
service_->SetOptions(
stream_track_->Component()->Source()->Id(), std::move(settings),
ConvertToBaseCallback(
WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
- WrapPersistent(resolver), trigger_take_photo)));
+ WrapPersistent(resolver),
+ WTF::Passed(std::move(resolver_cb)), trigger_take_photo)));
return promise;
}
@@ -233,7 +266,7 @@
return promise;
}
- service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>());
+ service_requests_.insert(resolver);
// m_streamTrack->component()->source()->id() is the renderer "name" of the
// camera;
@@ -499,13 +532,19 @@
current_constraints_ = temp_constraints;
- service_requests_.insert(resolver, constraints_vector);
+ service_requests_.insert(resolver);
+
+ MediaTrackConstraints resolver_constraints;
+ resolver_constraints.setAdvanced(constraints_vector);
+ auto resolver_cb = WTF::Bind(&ImageCapture::ResolveWithMediaTrackConstraints,
+ WrapPersistent(this), resolver_constraints);
service_->SetOptions(
stream_track_->Component()->Source()->Id(), std::move(settings),
- ConvertToBaseCallback(
- WTF::Bind(&ImageCapture::OnMojoSetOptions, WrapPersistent(this),
- WrapPersistent(resolver), false /* trigger_take_photo */)));
+ ConvertToBaseCallback(WTF::Bind(
+ &ImageCapture::OnMojoSetOptions, WrapPersistent(this),
+ WrapPersistent(resolver), WTF::Passed(std::move(resolver_cb)),
+ false /* trigger_take_photo */)));
}
const MediaTrackConstraintSet& ImageCapture::GetMediaTrackConstraints() const {
@@ -599,10 +638,10 @@
void ImageCapture::OnMojoGetPhotoState(
ScriptPromiseResolver* resolver,
+ PromiseResolverFunction resolve_function,
bool trigger_take_photo,
media::mojom::blink::PhotoStatePtr photo_state) {
- if (!service_requests_.Contains(resolver))
- return;
+ DCHECK(service_requests_.Contains(resolver));
if (photo_state.is_null()) {
resolver->Reject(DOMException::Create(kUnknownError, "platform error"));
@@ -610,6 +649,11 @@
return;
}
+ photo_settings_ = PhotoSettings();
+ photo_settings_.setImageHeight(photo_state->height->current);
+ photo_settings_.setImageWidth(photo_state->width->current);
+ // TODO(mcasas): collect the remaining two entries https://crbug.com/732521.
+
photo_capabilities_ = PhotoCapabilities::Create();
photo_capabilities_->SetRedEyeReduction(photo_state->red_eye_reduction);
// TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
@@ -637,27 +681,15 @@
return;
}
- // If this is a response to a SetMediaTrackConstraints() request, it will have
- // the original constraints to apply: Resolve() with those; Resolve() with the
- // |photo_capabilities_| otherwise.
- const HeapVector<MediaTrackConstraintSet>& originalConstraints =
- service_requests_.at(resolver);
- if (originalConstraints.IsEmpty()) {
- resolver->Resolve(photo_capabilities_);
- } else {
- MediaTrackConstraints constraints;
- constraints.setAdvanced(originalConstraints);
- resolver->Resolve(constraints);
- }
-
+ (*resolve_function)(resolver);
service_requests_.erase(resolver);
}
void ImageCapture::OnMojoSetOptions(ScriptPromiseResolver* resolver,
+ PromiseResolverFunction resolve_function,
bool trigger_take_photo,
bool result) {
- if (!service_requests_.Contains(resolver))
- return;
+ DCHECK(service_requests_.Contains(resolver));
if (!result) {
resolver->Reject(DOMException::Create(kUnknownError, "setOptions failed"));
@@ -668,15 +700,15 @@
// Retrieve the current device status after setting the options.
service_->GetPhotoState(
stream_track_->Component()->Source()->Id(),
- ConvertToBaseCallback(
- WTF::Bind(&ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
- WrapPersistent(resolver), trigger_take_photo)));
+ ConvertToBaseCallback(WTF::Bind(
+ &ImageCapture::OnMojoGetPhotoState, WrapPersistent(this),
+ WrapPersistent(resolver), WTF::Passed(std::move(resolve_function)),
+ trigger_take_photo)));
}
void ImageCapture::OnMojoTakePhoto(ScriptPromiseResolver* resolver,
media::mojom::blink::BlobPtr blob) {
- if (!service_requests_.Contains(resolver))
- return;
+ DCHECK(service_requests_.Contains(resolver));
// TODO(mcasas): Should be using a mojo::StructTraits.
if (blob->data.IsEmpty()) {
@@ -790,11 +822,34 @@
void ImageCapture::OnServiceConnectionError() {
service_.reset();
- for (const auto& resolver : service_requests_)
- resolver.key->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
+ for (ScriptPromiseResolver* resolver : service_requests_)
+ resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError));
service_requests_.clear();
}
+void ImageCapture::ResolveWithNothing(ScriptPromiseResolver* resolver) {
+ DCHECK(resolver);
+ resolver->Resolve();
+}
+
+void ImageCapture::ResolveWithPhotoSettings(ScriptPromiseResolver* resolver) {
+ DCHECK(resolver);
+ resolver->Resolve(photo_settings_);
+}
+
+void ImageCapture::ResolveWithPhotoCapabilities(
+ ScriptPromiseResolver* resolver) {
+ DCHECK(resolver);
+ resolver->Resolve(photo_capabilities_);
+}
+
+void ImageCapture::ResolveWithMediaTrackConstraints(
+ MediaTrackConstraints constraints,
+ ScriptPromiseResolver* resolver) {
+ DCHECK(resolver);
+ resolver->Resolve(constraints);
+}
+
DEFINE_TRACE(ImageCapture) {
visitor->Trace(stream_track_);
visitor->Trace(capabilities_);
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
index 1c096df1..600144c8 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
@@ -12,6 +12,7 @@
#include "media/capture/mojo/image_capture.mojom-blink.h"
#include "modules/EventTargetModules.h"
#include "modules/ModulesExport.h"
+#include "modules/imagecapture/PhotoSettings.h"
#include "modules/mediastream/MediaTrackCapabilities.h"
#include "modules/mediastream/MediaTrackConstraintSet.h"
#include "modules/mediastream/MediaTrackSettings.h"
@@ -24,11 +25,10 @@
class MediaStreamTrack;
class MediaTrackConstraints;
class PhotoCapabilities;
-class PhotoSettings;
class ScriptPromiseResolver;
class WebImageCaptureFrameGrabber;
-// TODO(mcasas): Consideradding a LayoutTest checking that this class is not
+// TODO(mcasas): Consider adding a LayoutTest checking that this class is not
// garbage collected while it has event listeners.
class MODULES_EXPORT ImageCapture final
: public EventTargetWithInlineData,
@@ -56,6 +56,7 @@
MediaStreamTrack* videoStreamTrack() const { return stream_track_.Get(); }
ScriptPromise getPhotoCapabilities(ScriptState*);
+ ScriptPromise getPhotoSettings(ScriptState*);
ScriptPromise setOptions(ScriptState*,
const PhotoSettings&,
@@ -79,12 +80,17 @@
DECLARE_VIRTUAL_TRACE();
private:
+ using PromiseResolverFunction =
+ std::unique_ptr<Function<void(ScriptPromiseResolver*)>>;
+
ImageCapture(ExecutionContext*, MediaStreamTrack*);
void OnMojoGetPhotoState(ScriptPromiseResolver*,
+ PromiseResolverFunction,
bool trigger_take_photo,
media::mojom::blink::PhotoStatePtr);
void OnMojoSetOptions(ScriptPromiseResolver*,
+ PromiseResolverFunction,
bool trigger_take_photo,
bool result);
void OnMojoTakePhoto(ScriptPromiseResolver*, media::mojom::blink::BlobPtr);
@@ -92,6 +98,12 @@
void UpdateMediaTrackCapabilities(media::mojom::blink::PhotoStatePtr);
void OnServiceConnectionError();
+ void ResolveWithNothing(ScriptPromiseResolver*);
+ void ResolveWithPhotoSettings(ScriptPromiseResolver*);
+ void ResolveWithPhotoCapabilities(ScriptPromiseResolver*);
+ void ResolveWithMediaTrackConstraints(MediaTrackConstraints,
+ ScriptPromiseResolver*);
+
Member<MediaStreamTrack> stream_track_;
std::unique_ptr<WebImageCaptureFrameGrabber> frame_grabber_;
media::mojom::blink::ImageCapturePtr service_;
@@ -99,12 +111,11 @@
MediaTrackCapabilities capabilities_;
MediaTrackSettings settings_;
MediaTrackConstraintSet current_constraints_;
+ PhotoSettings photo_settings_;
Member<PhotoCapabilities> photo_capabilities_;
- HeapHashMap<Member<ScriptPromiseResolver>,
- HeapVector<MediaTrackConstraintSet>>
- service_requests_;
+ HeapHashSet<Member<ScriptPromiseResolver>> service_requests_;
};
} // namespace blink
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
index c9b84af..e819110 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.idl
@@ -16,6 +16,7 @@
[ImplementedAs=videoStreamTrack] readonly attribute MediaStreamTrack track;
[CallWith=ScriptState] Promise<PhotoCapabilities> getPhotoCapabilities();
+ [CallWith=ScriptState] Promise<PhotoSettings> getPhotoSettings();
[CallWith=ScriptState] Promise<void> setOptions(PhotoSettings photoSettings);
[CallWith=ScriptState] Promise<Blob> takePhoto(optional PhotoSettings photoSettings);
[CallWith=ScriptState] Promise<ImageBitmap> grabFrame();