OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "modules/remoteplayback/RemotePlayback.h" | 5 #include "modules/remoteplayback/RemotePlayback.h" |
6 | 6 |
7 #include "bindings/core/v8/ScriptPromiseResolver.h" | 7 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| 8 #include "bindings/modules/v8/RemotePlaybackAvailabilityCallback.h" |
8 #include "core/HTMLNames.h" | 9 #include "core/HTMLNames.h" |
9 #include "core/dom/DOMException.h" | 10 #include "core/dom/DOMException.h" |
10 #include "core/dom/Document.h" | 11 #include "core/dom/Document.h" |
| 12 #include "core/dom/ExecutionContextTask.h" |
11 #include "core/events/Event.h" | 13 #include "core/events/Event.h" |
12 #include "core/html/HTMLMediaElement.h" | 14 #include "core/html/HTMLMediaElement.h" |
13 #include "modules/EventTargetModules.h" | 15 #include "modules/EventTargetModules.h" |
14 #include "modules/remoteplayback/RemotePlaybackAvailability.h" | |
15 #include "platform/UserGestureIndicator.h" | 16 #include "platform/UserGestureIndicator.h" |
16 | 17 |
17 namespace blink { | 18 namespace blink { |
18 | 19 |
19 namespace { | 20 namespace { |
20 | 21 |
21 const AtomicString& remotePlaybackStateToString(WebRemotePlaybackState state) { | 22 const AtomicString& remotePlaybackStateToString(WebRemotePlaybackState state) { |
22 DEFINE_STATIC_LOCAL(const AtomicString, connectingValue, ("connecting")); | 23 DEFINE_STATIC_LOCAL(const AtomicString, connectingValue, ("connecting")); |
23 DEFINE_STATIC_LOCAL(const AtomicString, connectedValue, ("connected")); | 24 DEFINE_STATIC_LOCAL(const AtomicString, connectedValue, ("connected")); |
24 DEFINE_STATIC_LOCAL(const AtomicString, disconnectedValue, ("disconnected")); | 25 DEFINE_STATIC_LOCAL(const AtomicString, disconnectedValue, ("disconnected")); |
25 | 26 |
26 switch (state) { | 27 switch (state) { |
27 case WebRemotePlaybackState::Connecting: | 28 case WebRemotePlaybackState::Connecting: |
28 return connectingValue; | 29 return connectingValue; |
29 case WebRemotePlaybackState::Connected: | 30 case WebRemotePlaybackState::Connected: |
30 return connectedValue; | 31 return connectedValue; |
31 case WebRemotePlaybackState::Disconnected: | 32 case WebRemotePlaybackState::Disconnected: |
32 return disconnectedValue; | 33 return disconnectedValue; |
33 } | 34 } |
34 | 35 |
35 ASSERT_NOT_REACHED(); | 36 ASSERT_NOT_REACHED(); |
36 return disconnectedValue; | 37 return disconnectedValue; |
37 } | 38 } |
38 | 39 |
39 } // anonymous namespace | 40 } // anonymous namespace |
40 | 41 |
41 // static | 42 // static |
42 RemotePlayback* RemotePlayback::create(HTMLMediaElement& element) { | 43 RemotePlayback* RemotePlayback::create(ScriptState* scriptState, |
43 ASSERT(element.document().frame()); | 44 HTMLMediaElement& element) { |
| 45 DCHECK(element.document().frame()); |
| 46 DCHECK(scriptState); |
44 | 47 |
45 RemotePlayback* remotePlayback = new RemotePlayback(element); | 48 RemotePlayback* remotePlayback = new RemotePlayback(scriptState, element); |
46 element.setRemotePlaybackClient(remotePlayback); | 49 element.setRemotePlaybackClient(remotePlayback); |
47 | 50 |
48 return remotePlayback; | 51 return remotePlayback; |
49 } | 52 } |
50 | 53 |
51 RemotePlayback::RemotePlayback(HTMLMediaElement& element) | 54 RemotePlayback::RemotePlayback(ScriptState* scriptState, |
| 55 HTMLMediaElement& element) |
52 : ActiveScriptWrappable(this), | 56 : ActiveScriptWrappable(this), |
| 57 m_scriptState(scriptState), |
53 m_state(element.isPlayingRemotely() | 58 m_state(element.isPlayingRemotely() |
54 ? WebRemotePlaybackState::Connected | 59 ? WebRemotePlaybackState::Connected |
55 : WebRemotePlaybackState::Disconnected), | 60 : WebRemotePlaybackState::Disconnected), |
56 m_availability(element.hasRemoteRoutes()), | 61 m_availability(element.hasRemoteRoutes()), |
57 m_mediaElement(&element) {} | 62 m_mediaElement(&element) {} |
58 | 63 |
59 const AtomicString& RemotePlayback::interfaceName() const { | 64 const AtomicString& RemotePlayback::interfaceName() const { |
60 return EventTargetNames::RemotePlayback; | 65 return EventTargetNames::RemotePlayback; |
61 } | 66 } |
62 | 67 |
63 ExecutionContext* RemotePlayback::getExecutionContext() const { | 68 ExecutionContext* RemotePlayback::getExecutionContext() const { |
64 return &m_mediaElement->document(); | 69 return &m_mediaElement->document(); |
65 } | 70 } |
66 | 71 |
67 ScriptPromise RemotePlayback::getAvailability(ScriptState* scriptState) { | 72 ScriptPromise RemotePlayback::watchAvailability( |
68 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); | 73 RemotePlaybackAvailabilityCallback* callback) { |
| 74 ScriptPromiseResolver* resolver = |
| 75 ScriptPromiseResolver::create(m_scriptState.get()); |
69 ScriptPromise promise = resolver->promise(); | 76 ScriptPromise promise = resolver->promise(); |
70 | 77 |
| 78 if (m_mediaElement->fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| 79 resolver->reject(DOMException::create( |
| 80 InvalidStateError, "disableRemotePlayback attribute is present.")); |
| 81 return promise; |
| 82 } |
| 83 |
| 84 // TODO(avayvod): implement steps 4 and 5 of the algorithm. |
| 85 // https://crbug.com/655233 |
| 86 int id; |
| 87 do { |
| 88 id = getExecutionContext()->circularSequentialID(); |
| 89 } while (!m_availabilityCallbacks.add(id, callback).isNewEntry); |
| 90 |
| 91 // Report the current availability via the callback. |
| 92 getExecutionContext()->postTask( |
| 93 BLINK_FROM_HERE, |
| 94 createSameThreadTask(&RemotePlayback::notifyInitialAvailability, |
| 95 wrapPersistent(this), id), |
| 96 "watchAvailabilityCallback"); |
| 97 |
71 // TODO(avayvod): Currently the availability is tracked for each media element | 98 // TODO(avayvod): Currently the availability is tracked for each media element |
72 // as soon as it's created, we probably want to limit that to when the | 99 // as soon as it's created, we probably want to limit that to when the |
73 // page/element is visible (see https://crbug.com/597281) and has default | 100 // page/element is visible (see https://crbug.com/597281) and has default |
74 // controls. If there are no default controls, we should also start tracking | 101 // controls. If there are no default controls, we should also start tracking |
75 // availability on demand meaning the Promise returned by getAvailability() | 102 // availability on demand meaning the Promise returned by watchAvailability() |
76 // will be resolved asynchronously. | 103 // will be resolved asynchronously. |
77 RemotePlaybackAvailability* availability = | 104 resolver->resolve(id); |
78 RemotePlaybackAvailability::take(resolver, m_availability); | |
79 m_availabilityObjects.append(availability); | |
80 resolver->resolve(availability); | |
81 return promise; | 105 return promise; |
82 } | 106 } |
83 | 107 |
84 ScriptPromise RemotePlayback::prompt(ScriptState* scriptState) { | 108 ScriptPromise RemotePlayback::cancelWatchAvailability(int id) { |
85 // TODO(avayvod): implement steps 4, 5, 8, 9 of the algorithm. | 109 ScriptPromiseResolver* resolver = |
86 // https://crbug.com/647441 | 110 ScriptPromiseResolver::create(m_scriptState.get()); |
87 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); | |
88 ScriptPromise promise = resolver->promise(); | 111 ScriptPromise promise = resolver->promise(); |
89 | 112 |
90 if (m_mediaElement->fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { | 113 if (m_mediaElement->fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| 114 resolver->reject(DOMException::create( |
| 115 InvalidStateError, "disableRemotePlayback attribute is present.")); |
| 116 return promise; |
| 117 } |
| 118 |
| 119 auto iter = m_availabilityCallbacks.find(id); |
| 120 if (iter == m_availabilityCallbacks.end()) { |
| 121 resolver->reject(DOMException::create( |
| 122 NotFoundError, "A callback with the given id is not found.")); |
| 123 return promise; |
| 124 } |
| 125 |
| 126 m_availabilityCallbacks.remove(iter); |
| 127 |
| 128 resolver->resolve(); |
| 129 return promise; |
| 130 } |
| 131 |
| 132 ScriptPromise RemotePlayback::cancelWatchAvailability() { |
| 133 ScriptPromiseResolver* resolver = |
| 134 ScriptPromiseResolver::create(m_scriptState.get()); |
| 135 ScriptPromise promise = resolver->promise(); |
| 136 |
| 137 if (m_mediaElement->fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| 138 resolver->reject(DOMException::create( |
| 139 InvalidStateError, "disableRemotePlayback attribute is present.")); |
| 140 return promise; |
| 141 } |
| 142 |
| 143 m_availabilityCallbacks.clear(); |
| 144 |
| 145 resolver->resolve(); |
| 146 return promise; |
| 147 } |
| 148 |
| 149 ScriptPromise RemotePlayback::prompt() { |
| 150 // TODO(avayvod): implement steps 5, 8, 9 of the algorithm. |
| 151 // https://crbug.com/647441 |
| 152 ScriptPromiseResolver* resolver = |
| 153 ScriptPromiseResolver::create(m_scriptState.get()); |
| 154 ScriptPromise promise = resolver->promise(); |
| 155 |
| 156 if (m_mediaElement->fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
91 resolver->reject(DOMException::create( | 157 resolver->reject(DOMException::create( |
92 InvalidStateError, "disableRemotePlayback attribute is present.")); | 158 InvalidStateError, "disableRemotePlayback attribute is present.")); |
93 return promise; | 159 return promise; |
94 } | 160 } |
95 | 161 |
96 if (m_promptPromiseResolver) { | 162 if (m_promptPromiseResolver) { |
97 resolver->reject(DOMException::create( | 163 resolver->reject(DOMException::create( |
98 OperationError, | 164 OperationError, |
99 "A prompt is already being shown for this media element.")); | 165 "A prompt is already being shown for this media element.")); |
100 return promise; | 166 return promise; |
(...skipping 18 matching lines...) Expand all Loading... |
119 } | 185 } |
120 | 186 |
121 return promise; | 187 return promise; |
122 } | 188 } |
123 | 189 |
124 String RemotePlayback::state() const { | 190 String RemotePlayback::state() const { |
125 return remotePlaybackStateToString(m_state); | 191 return remotePlaybackStateToString(m_state); |
126 } | 192 } |
127 | 193 |
128 bool RemotePlayback::hasPendingActivity() const { | 194 bool RemotePlayback::hasPendingActivity() const { |
129 return hasEventListeners() || !m_availabilityObjects.isEmpty() || | 195 return hasEventListeners() || !m_availabilityCallbacks.isEmpty() || |
130 m_promptPromiseResolver; | 196 m_promptPromiseResolver; |
131 } | 197 } |
132 | 198 |
| 199 void RemotePlayback::notifyInitialAvailability(int callbackId) { |
| 200 // May not find the callback if the website cancels it fast enough. |
| 201 auto iter = m_availabilityCallbacks.find(callbackId); |
| 202 if (iter == m_availabilityCallbacks.end()) |
| 203 return; |
| 204 |
| 205 iter->value->call(m_scriptState.get(), this, m_availability); |
| 206 } |
| 207 |
133 void RemotePlayback::stateChanged(WebRemotePlaybackState state) { | 208 void RemotePlayback::stateChanged(WebRemotePlaybackState state) { |
134 // We may get a "disconnected" state change while in the "disconnected" | 209 // We may get a "disconnected" state change while in the "disconnected" |
135 // state if initiated connection fails. So cleanup the promise resolvers | 210 // state if initiated connection fails. So cleanup the promise resolvers |
136 // before checking if anything changed. | 211 // before checking if anything changed. |
137 // TODO(avayvod): cleanup this logic when we implementing the "connecting" | 212 // TODO(avayvod): cleanup this logic when we implementing the "connecting" |
138 // state. | 213 // state. |
139 if (m_promptPromiseResolver) { | 214 if (m_promptPromiseResolver) { |
140 if (state != WebRemotePlaybackState::Disconnected) | 215 if (state != WebRemotePlaybackState::Disconnected) |
141 m_promptPromiseResolver->resolve(); | 216 m_promptPromiseResolver->resolve(); |
142 else | 217 else |
(...skipping 17 matching lines...) Expand all Loading... |
160 dispatchEvent(Event::create(EventTypeNames::disconnect)); | 235 dispatchEvent(Event::create(EventTypeNames::disconnect)); |
161 break; | 236 break; |
162 } | 237 } |
163 } | 238 } |
164 | 239 |
165 void RemotePlayback::availabilityChanged(bool available) { | 240 void RemotePlayback::availabilityChanged(bool available) { |
166 if (m_availability == available) | 241 if (m_availability == available) |
167 return; | 242 return; |
168 | 243 |
169 m_availability = available; | 244 m_availability = available; |
170 for (auto& availabilityObject : m_availabilityObjects) | 245 for (auto& callback : m_availabilityCallbacks.values()) |
171 availabilityObject->availabilityChanged(available); | 246 callback->call(m_scriptState.get(), this, m_availability); |
172 } | 247 } |
173 | 248 |
174 void RemotePlayback::promptCancelled() { | 249 void RemotePlayback::promptCancelled() { |
175 if (!m_promptPromiseResolver) | 250 if (!m_promptPromiseResolver) |
176 return; | 251 return; |
177 | 252 |
178 m_promptPromiseResolver->reject( | 253 m_promptPromiseResolver->reject( |
179 DOMException::create(NotAllowedError, "The prompt was dismissed.")); | 254 DOMException::create(NotAllowedError, "The prompt was dismissed.")); |
180 m_promptPromiseResolver = nullptr; | 255 m_promptPromiseResolver = nullptr; |
181 } | 256 } |
182 | 257 |
| 258 void RemotePlayback::setV8ReferencesForCallbacks( |
| 259 v8::Isolate* isolate, |
| 260 const v8::Persistent<v8::Object>& wrapper) { |
| 261 for (auto callback : m_availabilityCallbacks.values()) |
| 262 callback->setWrapperReference(isolate, wrapper); |
| 263 } |
| 264 |
183 DEFINE_TRACE(RemotePlayback) { | 265 DEFINE_TRACE(RemotePlayback) { |
184 visitor->trace(m_availabilityObjects); | 266 visitor->trace(m_availabilityCallbacks); |
185 visitor->trace(m_promptPromiseResolver); | 267 visitor->trace(m_promptPromiseResolver); |
186 visitor->trace(m_mediaElement); | 268 visitor->trace(m_mediaElement); |
187 EventTargetWithInlineData::trace(visitor); | 269 EventTargetWithInlineData::trace(visitor); |
188 } | 270 } |
189 | 271 |
190 } // namespace blink | 272 } // namespace blink |
OLD | NEW |