| 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 "bindings/modules/v8/RemotePlaybackAvailabilityCallback.h" |
| 9 #include "core/HTMLNames.h" | 9 #include "core/HTMLNames.h" |
| 10 #include "core/dom/DOMException.h" | 10 #include "core/dom/DOMException.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 } | 40 } |
| 41 | 41 |
| 42 void RunNotifyInitialAvailabilityTask(ExecutionContext* context, | 42 void RunNotifyInitialAvailabilityTask(ExecutionContext* context, |
| 43 std::unique_ptr<WTF::Closure> task) { | 43 std::unique_ptr<WTF::Closure> task) { |
| 44 probe::AsyncTask async_task(context, task.get()); | 44 probe::AsyncTask async_task(context, task.get()); |
| 45 (*task)(); | 45 (*task)(); |
| 46 } | 46 } |
| 47 | 47 |
| 48 } // anonymous namespace | 48 } // anonymous namespace |
| 49 | 49 |
| 50 RemotePlayback::AvailabilityCallback::AvailabilityCallback( |
| 51 RemotePlaybackAvailabilityCallback* callback) |
| 52 : bindings_cb_(this, callback) {} |
| 53 |
| 54 RemotePlayback::AvailabilityCallback::AvailabilityCallback( |
| 55 std::unique_ptr<WTF::Closure> callback) |
| 56 : bindings_cb_(nullptr, nullptr), internal_cb_(std::move(callback)) {} |
| 57 |
| 58 void RemotePlayback::AvailabilityCallback::Run(RemotePlayback* remote_playback, |
| 59 bool new_availability) { |
| 60 if (internal_cb_) { |
| 61 DCHECK(!bindings_cb_); |
| 62 (*internal_cb_.get())(); |
| 63 return; |
| 64 } |
| 65 |
| 66 bindings_cb_->call(remote_playback, new_availability); |
| 67 } |
| 68 |
| 69 DEFINE_TRACE_WRAPPERS(RemotePlayback::AvailabilityCallback) { |
| 70 visitor->TraceWrappers(bindings_cb_); |
| 71 } |
| 72 |
| 50 // static | 73 // static |
| 51 RemotePlayback* RemotePlayback::Create(HTMLMediaElement& element) { | 74 RemotePlayback* RemotePlayback::Create(HTMLMediaElement& element) { |
| 52 return new RemotePlayback(element); | 75 return new RemotePlayback(element); |
| 53 } | 76 } |
| 54 | 77 |
| 55 RemotePlayback::RemotePlayback(HTMLMediaElement& element) | 78 RemotePlayback::RemotePlayback(HTMLMediaElement& element) |
| 56 : state_(element.IsPlayingRemotely() | 79 : state_(element.IsPlayingRemotely() |
| 57 ? WebRemotePlaybackState::kConnected | 80 ? WebRemotePlaybackState::kConnected |
| 58 : WebRemotePlaybackState::kDisconnected), | 81 : WebRemotePlaybackState::kDisconnected), |
| 59 availability_(WebRemotePlaybackAvailability::kUnknown), | 82 availability_(WebRemotePlaybackAvailability::kUnknown), |
| (...skipping 19 matching lines...) Expand all Loading... |
| 79 return promise; | 102 return promise; |
| 80 } | 103 } |
| 81 | 104 |
| 82 if (MemoryCoordinator::IsLowEndDevice()) { | 105 if (MemoryCoordinator::IsLowEndDevice()) { |
| 83 resolver->Reject(DOMException::Create( | 106 resolver->Reject(DOMException::Create( |
| 84 kNotSupportedError, | 107 kNotSupportedError, |
| 85 "Availability monitoring is not supported on this device.")); | 108 "Availability monitoring is not supported on this device.")); |
| 86 return promise; | 109 return promise; |
| 87 } | 110 } |
| 88 | 111 |
| 89 int id; | 112 int id = WatchAvailabilityInternal(new AvailabilityCallback(callback)); |
| 90 do { | |
| 91 id = GetExecutionContext()->CircularSequentialID(); | |
| 92 } while ( | |
| 93 !availability_callbacks_ | |
| 94 .insert(id, TraceWrapperMember<RemotePlaybackAvailabilityCallback>( | |
| 95 this, callback)) | |
| 96 .is_new_entry); | |
| 97 | |
| 98 // Report the current availability via the callback. | |
| 99 // TODO(yuryu): Wrapping notifyInitialAvailability with WTF::Closure as | |
| 100 // InspectorInstrumentation requires a globally unique pointer to track tasks. | |
| 101 // We can remove the wrapper if InspectorInstrumentation returns a task id. | |
| 102 std::unique_ptr<WTF::Closure> task = WTF::Bind( | |
| 103 &RemotePlayback::NotifyInitialAvailability, WrapPersistent(this), id); | |
| 104 probe::AsyncTaskScheduled(GetExecutionContext(), "watchAvailabilityCallback", | |
| 105 task.get()); | |
| 106 TaskRunnerHelper::Get(TaskType::kMediaElementEvent, GetExecutionContext()) | |
| 107 ->PostTask(BLINK_FROM_HERE, | |
| 108 WTF::Bind(RunNotifyInitialAvailabilityTask, | |
| 109 WrapPersistent(GetExecutionContext()), | |
| 110 WTF::Passed(std::move(task)))); | |
| 111 | 113 |
| 112 // TODO(avayvod): Currently the availability is tracked for each media element | 114 // TODO(avayvod): Currently the availability is tracked for each media element |
| 113 // as soon as it's created, we probably want to limit that to when the | 115 // as soon as it's created, we probably want to limit that to when the |
| 114 // page/element is visible (see https://crbug.com/597281) and has default | 116 // page/element is visible (see https://crbug.com/597281) and has default |
| 115 // controls. If there are no default controls, we should also start tracking | 117 // controls. If there are no default controls, we should also start tracking |
| 116 // availability on demand meaning the Promise returned by watchAvailability() | 118 // availability on demand meaning the Promise returned by watchAvailability() |
| 117 // will be resolved asynchronously. | 119 // will be resolved asynchronously. |
| 118 resolver->Resolve(id); | 120 resolver->Resolve(id); |
| 119 return promise; | 121 return promise; |
| 120 } | 122 } |
| 121 | 123 |
| 122 ScriptPromise RemotePlayback::cancelWatchAvailability(ScriptState* script_state, | 124 ScriptPromise RemotePlayback::cancelWatchAvailability(ScriptState* script_state, |
| 123 int id) { | 125 int id) { |
| 124 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 126 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
| 125 ScriptPromise promise = resolver->Promise(); | 127 ScriptPromise promise = resolver->Promise(); |
| 126 | 128 |
| 127 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { | 129 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| 128 resolver->Reject(DOMException::Create( | 130 resolver->Reject(DOMException::Create( |
| 129 kInvalidStateError, "disableRemotePlayback attribute is present.")); | 131 kInvalidStateError, "disableRemotePlayback attribute is present.")); |
| 130 return promise; | 132 return promise; |
| 131 } | 133 } |
| 132 | 134 |
| 133 auto iter = availability_callbacks_.Find(id); | 135 if (!CancelWatchAvailabilityInternal(id)) { |
| 134 if (iter == availability_callbacks_.end()) { | |
| 135 resolver->Reject(DOMException::Create( | 136 resolver->Reject(DOMException::Create( |
| 136 kNotFoundError, "A callback with the given id is not found.")); | 137 kNotFoundError, "A callback with the given id is not found.")); |
| 137 return promise; | 138 return promise; |
| 138 } | 139 } |
| 139 | 140 |
| 140 availability_callbacks_.erase(iter); | |
| 141 | |
| 142 resolver->Resolve(); | 141 resolver->Resolve(); |
| 143 return promise; | 142 return promise; |
| 144 } | 143 } |
| 145 | 144 |
| 146 ScriptPromise RemotePlayback::cancelWatchAvailability( | 145 ScriptPromise RemotePlayback::cancelWatchAvailability( |
| 147 ScriptState* script_state) { | 146 ScriptState* script_state) { |
| 148 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 147 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
| 149 ScriptPromise promise = resolver->Promise(); | 148 ScriptPromise promise = resolver->Promise(); |
| 150 | 149 |
| 151 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { | 150 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 } | 192 } |
| 194 | 193 |
| 195 if (availability_ == WebRemotePlaybackAvailability::kSourceNotSupported || | 194 if (availability_ == WebRemotePlaybackAvailability::kSourceNotSupported || |
| 196 availability_ == WebRemotePlaybackAvailability::kSourceNotCompatible) { | 195 availability_ == WebRemotePlaybackAvailability::kSourceNotCompatible) { |
| 197 resolver->Reject(DOMException::Create( | 196 resolver->Reject(DOMException::Create( |
| 198 kNotSupportedError, | 197 kNotSupportedError, |
| 199 "The currentSrc is not compatible with remote playback")); | 198 "The currentSrc is not compatible with remote playback")); |
| 200 return promise; | 199 return promise; |
| 201 } | 200 } |
| 202 | 201 |
| 203 if (state_ == WebRemotePlaybackState::kDisconnected) { | 202 prompt_promise_resolver_ = resolver; |
| 204 prompt_promise_resolver_ = resolver; | 203 PromptInternal(); |
| 205 media_element_->RequestRemotePlayback(); | |
| 206 } else { | |
| 207 prompt_promise_resolver_ = resolver; | |
| 208 media_element_->RequestRemotePlaybackControl(); | |
| 209 } | |
| 210 | 204 |
| 211 return promise; | 205 return promise; |
| 212 } | 206 } |
| 213 | 207 |
| 214 String RemotePlayback::state() const { | 208 String RemotePlayback::state() const { |
| 215 return RemotePlaybackStateToString(state_); | 209 return RemotePlaybackStateToString(state_); |
| 216 } | 210 } |
| 217 | 211 |
| 218 bool RemotePlayback::HasPendingActivity() const { | 212 bool RemotePlayback::HasPendingActivity() const { |
| 219 return HasEventListeners() || !availability_callbacks_.IsEmpty() || | 213 return HasEventListeners() || !availability_callbacks_.IsEmpty() || |
| 220 prompt_promise_resolver_; | 214 prompt_promise_resolver_; |
| 221 } | 215 } |
| 222 | 216 |
| 217 void RemotePlayback::PromptInternal() { |
| 218 if (state_ == WebRemotePlaybackState::kDisconnected) |
| 219 media_element_->RequestRemotePlayback(); |
| 220 else |
| 221 media_element_->RequestRemotePlaybackControl(); |
| 222 } |
| 223 |
| 224 int RemotePlayback::WatchAvailabilityInternal(AvailabilityCallback* callback) { |
| 225 int id; |
| 226 do { |
| 227 id = GetExecutionContext()->CircularSequentialID(); |
| 228 } while (!availability_callbacks_.insert(id, callback).is_new_entry); |
| 229 |
| 230 // Report the current availability via the callback. |
| 231 // TODO(yuryu): Wrapping notifyInitialAvailability with WTF::Closure as |
| 232 // InspectorInstrumentation requires a globally unique pointer to track tasks. |
| 233 // We can remove the wrapper if InspectorInstrumentation returns a task id. |
| 234 std::unique_ptr<WTF::Closure> task = WTF::Bind( |
| 235 &RemotePlayback::NotifyInitialAvailability, WrapPersistent(this), id); |
| 236 probe::AsyncTaskScheduled(GetExecutionContext(), "watchAvailabilityCallback", |
| 237 task.get()); |
| 238 TaskRunnerHelper::Get(TaskType::kMediaElementEvent, GetExecutionContext()) |
| 239 ->PostTask(BLINK_FROM_HERE, |
| 240 WTF::Bind(RunNotifyInitialAvailabilityTask, |
| 241 WrapPersistent(GetExecutionContext()), |
| 242 WTF::Passed(std::move(task)))); |
| 243 return id; |
| 244 } |
| 245 |
| 246 bool RemotePlayback::CancelWatchAvailabilityInternal(int id) { |
| 247 auto iter = availability_callbacks_.Find(id); |
| 248 if (iter == availability_callbacks_.end()) |
| 249 return false; |
| 250 availability_callbacks_.erase(iter); |
| 251 return true; |
| 252 } |
| 253 |
| 223 void RemotePlayback::NotifyInitialAvailability(int callback_id) { | 254 void RemotePlayback::NotifyInitialAvailability(int callback_id) { |
| 224 // May not find the callback if the website cancels it fast enough. | 255 // May not find the callback if the website cancels it fast enough. |
| 225 auto iter = availability_callbacks_.Find(callback_id); | 256 auto iter = availability_callbacks_.Find(callback_id); |
| 226 if (iter == availability_callbacks_.end()) | 257 if (iter == availability_callbacks_.end()) |
| 227 return; | 258 return; |
| 228 | 259 |
| 229 iter->value->call(this, RemotePlaybackAvailable()); | 260 iter->value->Run(this, RemotePlaybackAvailable()); |
| 230 } | 261 } |
| 231 | 262 |
| 232 void RemotePlayback::StateChanged(WebRemotePlaybackState state) { | 263 void RemotePlayback::StateChanged(WebRemotePlaybackState state) { |
| 233 if (state_ == state) | 264 if (state_ == state) |
| 234 return; | 265 return; |
| 235 | 266 |
| 236 if (prompt_promise_resolver_) { | 267 if (prompt_promise_resolver_) { |
| 237 // Changing state to Disconnected from "disconnected" or "connecting" means | 268 // Changing state to Disconnected from "disconnected" or "connecting" means |
| 238 // that establishing connection with remote playback device failed. | 269 // that establishing connection with remote playback device failed. |
| 239 // Changing state to anything else means the state change intended by | 270 // Changing state to anything else means the state change intended by |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 if (availability_ == availability) | 302 if (availability_ == availability) |
| 272 return; | 303 return; |
| 273 | 304 |
| 274 bool old_availability = RemotePlaybackAvailable(); | 305 bool old_availability = RemotePlaybackAvailable(); |
| 275 availability_ = availability; | 306 availability_ = availability; |
| 276 bool new_availability = RemotePlaybackAvailable(); | 307 bool new_availability = RemotePlaybackAvailable(); |
| 277 if (new_availability == old_availability) | 308 if (new_availability == old_availability) |
| 278 return; | 309 return; |
| 279 | 310 |
| 280 for (auto& callback : availability_callbacks_.Values()) | 311 for (auto& callback : availability_callbacks_.Values()) |
| 281 callback->call(this, new_availability); | 312 callback->Run(this, new_availability); |
| 282 } | 313 } |
| 283 | 314 |
| 284 void RemotePlayback::PromptCancelled() { | 315 void RemotePlayback::PromptCancelled() { |
| 285 if (!prompt_promise_resolver_) | 316 if (!prompt_promise_resolver_) |
| 286 return; | 317 return; |
| 287 | 318 |
| 288 prompt_promise_resolver_->Reject( | 319 prompt_promise_resolver_->Reject( |
| 289 DOMException::Create(kNotAllowedError, "The prompt was dismissed.")); | 320 DOMException::Create(kNotAllowedError, "The prompt was dismissed.")); |
| 290 prompt_promise_resolver_ = nullptr; | 321 prompt_promise_resolver_ = nullptr; |
| 291 } | 322 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 307 media_element_->RequestRemotePlaybackStop(); | 338 media_element_->RequestRemotePlaybackStop(); |
| 308 } | 339 } |
| 309 | 340 |
| 310 DEFINE_TRACE(RemotePlayback) { | 341 DEFINE_TRACE(RemotePlayback) { |
| 311 visitor->Trace(availability_callbacks_); | 342 visitor->Trace(availability_callbacks_); |
| 312 visitor->Trace(prompt_promise_resolver_); | 343 visitor->Trace(prompt_promise_resolver_); |
| 313 visitor->Trace(media_element_); | 344 visitor->Trace(media_element_); |
| 314 EventTargetWithInlineData::Trace(visitor); | 345 EventTargetWithInlineData::Trace(visitor); |
| 315 } | 346 } |
| 316 | 347 |
| 317 DEFINE_TRACE_WRAPPERS(RemotePlayback) { | |
| 318 for (auto callback : availability_callbacks_.Values()) { | |
| 319 visitor->TraceWrappers(callback); | |
| 320 } | |
| 321 EventTargetWithInlineData::TraceWrappers(visitor); | |
| 322 } | |
| 323 | |
| 324 } // namespace blink | 348 } // namespace blink |
| OLD | NEW |