Chromium Code Reviews| 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" |
| 11 #include "core/dom/Document.h" | 11 #include "core/dom/Document.h" |
| 12 #include "core/dom/TaskRunnerHelper.h" | 12 #include "core/dom/TaskRunnerHelper.h" |
| 13 #include "core/dom/UserGestureIndicator.h" | 13 #include "core/dom/UserGestureIndicator.h" |
| 14 #include "core/events/Event.h" | 14 #include "core/events/Event.h" |
| 15 #include "core/html/HTMLMediaElement.h" | 15 #include "core/html/HTMLMediaElement.h" |
| 16 #include "core/html/HTMLVideoElement.h" | 16 #include "core/html/HTMLVideoElement.h" |
| 17 #include "core/probe/CoreProbes.h" | 17 #include "core/probe/CoreProbes.h" |
| 18 #include "modules/EventTargetModules.h" | 18 #include "modules/EventTargetModules.h" |
| 19 #include "modules/presentation/PresentationController.h" | |
| 19 #include "modules/remoteplayback/AvailabilityCallbackWrapper.h" | 20 #include "modules/remoteplayback/AvailabilityCallbackWrapper.h" |
| 20 #include "platform/MemoryCoordinator.h" | 21 #include "platform/MemoryCoordinator.h" |
| 22 #include "platform/json/JSONValues.h" | |
| 23 #include "platform/wtf/text/Base64.h" | |
| 21 | 24 |
| 22 namespace blink { | 25 namespace blink { |
| 23 | 26 |
| 24 namespace { | 27 namespace { |
| 25 | 28 |
| 26 const AtomicString& RemotePlaybackStateToString(WebRemotePlaybackState state) { | 29 const AtomicString& RemotePlaybackStateToString(WebRemotePlaybackState state) { |
| 27 DEFINE_STATIC_LOCAL(const AtomicString, connecting_value, ("connecting")); | 30 DEFINE_STATIC_LOCAL(const AtomicString, connecting_value, ("connecting")); |
| 28 DEFINE_STATIC_LOCAL(const AtomicString, connected_value, ("connected")); | 31 DEFINE_STATIC_LOCAL(const AtomicString, connected_value, ("connected")); |
| 29 DEFINE_STATIC_LOCAL(const AtomicString, disconnected_value, ("disconnected")); | 32 DEFINE_STATIC_LOCAL(const AtomicString, disconnected_value, ("disconnected")); |
| 30 | 33 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 52 // static | 55 // static |
| 53 RemotePlayback* RemotePlayback::Create(HTMLMediaElement& element) { | 56 RemotePlayback* RemotePlayback::Create(HTMLMediaElement& element) { |
| 54 return new RemotePlayback(element); | 57 return new RemotePlayback(element); |
| 55 } | 58 } |
| 56 | 59 |
| 57 RemotePlayback::RemotePlayback(HTMLMediaElement& element) | 60 RemotePlayback::RemotePlayback(HTMLMediaElement& element) |
| 58 : state_(element.IsPlayingRemotely() | 61 : state_(element.IsPlayingRemotely() |
| 59 ? WebRemotePlaybackState::kConnected | 62 ? WebRemotePlaybackState::kConnected |
| 60 : WebRemotePlaybackState::kDisconnected), | 63 : WebRemotePlaybackState::kDisconnected), |
| 61 availability_(WebRemotePlaybackAvailability::kUnknown), | 64 availability_(WebRemotePlaybackAvailability::kUnknown), |
| 62 media_element_(&element) {} | 65 media_element_(&element), |
| 66 is_listening_(false) {} | |
| 63 | 67 |
| 64 const AtomicString& RemotePlayback::InterfaceName() const { | 68 const AtomicString& RemotePlayback::InterfaceName() const { |
| 65 return EventTargetNames::RemotePlayback; | 69 return EventTargetNames::RemotePlayback; |
| 66 } | 70 } |
| 67 | 71 |
| 68 ExecutionContext* RemotePlayback::GetExecutionContext() const { | 72 ExecutionContext* RemotePlayback::GetExecutionContext() const { |
| 69 return &media_element_->GetDocument(); | 73 return &media_element_->GetDocument(); |
| 70 } | 74 } |
| 71 | 75 |
| 72 ScriptPromise RemotePlayback::watchAvailability( | 76 ScriptPromise RemotePlayback::watchAvailability( |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 131 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
| 128 ScriptPromise promise = resolver->Promise(); | 132 ScriptPromise promise = resolver->Promise(); |
| 129 | 133 |
| 130 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { | 134 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| 131 resolver->Reject(DOMException::Create( | 135 resolver->Reject(DOMException::Create( |
| 132 kInvalidStateError, "disableRemotePlayback attribute is present.")); | 136 kInvalidStateError, "disableRemotePlayback attribute is present.")); |
| 133 return promise; | 137 return promise; |
| 134 } | 138 } |
| 135 | 139 |
| 136 availability_callbacks_.clear(); | 140 availability_callbacks_.clear(); |
| 141 UpdateListeningState(); | |
| 137 | 142 |
| 138 resolver->Resolve(); | 143 resolver->Resolve(); |
| 139 return promise; | 144 return promise; |
| 140 } | 145 } |
| 141 | 146 |
| 142 ScriptPromise RemotePlayback::prompt(ScriptState* script_state) { | 147 ScriptPromise RemotePlayback::prompt(ScriptState* script_state) { |
| 143 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 148 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
| 144 ScriptPromise promise = resolver->Promise(); | 149 ScriptPromise promise = resolver->Promise(); |
| 145 | 150 |
| 146 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { | 151 if (media_element_->FastHasAttribute(HTMLNames::disableremoteplaybackAttr)) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 163 return promise; | 168 return promise; |
| 164 } | 169 } |
| 165 | 170 |
| 166 if (!RuntimeEnabledFeatures::remotePlaybackBackendEnabled() || | 171 if (!RuntimeEnabledFeatures::remotePlaybackBackendEnabled() || |
| 167 availability_ == WebRemotePlaybackAvailability::kDeviceNotAvailable) { | 172 availability_ == WebRemotePlaybackAvailability::kDeviceNotAvailable) { |
| 168 resolver->Reject(DOMException::Create(kNotFoundError, | 173 resolver->Reject(DOMException::Create(kNotFoundError, |
| 169 "No remote playback devices found.")); | 174 "No remote playback devices found.")); |
| 170 return promise; | 175 return promise; |
| 171 } | 176 } |
| 172 | 177 |
| 178 // TODO(avayvod): none of these two states is propagated with the new | |
| 179 // pipeline. | |
|
mark a. foltz
2017/05/31 21:16:41
So kNotSupported error will not be thrown going fo
| |
| 173 if (availability_ == WebRemotePlaybackAvailability::kSourceNotSupported || | 180 if (availability_ == WebRemotePlaybackAvailability::kSourceNotSupported || |
| 174 availability_ == WebRemotePlaybackAvailability::kSourceNotCompatible) { | 181 availability_ == WebRemotePlaybackAvailability::kSourceNotCompatible) { |
| 175 resolver->Reject(DOMException::Create( | 182 resolver->Reject(DOMException::Create( |
| 176 kNotSupportedError, | 183 kNotSupportedError, |
| 177 "The currentSrc is not compatible with remote playback")); | 184 "The currentSrc is not compatible with remote playback")); |
| 178 return promise; | 185 return promise; |
| 179 } | 186 } |
| 180 | 187 |
| 181 prompt_promise_resolver_ = resolver; | 188 prompt_promise_resolver_ = resolver; |
| 182 PromptInternal(); | 189 PromptInternal(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 // We can remove the wrapper if InspectorInstrumentation returns a task id. | 223 // We can remove the wrapper if InspectorInstrumentation returns a task id. |
| 217 std::unique_ptr<WTF::Closure> task = WTF::Bind( | 224 std::unique_ptr<WTF::Closure> task = WTF::Bind( |
| 218 &RemotePlayback::NotifyInitialAvailability, WrapPersistent(this), id); | 225 &RemotePlayback::NotifyInitialAvailability, WrapPersistent(this), id); |
| 219 probe::AsyncTaskScheduled(GetExecutionContext(), "watchAvailabilityCallback", | 226 probe::AsyncTaskScheduled(GetExecutionContext(), "watchAvailabilityCallback", |
| 220 task.get()); | 227 task.get()); |
| 221 TaskRunnerHelper::Get(TaskType::kMediaElementEvent, GetExecutionContext()) | 228 TaskRunnerHelper::Get(TaskType::kMediaElementEvent, GetExecutionContext()) |
| 222 ->PostTask(BLINK_FROM_HERE, | 229 ->PostTask(BLINK_FROM_HERE, |
| 223 WTF::Bind(RunNotifyInitialAvailabilityTask, | 230 WTF::Bind(RunNotifyInitialAvailabilityTask, |
| 224 WrapPersistent(GetExecutionContext()), | 231 WrapPersistent(GetExecutionContext()), |
| 225 WTF::Passed(std::move(task)))); | 232 WTF::Passed(std::move(task)))); |
| 233 | |
| 234 UpdateListeningState(); | |
| 226 return id; | 235 return id; |
| 227 } | 236 } |
| 228 | 237 |
| 229 bool RemotePlayback::CancelWatchAvailabilityInternal(int id) { | 238 bool RemotePlayback::CancelWatchAvailabilityInternal(int id) { |
| 230 auto iter = availability_callbacks_.find(id); | 239 auto iter = availability_callbacks_.find(id); |
| 231 if (iter == availability_callbacks_.end()) | 240 if (iter == availability_callbacks_.end()) |
| 232 return false; | 241 return false; |
| 233 availability_callbacks_.erase(iter); | 242 availability_callbacks_.erase(iter); |
| 243 UpdateListeningState(); | |
| 244 | |
| 234 return true; | 245 return true; |
| 235 } | 246 } |
| 236 | 247 |
| 237 void RemotePlayback::NotifyInitialAvailability(int callback_id) { | 248 void RemotePlayback::NotifyInitialAvailability(int callback_id) { |
| 238 // May not find the callback if the website cancels it fast enough. | 249 // May not find the callback if the website cancels it fast enough. |
| 239 auto iter = availability_callbacks_.find(callback_id); | 250 auto iter = availability_callbacks_.find(callback_id); |
| 240 if (iter == availability_callbacks_.end()) | 251 if (iter == availability_callbacks_.end()) |
| 241 return; | 252 return; |
| 242 | 253 |
| 243 iter->value->Run(this, RemotePlaybackAvailable()); | 254 iter->value->Run(this, RemotePlaybackAvailable()); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 | 316 |
| 306 void RemotePlayback::PromptCancelled() { | 317 void RemotePlayback::PromptCancelled() { |
| 307 if (!prompt_promise_resolver_) | 318 if (!prompt_promise_resolver_) |
| 308 return; | 319 return; |
| 309 | 320 |
| 310 prompt_promise_resolver_->Reject( | 321 prompt_promise_resolver_->Reject( |
| 311 DOMException::Create(kNotAllowedError, "The prompt was dismissed.")); | 322 DOMException::Create(kNotAllowedError, "The prompt was dismissed.")); |
| 312 prompt_promise_resolver_ = nullptr; | 323 prompt_promise_resolver_ = nullptr; |
| 313 } | 324 } |
| 314 | 325 |
| 326 void RemotePlayback::SourceChanged(const WebURL& source) { | |
| 327 DCHECK(RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled()); | |
| 328 | |
| 329 // If we're listening for the previous source, we should stop listening to | |
|
mark a. foltz
2017/05/31 21:16:41
Do you want to restrict source URLs considered to
| |
| 330 // update the source for the WebPresentationClient. | |
| 331 if (!availability_urls_.empty()) { | |
| 332 WebVector<WebURL> empty_urls; | |
| 333 availability_urls_.Swap(empty_urls); | |
| 334 UpdateListeningState(); | |
| 335 } | |
| 336 | |
| 337 // Ignore the new source if it's not meaningful. | |
| 338 if (!source.IsEmpty() && source.IsValid()) { | |
| 339 UpdateAvailabilityUrls(source); | |
| 340 UpdateListeningState(); | |
| 341 } | |
| 342 } | |
| 343 | |
| 315 bool RemotePlayback::RemotePlaybackAvailable() const { | 344 bool RemotePlayback::RemotePlaybackAvailable() const { |
| 316 return availability_ == WebRemotePlaybackAvailability::kDeviceAvailable; | 345 return availability_ == WebRemotePlaybackAvailability::kDeviceAvailable; |
| 317 } | 346 } |
| 318 | 347 |
| 319 void RemotePlayback::RemotePlaybackDisabled() { | 348 void RemotePlayback::RemotePlaybackDisabled() { |
| 320 if (prompt_promise_resolver_) { | 349 if (prompt_promise_resolver_) { |
| 321 prompt_promise_resolver_->Reject(DOMException::Create( | 350 prompt_promise_resolver_->Reject(DOMException::Create( |
| 322 kInvalidStateError, "disableRemotePlayback attribute is present.")); | 351 kInvalidStateError, "disableRemotePlayback attribute is present.")); |
| 323 prompt_promise_resolver_ = nullptr; | 352 prompt_promise_resolver_ = nullptr; |
| 324 } | 353 } |
| 325 | 354 |
| 326 availability_callbacks_.clear(); | 355 availability_callbacks_.clear(); |
| 356 UpdateListeningState(); | |
| 327 | 357 |
| 328 if (state_ != WebRemotePlaybackState::kDisconnected) | 358 if (state_ != WebRemotePlaybackState::kDisconnected) |
| 329 media_element_->RequestRemotePlaybackStop(); | 359 media_element_->RequestRemotePlaybackStop(); |
| 330 } | 360 } |
| 331 | 361 |
| 362 void RemotePlayback::AvailabilityChanged(bool availability) { | |
| 363 DCHECK(RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled()); | |
| 364 AvailabilityChanged(availability | |
| 365 ? WebRemotePlaybackAvailability::kDeviceAvailable | |
| 366 : WebRemotePlaybackAvailability::kDeviceNotAvailable); | |
| 367 } | |
| 368 | |
| 369 const WebVector<WebURL>& RemotePlayback::Urls() const { | |
| 370 DCHECK(RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled()); | |
| 371 // TODO(avayvod): update the URL format and add frame url, mime type and | |
| 372 // response headers when available. | |
| 373 return availability_urls_; | |
| 374 } | |
| 375 | |
| 376 void RemotePlayback::UpdateListeningState() { | |
| 377 if (!RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled()) | |
| 378 return; | |
| 379 | |
| 380 bool will_be_listening = | |
| 381 !availability_urls_.empty() && !availability_callbacks_.IsEmpty(); | |
| 382 if (is_listening_ == will_be_listening) | |
| 383 return; | |
| 384 | |
| 385 WebPresentationClient* client = | |
| 386 PresentationController::ClientFromContext(GetExecutionContext()); | |
| 387 if (!client) | |
| 388 return; | |
| 389 | |
| 390 if (will_be_listening) { | |
| 391 client->StartListening(this); | |
| 392 } else { | |
| 393 client->StopListening(this); | |
| 394 } | |
| 395 is_listening_ = will_be_listening; | |
| 396 } | |
| 397 | |
| 398 void RemotePlayback::UpdateAvailabilityUrls(const WebURL& source) { | |
| 399 // The URL for each media element's source looks like the following: | |
| 400 // chrome-media-source://<encoded-data> where |encoded-data| is base64 URL | |
| 401 // encoded string representation of a JSON structure with various information | |
| 402 // about the media element's source that looks like this: | |
| 403 // { | |
| 404 // "sourceUrl": "<sourceUrl>", | |
| 405 // } | |
| 406 // TODO(avayvod): add and fill more info to the JSON structure. | |
|
mark a. foltz
2017/05/31 21:16:41
Can you explain why URL-encoding is not sufficient
| |
| 407 std::unique_ptr<JSONObject> source_info = JSONObject::Create(); | |
| 408 source_info->SetString("sourceUrl", source.GetString()); | |
| 409 CString json_source_info = source_info->ToJSONString().Utf8(); | |
| 410 String encoded_source_info = | |
| 411 WTF::Base64URLEncode(json_source_info.data(), json_source_info.length()); | |
| 412 | |
| 413 WebVector<WebURL> new_availability_urls((size_t)1); | |
| 414 new_availability_urls[0] = | |
| 415 KURL(kParsedURLString, "chrome-media-source://" + encoded_source_info); | |
| 416 availability_urls_.Swap(new_availability_urls); | |
| 417 } | |
| 418 | |
| 332 DEFINE_TRACE(RemotePlayback) { | 419 DEFINE_TRACE(RemotePlayback) { |
| 333 visitor->Trace(availability_callbacks_); | 420 visitor->Trace(availability_callbacks_); |
| 334 visitor->Trace(prompt_promise_resolver_); | 421 visitor->Trace(prompt_promise_resolver_); |
| 335 visitor->Trace(media_element_); | 422 visitor->Trace(media_element_); |
| 336 EventTargetWithInlineData::Trace(visitor); | 423 EventTargetWithInlineData::Trace(visitor); |
| 337 } | 424 } |
| 338 | 425 |
| 339 DEFINE_TRACE_WRAPPERS(RemotePlayback) { | 426 DEFINE_TRACE_WRAPPERS(RemotePlayback) { |
| 340 for (auto callback : availability_callbacks_.Values()) | 427 for (auto callback : availability_callbacks_.Values()) |
| 341 visitor->TraceWrappers(callback); | 428 visitor->TraceWrappers(callback); |
| 342 EventTargetWithInlineData::TraceWrappers(visitor); | 429 EventTargetWithInlineData::TraceWrappers(visitor); |
| 343 } | 430 } |
| 344 | 431 |
| 345 } // namespace blink | 432 } // namespace blink |
| OLD | NEW |