Chromium Code Reviews| Index: third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp |
| diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp |
| index b900c515a35b7254993b5b328094b64835464a21..7fb2166c51fc585a89efeb8eabd8014423e25c17 100644 |
| --- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp |
| +++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp |
| @@ -7,12 +7,27 @@ |
| #include "bindings/core/v8/ScriptPromiseResolver.h" |
| #include "core/dom/DOMException.h" |
| #include "core/dom/ExceptionCode.h" |
| +#include "core/fileapi/Blob.h" |
| #include "modules/EventTargetModules.h" |
| #include "modules/mediastream/MediaStreamTrack.h" |
| +#include "platform/mojo/MojoHelper.h" |
| #include "public/platform/Platform.h" |
| +#include "public/platform/ServiceRegistry.h" |
| namespace blink { |
| +namespace { |
| + |
| +const char kNoServiceError[] = "ImageCapture service unavailable."; |
| + |
| +bool isActive(ScriptPromiseResolver* resolver) |
| +{ |
| + ExecutionContext* context = resolver->getExecutionContext(); |
| + return context && !context->activeDOMObjectsAreStopped(); |
| +} |
| + |
| +} // anonymous namespace |
| + |
| ImageCapture* ImageCapture::create(ExecutionContext* context, MediaStreamTrack* track, ExceptionState& exceptionState) |
| { |
| if (track->kind() != "video") { |
| @@ -26,6 +41,9 @@ ImageCapture* ImageCapture::create(ExecutionContext* context, MediaStreamTrack* |
| ImageCapture::~ImageCapture() |
| { |
| DCHECK(!hasEventListeners()); |
| + // There should be no more outstanding |m_serviceRequests| at this point |
| + // since each of them holds a persistent handle to this object. |
| + DCHECK(m_serviceRequests.isEmpty()); |
| } |
| const AtomicString& ImageCapture::interfaceName() const |
| @@ -46,9 +64,36 @@ bool ImageCapture::hasPendingActivity() const |
| void ImageCapture::contextDestroyed() |
| { |
| removeAllEventListeners(); |
| + m_serviceRequests.clear(); |
| DCHECK(!hasEventListeners()); |
| } |
| +ScriptPromise ImageCapture::takePhoto(ScriptState* scriptState, ExceptionState& exceptionState) |
| +{ |
| + |
| + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| + ScriptPromise promise = resolver->promise(); |
| + |
| + // Spec instructs to return an exception if the track's ready state is not "live". Also reject if the track is disabled or muted. |
| + if (m_streamTrack->readyState() != "live" || !m_streamTrack->enabled() || m_streamTrack->muted()) { |
| + resolver->reject(DOMException::create(InvalidStateError, "The associated Track is in an invalid state.")); |
| + return promise; |
| + } |
| + |
| + if (!m_service) { |
| + resolver->reject(DOMException::create(NotFoundError, kNoServiceError)); |
| + return promise; |
| + } |
| + |
| + m_serviceRequests.add(resolver); |
| + |
| + // 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() |
| + m_service->TakePhoto(m_streamTrack->component()->source()->id(), createBaseCallback(bind<String, String>(&ImageCapture::onTakePhoto, this, resolver))); |
| + return promise; |
| +} |
| + |
| ScriptPromise ImageCapture::grabFrame(ScriptState* scriptState, ExceptionState& exceptionState) |
| { |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| @@ -70,6 +115,33 @@ ImageCapture::ImageCapture(ExecutionContext* context, MediaStreamTrack* track) |
| , m_streamTrack(track) |
| { |
| DCHECK(m_streamTrack); |
| + DCHECK(!m_service.is_bound()); |
| + |
| + Platform::current()->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_service)); |
| + |
| + m_service.set_connection_error_handler(createBaseCallback(bind(&ImageCapture::onServiceConnectionError, WeakPersistentThisPointer<ImageCapture>(this)))); |
| +} |
| + |
| +void ImageCapture::onTakePhoto(ScriptPromiseResolver* resolver, const String& mimeType, const String& data) |
| +{ |
| + if (!isActive(resolver)) { |
| + resolver->reject(DOMException::create(AbortError, "Operation was aborted")); |
|
Reilly Grant (use Gerrit)
2016/04/28 16:19:40
If the context is inactive you don't need to resol
mcasas
2016/04/28 19:35:49
Done.
|
| + return; |
| + } |
| + WTF::CString string = data.utf8(); |
| + DCHECK(!string.isNull()); |
| + resolver->resolve(Blob::create(reinterpret_cast<unsigned char*>(string.mutableData()), string.length(), mimeType)); |
| + m_serviceRequests.remove(resolver); |
| +} |
| + |
| +void ImageCapture::onServiceConnectionError() |
| +{ |
| + m_service.reset(); |
| + for (ScriptPromiseResolver* resolver : m_serviceRequests) { |
| + if (isActive(resolver)) |
|
haraken
2016/04/28 15:19:59
Why do we need to check isActive? The m_serviceReq
Reilly Grant (use Gerrit)
2016/04/28 15:27:56
I cargo culted this check from CallbackPromiseAdap
haraken
2016/04/28 15:45:00
I'm saying the context is guaranteed to be alive h
Reilly Grant (use Gerrit)
2016/04/28 16:19:40
Ah, I see now.
mcasas
2016/04/28 19:35:49
Remove the check for isActive() then.
|
| + resolver->reject(DOMException::create(NotFoundError, kNoServiceError)); |
| + } |
| + m_serviceRequests.clear(); |
| } |
| bool ImageCapture::addEventListenerInternal(const AtomicString& eventType, EventListener* listener, const EventListenerOptions& options) |
| @@ -80,6 +152,7 @@ bool ImageCapture::addEventListenerInternal(const AtomicString& eventType, Event |
| DEFINE_TRACE(ImageCapture) |
| { |
| visitor->trace(m_streamTrack); |
| + visitor->trace(m_serviceRequests); |
| EventTargetWithInlineData::trace(visitor); |
| ContextLifecycleObserver::trace(visitor); |
| } |