blob: fe3f93db87081cebfc898e442b903cb6d2c49731 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/mediastream/MediaDevices.h"
#include "bindings/core/v8/ScriptPromise.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"
#include "core/dom/events/Event.h"
#include "modules/mediastream/MediaDevicesRequest.h"
#include "modules/mediastream/MediaErrorState.h"
#include "modules/mediastream/MediaStream.h"
#include "modules/mediastream/MediaStreamConstraints.h"
#include "modules/mediastream/NavigatorMediaStream.h"
#include "modules/mediastream/NavigatorUserMediaErrorCallback.h"
#include "modules/mediastream/NavigatorUserMediaSuccessCallback.h"
#include "modules/mediastream/UserMediaController.h"
#include "modules/mediastream/UserMediaRequest.h"
#include "platform/bindings/ScriptState.h"
namespace blink {
namespace {
class PromiseSuccessCallback final : public NavigatorUserMediaSuccessCallback {
public:
explicit PromiseSuccessCallback(ScriptPromiseResolver* resolver)
: resolver_(resolver) {}
~PromiseSuccessCallback() {}
void handleEvent(MediaStream* stream) { resolver_->Resolve(stream); }
virtual void Trace(blink::Visitor* visitor) {
visitor->Trace(resolver_);
NavigatorUserMediaSuccessCallback::Trace(visitor);
}
private:
Member<ScriptPromiseResolver> resolver_;
};
class PromiseErrorCallback final : public NavigatorUserMediaErrorCallback {
public:
explicit PromiseErrorCallback(ScriptPromiseResolver* resolver)
: resolver_(resolver) {}
~PromiseErrorCallback() {}
void handleEvent(DOMExceptionOrOverconstrainedError error) {
resolver_->Reject(error);
}
virtual void Trace(blink::Visitor* visitor) {
visitor->Trace(resolver_);
NavigatorUserMediaErrorCallback::Trace(visitor);
}
private:
Member<ScriptPromiseResolver> resolver_;
};
} // namespace
MediaDevices* MediaDevices::Create(ExecutionContext* context) {
MediaDevices* media_devices = new MediaDevices(context);
media_devices->PauseIfNeeded();
return media_devices;
}
MediaDevices::MediaDevices(ExecutionContext* context)
: PausableObject(context),
observing_(false),
stopped_(false),
dispatch_scheduled_event_runner_(AsyncMethodRunner<MediaDevices>::Create(
this,
&MediaDevices::DispatchScheduledEvent)) {}
MediaDevices::~MediaDevices() {}
ScriptPromise MediaDevices::enumerateDevices(ScriptState* script_state) {
Document* document = ToDocument(ExecutionContext::From(script_state));
UserMediaController* user_media =
UserMediaController::From(document->GetFrame());
if (!user_media)
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kNotSupportedError,
"No media device controller available; is this a "
"detached window?"));
MediaDevicesRequest* request =
MediaDevicesRequest::Create(script_state, user_media);
return request->Start();
}
ScriptPromise MediaDevices::getUserMedia(ScriptState* script_state,
const MediaStreamConstraints& options,
ExceptionState& exception_state) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
NavigatorUserMediaSuccessCallback* success_callback =
new PromiseSuccessCallback(resolver);
NavigatorUserMediaErrorCallback* error_callback =
new PromiseErrorCallback(resolver);
Document* document = ToDocument(ExecutionContext::From(script_state));
UserMediaController* user_media =
UserMediaController::From(document->GetFrame());
if (!user_media)
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(kNotSupportedError,
"No media device controller available; is this a "
"detached window?"));
MediaErrorState error_state;
UserMediaRequest* request =
UserMediaRequest::Create(document, user_media, options, success_callback,
error_callback, error_state);
if (!request) {
DCHECK(error_state.HadException());
if (error_state.CanGenerateException()) {
error_state.RaiseException(exception_state);
return exception_state.Reject(script_state);
}
ScriptPromise rejected_promise = resolver->Promise();
resolver->Reject(error_state.CreateError());
return rejected_promise;
}
String error_message;
if (!request->IsSecureContextUse(error_message)) {
return ScriptPromise::RejectWithDOMException(
script_state, DOMException::Create(kNotSupportedError, error_message));
}
auto promise = resolver->Promise();
request->Start();
return promise;
}
void MediaDevices::DidChangeMediaDevices() {
Document* document = ToDocument(GetExecutionContext());
DCHECK(document);
if (RuntimeEnabledFeatures::OnDeviceChangeEnabled())
ScheduleDispatchEvent(Event::Create(EventTypeNames::devicechange));
}
const AtomicString& MediaDevices::InterfaceName() const {
return EventTargetNames::MediaDevices;
}
ExecutionContext* MediaDevices::GetExecutionContext() const {
return PausableObject::GetExecutionContext();
}
void MediaDevices::RemoveAllEventListeners() {
EventTargetWithInlineData::RemoveAllEventListeners();
DCHECK(!HasEventListeners());
StopObserving();
}
void MediaDevices::AddedEventListener(
const AtomicString& event_type,
RegisteredEventListener& registered_listener) {
EventTargetWithInlineData::AddedEventListener(event_type,
registered_listener);
StartObserving();
}
void MediaDevices::RemovedEventListener(
const AtomicString& event_type,
const RegisteredEventListener& registered_listener) {
EventTargetWithInlineData::RemovedEventListener(event_type,
registered_listener);
if (!HasEventListeners())
StopObserving();
}
bool MediaDevices::HasPendingActivity() const {
DCHECK(stopped_ || observing_ == HasEventListeners());
return observing_;
}
void MediaDevices::ContextDestroyed(ExecutionContext*) {
if (stopped_)
return;
stopped_ = true;
StopObserving();
}
void MediaDevices::Pause() {
dispatch_scheduled_event_runner_->Pause();
}
void MediaDevices::Unpause() {
dispatch_scheduled_event_runner_->Unpause();
}
void MediaDevices::ScheduleDispatchEvent(Event* event) {
scheduled_events_.push_back(event);
dispatch_scheduled_event_runner_->RunAsync();
}
void MediaDevices::DispatchScheduledEvent() {
HeapVector<Member<Event>> events;
events.swap(scheduled_events_);
for (const auto& event : events)
DispatchEvent(event);
}
void MediaDevices::StartObserving() {
if (observing_ || stopped_)
return;
UserMediaController* user_media = GetUserMediaController();
if (!user_media)
return;
user_media->SetMediaDeviceChangeObserver(this);
observing_ = true;
}
void MediaDevices::StopObserving() {
if (!observing_)
return;
UserMediaController* user_media = GetUserMediaController();
if (!user_media)
return;
user_media->SetMediaDeviceChangeObserver(nullptr);
observing_ = false;
}
UserMediaController* MediaDevices::GetUserMediaController() {
Document* document = ToDocument(GetExecutionContext());
if (!document)
return nullptr;
return UserMediaController::From(document->GetFrame());
}
void MediaDevices::Dispose() {
StopObserving();
}
void MediaDevices::Trace(blink::Visitor* visitor) {
visitor->Trace(dispatch_scheduled_event_runner_);
visitor->Trace(scheduled_events_);
EventTargetWithInlineData::Trace(visitor);
PausableObject::Trace(visitor);
}
} // namespace blink