Commit 0fc8b636 authored by avayvod's avatar avayvod Committed by Commit bot

[Blink, RemotePlayback] Reject the prompt() with OperationError if there's a...

[Blink, RemotePlayback] Reject the prompt() with OperationError if there's a pending promise for this element.

BUG=647441
TEST=avayvod.github.io/remote-playback/test.html + layout test

Review-Url: https://codereview.chromium.org/2347763002
Cr-Commit-Position: refs/heads/master@{#422810}
parent f963ad86
<!DOCTYPE html>
<html>
<head>
<title>Test that calling prompt() before the promise returned by the previous call is resolved throws an exception</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../media-file.js"></script>
</head>
<body>
<button id="button">Click me</button>
<script>
function clickOnElem(e) {
eventSender.mouseMoveTo(
e.offsetLeft + e.offsetWidth / 2,
e.offsetTop + e.offsetHeight / 2);
eventSender.mouseDown();
eventSender.mouseUp();
}
async_test(function(test)
{
var v = document.createElement('video');
v.src = findMediaFile('video', 'content/test');
document.body.appendChild(v);
var btn = document.getElementById('button');
btn.onclick = function() {
v.remote.prompt();
btn.onclick = function() {
v.remote.prompt().then(
test.unreached_func(),
test.step_func_done(function(e) {
assert_equals(e.name, 'OperationError');
assert_equals(e.message, 'A prompt is already being shown for this media element.');
}));
btn.onclick = null;
};
clickOnElem(btn);
}
clickOnElem(btn);
}, 'Test that calling prompt() before the promise returned by the previous call is resolved throws an exception.');
</script>
</body>
</html>
...@@ -2194,13 +2194,13 @@ void HTMLMediaElement::pauseInternal() { ...@@ -2194,13 +2194,13 @@ void HTMLMediaElement::pauseInternal() {
} }
void HTMLMediaElement::requestRemotePlayback() { void HTMLMediaElement::requestRemotePlayback() {
DCHECK(m_remoteRoutesAvailable); if (webMediaPlayer())
webMediaPlayer()->requestRemotePlayback(); webMediaPlayer()->requestRemotePlayback();
} }
void HTMLMediaElement::requestRemotePlaybackControl() { void HTMLMediaElement::requestRemotePlaybackControl() {
DCHECK(m_remoteRoutesAvailable); if (webMediaPlayer())
webMediaPlayer()->requestRemotePlaybackControl(); webMediaPlayer()->requestRemotePlaybackControl();
} }
void HTMLMediaElement::closeMediaSource() { void HTMLMediaElement::closeMediaSource() {
......
...@@ -89,8 +89,13 @@ ScriptPromise RemotePlayback::prompt(ScriptState* scriptState) { ...@@ -89,8 +89,13 @@ ScriptPromise RemotePlayback::prompt(ScriptState* scriptState) {
return promise; return promise;
} }
// TODO(avayvod): should we have a separate flag to disable the user gesture if (m_promptPromiseResolver) {
// requirement (for tests) or reuse the one for the PresentationRequest::start()? resolver->reject(DOMException::create(
OperationError,
"A prompt is already being shown for this media element."));
return promise;
}
if (!UserGestureIndicator::utilizeUserGesture()) { if (!UserGestureIndicator::utilizeUserGesture()) {
resolver->reject(DOMException::create( resolver->reject(DOMException::create(
InvalidAccessError, "RemotePlayback::prompt() requires user gesture.")); InvalidAccessError, "RemotePlayback::prompt() requires user gesture."));
...@@ -98,7 +103,7 @@ ScriptPromise RemotePlayback::prompt(ScriptState* scriptState) { ...@@ -98,7 +103,7 @@ ScriptPromise RemotePlayback::prompt(ScriptState* scriptState) {
} }
if (m_state == WebRemotePlaybackState::Disconnected) { if (m_state == WebRemotePlaybackState::Disconnected) {
m_promptPromiseResolvers.append(resolver); m_promptPromiseResolver = resolver;
m_mediaElement->requestRemotePlayback(); m_mediaElement->requestRemotePlayback();
} else { } else {
m_mediaElement->requestRemotePlaybackControl(); m_mediaElement->requestRemotePlaybackControl();
...@@ -118,7 +123,7 @@ String RemotePlayback::state() const { ...@@ -118,7 +123,7 @@ String RemotePlayback::state() const {
bool RemotePlayback::hasPendingActivity() const { bool RemotePlayback::hasPendingActivity() const {
return hasEventListeners() || !m_availabilityObjects.isEmpty() || return hasEventListeners() || !m_availabilityObjects.isEmpty() ||
!m_promptPromiseResolvers.isEmpty(); m_promptPromiseResolver;
} }
void RemotePlayback::stateChanged(WebRemotePlaybackState state) { void RemotePlayback::stateChanged(WebRemotePlaybackState state) {
...@@ -127,15 +132,14 @@ void RemotePlayback::stateChanged(WebRemotePlaybackState state) { ...@@ -127,15 +132,14 @@ void RemotePlayback::stateChanged(WebRemotePlaybackState state) {
// before checking if anything changed. // before checking if anything changed.
// TODO(avayvod): cleanup this logic when we implementing the "connecting" // TODO(avayvod): cleanup this logic when we implementing the "connecting"
// state. // state.
if (state != WebRemotePlaybackState::Disconnected) { if (m_promptPromiseResolver) {
for (auto& resolver : m_promptPromiseResolvers) if (state != WebRemotePlaybackState::Disconnected)
resolver->resolve(); m_promptPromiseResolver->resolve();
} else { else
for (auto& resolver : m_promptPromiseResolvers) m_promptPromiseResolver->reject(DOMException::create(
resolver->reject(DOMException::create(
AbortError, "Failed to connect to the remote device.")); AbortError, "Failed to connect to the remote device."));
m_promptPromiseResolver = nullptr;
} }
m_promptPromiseResolvers.clear();
if (m_state == state) if (m_state == state)
return; return;
...@@ -154,14 +158,16 @@ void RemotePlayback::availabilityChanged(bool available) { ...@@ -154,14 +158,16 @@ void RemotePlayback::availabilityChanged(bool available) {
} }
void RemotePlayback::promptCancelled() { void RemotePlayback::promptCancelled() {
for (auto& resolver : m_promptPromiseResolvers) if (!m_promptPromiseResolver)
resolver->resolve(); return;
m_promptPromiseResolvers.clear();
m_promptPromiseResolver->resolve();
m_promptPromiseResolver = nullptr;
} }
DEFINE_TRACE(RemotePlayback) { DEFINE_TRACE(RemotePlayback) {
visitor->trace(m_availabilityObjects); visitor->trace(m_availabilityObjects);
visitor->trace(m_promptPromiseResolvers); visitor->trace(m_promptPromiseResolver);
visitor->trace(m_mediaElement); visitor->trace(m_mediaElement);
EventTargetWithInlineData::trace(visitor); EventTargetWithInlineData::trace(visitor);
} }
......
...@@ -59,7 +59,7 @@ class RemotePlayback final : public EventTargetWithInlineData, ...@@ -59,7 +59,7 @@ class RemotePlayback final : public EventTargetWithInlineData,
bool m_availability; bool m_availability;
HeapVector<Member<RemotePlaybackAvailability>> m_availabilityObjects; HeapVector<Member<RemotePlaybackAvailability>> m_availabilityObjects;
Member<HTMLMediaElement> m_mediaElement; Member<HTMLMediaElement> m_mediaElement;
HeapVector<Member<ScriptPromiseResolver>> m_promptPromiseResolvers; Member<ScriptPromiseResolver> m_promptPromiseResolver;
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment