blob: 931ce9903e743db2d7271404ff420fc7fac50cda [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/audio_output_devices/HTMLMediaElementAudioOutputDevice.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/ScriptState.h"
#include "core/dom/DOMException.h"
#include "core/dom/ExecutionContext.h"
#include "modules/audio_output_devices/AudioOutputDeviceClient.h"
#include "modules/audio_output_devices/SetSinkIdCallbacks.h"
#include "public/platform/WebSecurityOrigin.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
namespace {
class SetSinkIdResolver : public ScriptPromiseResolver {
WTF_MAKE_NONCOPYABLE(SetSinkIdResolver);
public:
static SetSinkIdResolver* create(ScriptState*,
HTMLMediaElement&,
const String& sinkId);
~SetSinkIdResolver() override = default;
void startAsync();
DECLARE_VIRTUAL_TRACE();
private:
SetSinkIdResolver(ScriptState*, HTMLMediaElement&, const String& sinkId);
void timerFired(TimerBase*);
Member<HTMLMediaElement> m_element;
String m_sinkId;
Timer<SetSinkIdResolver> m_timer;
};
SetSinkIdResolver* SetSinkIdResolver::create(ScriptState* scriptState,
HTMLMediaElement& element,
const String& sinkId) {
SetSinkIdResolver* resolver =
new SetSinkIdResolver(scriptState, element, sinkId);
resolver->suspendIfNeeded();
resolver->keepAliveWhilePending();
return resolver;
}
SetSinkIdResolver::SetSinkIdResolver(ScriptState* scriptState,
HTMLMediaElement& element,
const String& sinkId)
: ScriptPromiseResolver(scriptState),
m_element(element),
m_sinkId(sinkId),
m_timer(this, &SetSinkIdResolver::timerFired) {}
void SetSinkIdResolver::startAsync() {
m_timer.startOneShot(0, BLINK_FROM_HERE);
}
void SetSinkIdResolver::timerFired(TimerBase* timer) {
ExecutionContext* context = getExecutionContext();
ASSERT(context && context->isDocument());
std::unique_ptr<SetSinkIdCallbacks> callbacks =
wrapUnique(new SetSinkIdCallbacks(this, *m_element, m_sinkId));
WebMediaPlayer* webMediaPlayer = m_element->webMediaPlayer();
if (webMediaPlayer) {
// Using release() to transfer ownership because |webMediaPlayer| is a
// platform object that takes raw pointers.
webMediaPlayer->setSinkId(m_sinkId,
WebSecurityOrigin(context->getSecurityOrigin()),
callbacks.release());
} else {
if (AudioOutputDeviceClient* client =
AudioOutputDeviceClient::from(context)) {
client->checkIfAudioSinkExistsAndIsAuthorized(context, m_sinkId,
std::move(callbacks));
} else {
// The context has been detached. Impossible to get a security origin to
// check.
ASSERT(context->activeDOMObjectsAreStopped());
reject(DOMException::create(
SecurityError,
"Impossible to authorize device for detached context"));
}
}
}
DEFINE_TRACE(SetSinkIdResolver) {
visitor->trace(m_element);
ScriptPromiseResolver::trace(visitor);
}
} // namespace
HTMLMediaElementAudioOutputDevice::HTMLMediaElementAudioOutputDevice()
: m_sinkId("") {}
String HTMLMediaElementAudioOutputDevice::sinkId(HTMLMediaElement& element) {
HTMLMediaElementAudioOutputDevice& aodElement =
HTMLMediaElementAudioOutputDevice::from(element);
return aodElement.m_sinkId;
}
void HTMLMediaElementAudioOutputDevice::setSinkId(const String& sinkId) {
m_sinkId = sinkId;
}
ScriptPromise HTMLMediaElementAudioOutputDevice::setSinkId(
ScriptState* scriptState,
HTMLMediaElement& element,
const String& sinkId) {
SetSinkIdResolver* resolver =
SetSinkIdResolver::create(scriptState, element, sinkId);
ScriptPromise promise = resolver->promise();
if (sinkId == HTMLMediaElementAudioOutputDevice::sinkId(element))
resolver->resolve();
else
resolver->startAsync();
return promise;
}
const char* HTMLMediaElementAudioOutputDevice::supplementName() {
return "HTMLMediaElementAudioOutputDevice";
}
HTMLMediaElementAudioOutputDevice& HTMLMediaElementAudioOutputDevice::from(
HTMLMediaElement& element) {
HTMLMediaElementAudioOutputDevice* supplement =
static_cast<HTMLMediaElementAudioOutputDevice*>(
Supplement<HTMLMediaElement>::from(element, supplementName()));
if (!supplement) {
supplement = new HTMLMediaElementAudioOutputDevice();
provideTo(element, supplementName(), supplement);
}
return *supplement;
}
DEFINE_TRACE(HTMLMediaElementAudioOutputDevice) {
Supplement<HTMLMediaElement>::trace(visitor);
}
} // namespace blink