OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/media/router/presentation_service_delegate_impl.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/containers/scoped_ptr_hash_map.h" |
| 10 #include "base/guid.h" |
| 11 #include "base/strings/string_util.h" |
| 12 #include "base/strings/stringprintf.h" |
| 13 #include "chrome/browser/media/router/create_session_request.h" |
| 14 #include "chrome/browser/media/router/media_route.h" |
| 15 #include "chrome/browser/media/router/media_router.h" |
| 16 #include "chrome/browser/media/router/media_router_mojo_impl.h" |
| 17 #include "chrome/browser/media/router/media_router_mojo_impl_factory.h" |
| 18 #include "chrome/browser/media/router/media_sink.h" |
| 19 #include "chrome/browser/media/router/media_source_helper.h" |
| 20 #include "chrome/browser/media/router/presentation_media_sinks_observer.h" |
| 21 #include "chrome/browser/sessions/session_tab_helper.h" |
| 22 #include "content/public/browser/presentation_screen_availability_listener.h" |
| 23 #include "content/public/browser/presentation_session.h" |
| 24 #include "content/public/browser/render_frame_host.h" |
| 25 #include "content/public/browser/render_process_host.h" |
| 26 |
| 27 DEFINE_WEB_CONTENTS_USER_DATA_KEY( |
| 28 media_router::PresentationServiceDelegateImpl); |
| 29 |
| 30 using content::RenderFrameHost; |
| 31 |
| 32 namespace media_router { |
| 33 |
| 34 using RenderFrameHostId = std::pair<int, int>; |
| 35 |
| 36 namespace { |
| 37 |
| 38 using DelegateObserver = content::PresentationServiceDelegate::Observer; |
| 39 |
| 40 // Returns the unique identifier for the supplied RenderViewHost. |
| 41 RenderFrameHostId GetRenderFrameHostId(RenderFrameHost* render_view_host) { |
| 42 int render_process_id = render_view_host->GetProcess()->GetID(); |
| 43 int render_frame_id = render_view_host->GetRoutingID(); |
| 44 return RenderFrameHostId(render_process_id, render_frame_id); |
| 45 } |
| 46 |
| 47 std::string GetDisplayNameForFrame(RenderFrameHostId render_frame_host_id) { |
| 48 RenderFrameHost* render_view_host = RenderFrameHost::FromID( |
| 49 render_frame_host_id.first, render_frame_host_id.second); |
| 50 std::string host = render_view_host->GetLastCommittedURL().host(); |
| 51 if (StartsWithASCII(host, "www.", false)) |
| 52 host = host.substr(4); |
| 53 return host; |
| 54 } |
| 55 |
| 56 } // namespace |
| 57 |
| 58 // Used by PresentationServiceDelegateImpl to manage |
| 59 // listeners and default presentation info in a render frame. |
| 60 // Its lifetime: |
| 61 // * PresentationFrameManager AddDelegateObserver |
| 62 // * Reset 0+ times. |
| 63 // * PresentationFrameManager.RemoveDelegateObserver. |
| 64 class PresentationFrame { |
| 65 public: |
| 66 PresentationFrame(content::WebContents* web_contents, MediaRouter* router); |
| 67 ~PresentationFrame(); |
| 68 |
| 69 // Mirror corresponding APIs in PresentationServiceDelegateImpl. |
| 70 bool SetScreenAvailabilityListener( |
| 71 content::PresentationScreenAvailabilityListener* listener); |
| 72 bool RemoveScreenAvailabilityListener( |
| 73 content::PresentationScreenAvailabilityListener* listener); |
| 74 bool HasScreenAvailabilityListenerForTest( |
| 75 const MediaSourceId& source_id) const; |
| 76 void SetDefaultPresentationInfo(const std::string& default_presentation_url, |
| 77 const std::string& default_presentation_id); |
| 78 std::string GetDefaultPresentationId() const; |
| 79 void Reset(); |
| 80 |
| 81 void OnDefaultPresentationStarted( |
| 82 const content::PresentationSessionInfo& session) const; |
| 83 void OnPresentationServiceDelegateDestroyed() const; |
| 84 |
| 85 void set_delegate_observer(DelegateObserver* observer) { |
| 86 delegate_observer_ = observer; |
| 87 } |
| 88 |
| 89 private: |
| 90 MediaSource GetMediaSourceFromListener( |
| 91 content::PresentationScreenAvailabilityListener* listener) const; |
| 92 |
| 93 scoped_ptr<content::PresentationSessionInfo> default_presentation_info_; |
| 94 scoped_ptr<PresentationMediaSinksObserver> sinks_observer_; |
| 95 |
| 96 // References to the owning WebContents, and the corresponding MediaRouter. |
| 97 const content::WebContents* web_contents_; |
| 98 MediaRouter* router_; |
| 99 |
| 100 DelegateObserver* delegate_observer_; |
| 101 }; |
| 102 |
| 103 PresentationFrame::PresentationFrame(content::WebContents* web_contents, |
| 104 MediaRouter* router) |
| 105 : web_contents_(web_contents), |
| 106 router_(router), |
| 107 delegate_observer_(nullptr) { |
| 108 DCHECK(web_contents_); |
| 109 DCHECK(router_); |
| 110 } |
| 111 |
| 112 PresentationFrame::~PresentationFrame() { |
| 113 } |
| 114 |
| 115 void PresentationFrame::OnPresentationServiceDelegateDestroyed() const { |
| 116 if (delegate_observer_) |
| 117 delegate_observer_->OnDelegateDestroyed(); |
| 118 } |
| 119 |
| 120 void PresentationFrame::OnDefaultPresentationStarted( |
| 121 const content::PresentationSessionInfo& session) const { |
| 122 if (delegate_observer_) |
| 123 delegate_observer_->OnDefaultPresentationStarted(session); |
| 124 } |
| 125 |
| 126 bool PresentationFrame::SetScreenAvailabilityListener( |
| 127 content::PresentationScreenAvailabilityListener* listener) { |
| 128 if (sinks_observer_ && sinks_observer_->listener() == listener) { |
| 129 return false; |
| 130 } |
| 131 MediaSource source(GetMediaSourceFromListener(listener)); |
| 132 sinks_observer_.reset( |
| 133 new PresentationMediaSinksObserver(router_, listener, source)); |
| 134 return true; |
| 135 } |
| 136 |
| 137 bool PresentationFrame::RemoveScreenAvailabilityListener( |
| 138 content::PresentationScreenAvailabilityListener* listener) { |
| 139 if (sinks_observer_ && sinks_observer_->listener() == listener) { |
| 140 sinks_observer_.reset(); |
| 141 return true; |
| 142 } |
| 143 return false; |
| 144 } |
| 145 |
| 146 bool PresentationFrame::HasScreenAvailabilityListenerForTest( |
| 147 const MediaSourceId& source_id) const { |
| 148 return sinks_observer_ && sinks_observer_->source().id() == source_id; |
| 149 } |
| 150 |
| 151 void PresentationFrame::Reset() { |
| 152 sinks_observer_.reset(); |
| 153 default_presentation_info_.reset(); |
| 154 } |
| 155 |
| 156 void PresentationFrame::SetDefaultPresentationInfo( |
| 157 const std::string& default_presentation_url, |
| 158 const std::string& default_presentation_id) { |
| 159 if (default_presentation_url.empty() && default_presentation_id.empty()) { |
| 160 default_presentation_info_.reset(); |
| 161 } else { |
| 162 default_presentation_info_.reset(new content::PresentationSessionInfo( |
| 163 default_presentation_url, default_presentation_id)); |
| 164 } |
| 165 } |
| 166 |
| 167 std::string PresentationFrame::GetDefaultPresentationId() const { |
| 168 return default_presentation_info_ |
| 169 ? default_presentation_info_->presentation_id |
| 170 : ""; |
| 171 } |
| 172 |
| 173 MediaSource PresentationFrame::GetMediaSourceFromListener( |
| 174 content::PresentationScreenAvailabilityListener* listener) const { |
| 175 // If the default presentation URL is empty then fall back to tab mirroring. |
| 176 std::string presentation_url(listener->GetPresentationUrl()); |
| 177 return presentation_url.empty() |
| 178 ? MediaSourceForTab(SessionTabHelper::IdForTab(web_contents_)) |
| 179 : MediaSourceForPresentationUrl(presentation_url); |
| 180 } |
| 181 |
| 182 // Used by PresentationServiceDelegateImpl to manage PresentationFrames. |
| 183 class PresentationFrameManager { |
| 184 public: |
| 185 PresentationFrameManager(content::WebContents* web_contents, |
| 186 MediaRouter* router); |
| 187 ~PresentationFrameManager(); |
| 188 |
| 189 // Mirror corresponding APIs in PresentationServiceDelegateImpl. |
| 190 bool SetScreenAvailabilityListener( |
| 191 const RenderFrameHostId& render_frame_host_id, |
| 192 content::PresentationScreenAvailabilityListener* listener); |
| 193 bool RemoveScreenAvailabilityListener( |
| 194 const RenderFrameHostId& render_frame_host_id, |
| 195 content::PresentationScreenAvailabilityListener* listener); |
| 196 void SetDefaultPresentationInfo(const RenderFrameHostId& render_frame_host_id, |
| 197 const std::string& default_presentation_url, |
| 198 const std::string& default_presentation_id); |
| 199 void AddDelegateObserver(const RenderFrameHostId& render_frame_host_id, |
| 200 DelegateObserver* observer); |
| 201 void RemoveDelegateObserver(const RenderFrameHostId& render_frame_host_id); |
| 202 void Reset(const RenderFrameHostId& render_frame_host_id); |
| 203 bool HasScreenAvailabilityListenerForTest( |
| 204 const RenderFrameHostId& render_frame_host_id, |
| 205 const MediaSourceId& source_id) const; |
| 206 void SetMediaRouterForTest(MediaRouter* router); |
| 207 |
| 208 // Returns default presentation ID, or empty string if no default |
| 209 // presentation ID is set in the frame. |
| 210 std::string GetDefaultPresentationId( |
| 211 const RenderFrameHostId& render_frame_host_id) const; |
| 212 |
| 213 void OnDefaultPresentationStarted( |
| 214 const RenderFrameHostId& render_frame_host_id, |
| 215 const content::PresentationSessionInfo& session) const; |
| 216 |
| 217 private: |
| 218 PresentationFrame* GetOrAddPresentationFrame( |
| 219 const RenderFrameHostId& render_frame_host_id); |
| 220 |
| 221 // Maps a frame identifier to a PresentationFrame object for frames |
| 222 // that are using presentation API. |
| 223 base::ScopedPtrHashMap<RenderFrameHostId, scoped_ptr<PresentationFrame>> |
| 224 presentation_frames_; |
| 225 |
| 226 // References to the owning WebContents, and the corresponding MediaRouter. |
| 227 MediaRouter* router_; |
| 228 content::WebContents* web_contents_; |
| 229 }; |
| 230 |
| 231 PresentationFrameManager::PresentationFrameManager( |
| 232 content::WebContents* web_contents, |
| 233 MediaRouter* router) |
| 234 : router_(router), web_contents_(web_contents) { |
| 235 DCHECK(web_contents_); |
| 236 DCHECK(router_); |
| 237 } |
| 238 |
| 239 PresentationFrameManager::~PresentationFrameManager() { |
| 240 for (auto& frame : presentation_frames_) |
| 241 frame.second->OnPresentationServiceDelegateDestroyed(); |
| 242 } |
| 243 |
| 244 void PresentationFrameManager::OnDefaultPresentationStarted( |
| 245 const RenderFrameHostId& render_frame_host_id, |
| 246 const content::PresentationSessionInfo& session) const { |
| 247 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 248 if (presentation_frame) |
| 249 presentation_frame->OnDefaultPresentationStarted(session); |
| 250 } |
| 251 |
| 252 bool PresentationFrameManager::SetScreenAvailabilityListener( |
| 253 const RenderFrameHostId& render_frame_host_id, |
| 254 content::PresentationScreenAvailabilityListener* listener) { |
| 255 DCHECK(listener); |
| 256 auto presentation_frame = GetOrAddPresentationFrame(render_frame_host_id); |
| 257 return presentation_frame->SetScreenAvailabilityListener(listener); |
| 258 } |
| 259 |
| 260 bool PresentationFrameManager::RemoveScreenAvailabilityListener( |
| 261 const RenderFrameHostId& render_frame_host_id, |
| 262 content::PresentationScreenAvailabilityListener* listener) { |
| 263 DCHECK(listener); |
| 264 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 265 return presentation_frame && |
| 266 presentation_frame->RemoveScreenAvailabilityListener(listener); |
| 267 } |
| 268 |
| 269 bool PresentationFrameManager::HasScreenAvailabilityListenerForTest( |
| 270 const RenderFrameHostId& render_frame_host_id, |
| 271 const MediaSourceId& source_id) const { |
| 272 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 273 return presentation_frame && |
| 274 presentation_frame->HasScreenAvailabilityListenerForTest(source_id); |
| 275 } |
| 276 |
| 277 void PresentationFrameManager::SetDefaultPresentationInfo( |
| 278 const RenderFrameHostId& render_frame_host_id, |
| 279 const std::string& default_presentation_url, |
| 280 const std::string& default_presentation_id) { |
| 281 auto presentation_frame = GetOrAddPresentationFrame(render_frame_host_id); |
| 282 presentation_frame->SetDefaultPresentationInfo(default_presentation_url, |
| 283 default_presentation_id); |
| 284 } |
| 285 |
| 286 std::string PresentationFrameManager::GetDefaultPresentationId( |
| 287 const RenderFrameHostId& render_frame_host_id) const { |
| 288 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 289 return presentation_frame ? presentation_frame->GetDefaultPresentationId() |
| 290 : ""; |
| 291 } |
| 292 |
| 293 void PresentationFrameManager::AddDelegateObserver( |
| 294 const RenderFrameHostId& render_frame_host_id, |
| 295 DelegateObserver* observer) { |
| 296 auto presentation_frame = GetOrAddPresentationFrame(render_frame_host_id); |
| 297 presentation_frame->set_delegate_observer(observer); |
| 298 } |
| 299 |
| 300 void PresentationFrameManager::RemoveDelegateObserver( |
| 301 const RenderFrameHostId& render_frame_host_id) { |
| 302 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 303 if (presentation_frame) { |
| 304 presentation_frame->set_delegate_observer(nullptr); |
| 305 presentation_frames_.erase(render_frame_host_id); |
| 306 } |
| 307 } |
| 308 |
| 309 void PresentationFrameManager::Reset( |
| 310 const RenderFrameHostId& render_frame_host_id) { |
| 311 auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
| 312 if (presentation_frame) |
| 313 presentation_frame->Reset(); |
| 314 } |
| 315 |
| 316 PresentationFrame* PresentationFrameManager::GetOrAddPresentationFrame( |
| 317 const RenderFrameHostId& render_frame_host_id) { |
| 318 if (!presentation_frames_.contains(render_frame_host_id)) { |
| 319 presentation_frames_.add( |
| 320 render_frame_host_id, |
| 321 scoped_ptr<PresentationFrame>( |
| 322 new PresentationFrame(web_contents_, router_))); |
| 323 } |
| 324 return presentation_frames_.get(render_frame_host_id); |
| 325 } |
| 326 |
| 327 void PresentationFrameManager::SetMediaRouterForTest(MediaRouter* router) { |
| 328 router_ = router; |
| 329 } |
| 330 |
| 331 PresentationServiceDelegateImpl::PresentationServiceDelegateImpl( |
| 332 content::WebContents* web_contents) |
| 333 : web_contents_(web_contents), |
| 334 router_(MediaRouterMojoImplFactory::GetApiForBrowserContext( |
| 335 web_contents_->GetBrowserContext())), |
| 336 frame_manager_(new PresentationFrameManager(web_contents, router_)), |
| 337 weak_factory_(this) { |
| 338 DCHECK(web_contents_); |
| 339 DCHECK(router_); |
| 340 } |
| 341 |
| 342 PresentationServiceDelegateImpl::~PresentationServiceDelegateImpl() { |
| 343 } |
| 344 |
| 345 void PresentationServiceDelegateImpl::AddObserver(int render_process_id, |
| 346 int render_frame_id, |
| 347 DelegateObserver* observer) { |
| 348 DCHECK(observer); |
| 349 frame_manager_->AddDelegateObserver( |
| 350 RenderFrameHostId(render_process_id, render_frame_id), observer); |
| 351 } |
| 352 |
| 353 void PresentationServiceDelegateImpl::RemoveObserver(int render_process_id, |
| 354 int render_frame_id) { |
| 355 frame_manager_->RemoveDelegateObserver( |
| 356 RenderFrameHostId(render_process_id, render_frame_id)); |
| 357 } |
| 358 |
| 359 bool PresentationServiceDelegateImpl::AddScreenAvailabilityListener( |
| 360 int render_process_id, |
| 361 int render_frame_id, |
| 362 content::PresentationScreenAvailabilityListener* listener) { |
| 363 DCHECK(listener); |
| 364 return frame_manager_->SetScreenAvailabilityListener( |
| 365 RenderFrameHostId(render_process_id, render_frame_id), listener); |
| 366 } |
| 367 |
| 368 void PresentationServiceDelegateImpl::RemoveScreenAvailabilityListener( |
| 369 int render_process_id, |
| 370 int render_frame_id, |
| 371 content::PresentationScreenAvailabilityListener* listener) { |
| 372 DCHECK(listener); |
| 373 frame_manager_->RemoveScreenAvailabilityListener( |
| 374 RenderFrameHostId(render_process_id, render_frame_id), listener); |
| 375 } |
| 376 |
| 377 void PresentationServiceDelegateImpl::Reset(int render_process_id, |
| 378 int render_frame_id) { |
| 379 RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id); |
| 380 frame_manager_->Reset(render_frame_host_id); |
| 381 if (IsMainFrame(render_process_id, render_frame_id)) |
| 382 UpdateDefaultMediaSourceAndNotifyObservers(MediaSource(), std::string()); |
| 383 } |
| 384 |
| 385 void PresentationServiceDelegateImpl::SetDefaultPresentationUrl( |
| 386 int render_process_id, |
| 387 int render_frame_id, |
| 388 const std::string& default_presentation_url, |
| 389 const std::string& default_presentation_id) { |
| 390 RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id); |
| 391 frame_manager_->SetDefaultPresentationInfo( |
| 392 render_frame_host_id, default_presentation_url, default_presentation_id); |
| 393 if (IsMainFrame(render_process_id, render_frame_id)) { |
| 394 // This is the main frame, which means tab-level default presentation |
| 395 // might have been updated. |
| 396 MediaSource default_source; |
| 397 if (!default_presentation_url.empty()) |
| 398 default_source = MediaSourceForPresentationUrl(default_presentation_url); |
| 399 |
| 400 std::string default_frame_display_name( |
| 401 GetDisplayNameForFrame(render_frame_host_id)); |
| 402 UpdateDefaultMediaSourceAndNotifyObservers(default_source, |
| 403 default_frame_display_name); |
| 404 } |
| 405 } |
| 406 |
| 407 bool PresentationServiceDelegateImpl::IsMainFrame(int render_process_id, |
| 408 int render_frame_id) const { |
| 409 RenderFrameHost* main_frame = web_contents_->GetMainFrame(); |
| 410 return main_frame && |
| 411 GetRenderFrameHostId(main_frame) == |
| 412 RenderFrameHostId(render_process_id, render_frame_id); |
| 413 } |
| 414 |
| 415 void PresentationServiceDelegateImpl:: |
| 416 UpdateDefaultMediaSourceAndNotifyObservers( |
| 417 const MediaSource& default_source, |
| 418 const std::string& default_frame_display_name) { |
| 419 if (!default_source.Equals(default_source_) || |
| 420 default_frame_display_name != default_frame_display_name_) { |
| 421 default_source_ = default_source; |
| 422 default_frame_display_name_ = default_frame_display_name; |
| 423 FOR_EACH_OBSERVER(DefaultMediaSourceObserver, |
| 424 default_media_source_observers_, |
| 425 OnDefaultMediaSourceChanged(default_source_, |
| 426 default_frame_display_name_)); |
| 427 } |
| 428 } |
| 429 |
| 430 void PresentationServiceDelegateImpl::StartSession( |
| 431 int render_process_id, |
| 432 int render_frame_id, |
| 433 const std::string& presentation_url, |
| 434 const std::string& presentation_id, |
| 435 const PresentationSessionSuccessCallback& success_cb, |
| 436 const PresentationSessionErrorCallback& error_cb) { |
| 437 // BUG=464205 |
| 438 NOTIMPLEMENTED(); |
| 439 } |
| 440 |
| 441 void PresentationServiceDelegateImpl::JoinSession( |
| 442 int render_process_id, |
| 443 int render_frame_id, |
| 444 const std::string& presentation_url, |
| 445 const std::string& presentation_id, |
| 446 const PresentationSessionSuccessCallback& success_cb, |
| 447 const PresentationSessionErrorCallback& error_cb) { |
| 448 // BUG=464205 |
| 449 NOTIMPLEMENTED(); |
| 450 } |
| 451 |
| 452 void PresentationServiceDelegateImpl::ListenForSessionMessages( |
| 453 int render_process_id, |
| 454 int render_frame_id, |
| 455 const PresentationSessionMessageCallback& message_cb) { |
| 456 // BUG=464205 |
| 457 NOTIMPLEMENTED(); |
| 458 } |
| 459 |
| 460 void PresentationServiceDelegateImpl::SendMessage( |
| 461 int render_process_id, |
| 462 int render_frame_id, |
| 463 scoped_ptr<content::PresentationSessionMessage> message_request, |
| 464 const SendMessageCallback& send_message_cb) { |
| 465 // BUG=464205 |
| 466 NOTIMPLEMENTED(); |
| 467 } |
| 468 |
| 469 void PresentationServiceDelegateImpl::OnRouteCreated(const MediaRoute& route) { |
| 470 const MediaSource& source = route.media_source(); |
| 471 DCHECK(!source.Empty()); |
| 472 if (!default_source_.Equals(source)) |
| 473 return; |
| 474 RenderFrameHost* main_frame = web_contents_->GetMainFrame(); |
| 475 if (!main_frame) |
| 476 return; |
| 477 RenderFrameHostId render_frame_host_id(GetRenderFrameHostId(main_frame)); |
| 478 // TODO(imcheng): Pass in valid default presentation ID once it is |
| 479 // available from MediaRoute URN. BUG=493365 |
| 480 frame_manager_->OnDefaultPresentationStarted( |
| 481 render_frame_host_id, |
| 482 content::PresentationSessionInfo(PresentationUrlFromMediaSource(source), |
| 483 std::string())); |
| 484 } |
| 485 |
| 486 void PresentationServiceDelegateImpl::AddDefaultMediaSourceObserver( |
| 487 DefaultMediaSourceObserver* observer) { |
| 488 default_media_source_observers_.AddObserver(observer); |
| 489 } |
| 490 |
| 491 void PresentationServiceDelegateImpl::RemoveDefaultMediaSourceObserver( |
| 492 DefaultMediaSourceObserver* observer) { |
| 493 default_media_source_observers_.RemoveObserver(observer); |
| 494 } |
| 495 |
| 496 void PresentationServiceDelegateImpl::SetMediaRouterForTest( |
| 497 MediaRouter* router) { |
| 498 router_ = router; |
| 499 frame_manager_->SetMediaRouterForTest(router); |
| 500 } |
| 501 |
| 502 base::WeakPtr<PresentationServiceDelegateImpl> |
| 503 PresentationServiceDelegateImpl::GetWeakPtr() { |
| 504 return weak_factory_.GetWeakPtr(); |
| 505 } |
| 506 |
| 507 bool PresentationServiceDelegateImpl::HasScreenAvailabilityListenerForTest( |
| 508 int render_process_id, |
| 509 int render_frame_id, |
| 510 const MediaSourceId& source_id) const { |
| 511 RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id); |
| 512 return frame_manager_->HasScreenAvailabilityListenerForTest( |
| 513 render_frame_host_id, source_id); |
| 514 } |
| 515 |
| 516 } // namespace media_router |
OLD | NEW |