blob: 0fb775616f3c9a1aca40fff96dfb7336536189d2 [file] [log] [blame]
// Copyright 2016 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/remoteplayback/RemotePlayback.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "core/dom/DOMException.h"
#include "core/dom/Document.h"
#include "core/events/Event.h"
#include "core/html/HTMLMediaElement.h"
#include "modules/EventTargetModules.h"
#include "modules/remoteplayback/RemotePlaybackAvailability.h"
#include "platform/UserGestureIndicator.h"
namespace blink {
namespace {
const AtomicString& remotePlaybackStateToString(WebRemotePlaybackState state)
{
DEFINE_STATIC_LOCAL(const AtomicString, connectedValue, ("connected"));
DEFINE_STATIC_LOCAL(const AtomicString, disconnectedValue, ("disconnected"));
switch (state) {
case WebRemotePlaybackState::Connected:
return connectedValue;
case WebRemotePlaybackState::Disconnected:
return disconnectedValue;
}
ASSERT_NOT_REACHED();
return disconnectedValue;
}
} // anonymous namespace
// static
RemotePlayback* RemotePlayback::create(HTMLMediaElement& element)
{
ASSERT(element.document().frame());
RemotePlayback* remotePlayback = new RemotePlayback(element);
element.setRemotePlaybackClient(remotePlayback);
return remotePlayback;
}
RemotePlayback::RemotePlayback(HTMLMediaElement& element)
: DOMWindowProperty(element.document().frame())
, m_state(element.isPlayingRemotely() ? WebRemotePlaybackState::Connected : WebRemotePlaybackState::Disconnected)
, m_availability(element.hasRemoteRoutes())
, m_mediaElement(&element)
{
}
const AtomicString& RemotePlayback::interfaceName() const
{
return EventTargetNames::RemotePlayback;
}
ExecutionContext* RemotePlayback::getExecutionContext() const
{
if (!m_frame)
return 0;
return m_frame->document();
}
ScriptPromise RemotePlayback::getAvailability(ScriptState* scriptState)
{
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
// TODO(avayvod): currently the availability is tracked for each media element
// as soon as it's created, we probably want to limit that to when the page/element
// is visible (see https://crbug.com/597281) and has default controls. If there's
// no default controls, we should also start tracking availability on demand
// meaning the Promise returned by getAvailability() will be resolved asynchronously.
RemotePlaybackAvailability* availability = RemotePlaybackAvailability::take(resolver, m_availability);
m_availabilityObjects.append(availability);
resolver->resolve(availability);
return promise;
}
ScriptPromise RemotePlayback::connect(ScriptState* scriptState)
{
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
// TODO(avayvod): should we have a separate flag to disable the user gesture
// requirement (for tests) or reuse the one for the PresentationRequest::start()?
if (!UserGestureIndicator::utilizeUserGesture()) {
resolver->reject(DOMException::create(InvalidAccessError, "RemotePlayback::connect() requires user gesture."));
return promise;
}
if (m_state == WebRemotePlaybackState::Disconnected) {
m_connectPromiseResolvers.append(resolver);
m_mediaElement->requestRemotePlayback();
} else {
m_mediaElement->requestRemotePlaybackControl();
resolver->resolve(false);
}
return promise;
}
String RemotePlayback::state() const
{
return remotePlaybackStateToString(m_state);
}
void RemotePlayback::stateChanged(WebRemotePlaybackState state)
{
// We may get a "disconnected" state change while in the "disconnected"
// state if initiated connection fails. So cleanup the promise resolvers
// before checking if anything changed.
// TODO(avayvod): cleanup this logic when we implementing the "connecting"
// state.
if (state != WebRemotePlaybackState::Disconnected) {
for (auto& resolver : m_connectPromiseResolvers)
resolver->resolve(true);
} else {
for (auto& resolver : m_connectPromiseResolvers)
resolver->reject(DOMException::create(AbortError, "Failed to connect to the remote device."));
}
m_connectPromiseResolvers.clear();
if (m_state == state)
return;
m_state = state;
dispatchEvent(Event::create(EventTypeNames::statechange));
}
void RemotePlayback::availabilityChanged(bool available)
{
if (m_availability == available)
return;
m_availability = available;
for (auto& availabilityObject : m_availabilityObjects)
availabilityObject->availabilityChanged(available);
}
void RemotePlayback::connectCancelled()
{
for (auto& resolver : m_connectPromiseResolvers)
resolver->resolve(false);
m_connectPromiseResolvers.clear();
}
DEFINE_TRACE(RemotePlayback)
{
visitor->trace(m_availabilityObjects);
visitor->trace(m_mediaElement);
visitor->trace(m_connectPromiseResolvers);
EventTargetWithInlineData::trace(visitor);
DOMWindowProperty::trace(visitor);
}
} // namespace blink