| // 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 = | |
| WTF::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. | |
| DCHECK(context->isContextDestroyed()); | |
| 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 |