Chromium Code Reviews| Index: chrome/browser/media/router/offscreen_presentation_manager.cc |
| diff --git a/chrome/browser/media/router/offscreen_presentation_manager.cc b/chrome/browser/media/router/offscreen_presentation_manager.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1dd2f0f949731a1a2d6436ca3f160d0d8c0956ba |
| --- /dev/null |
| +++ b/chrome/browser/media/router/offscreen_presentation_manager.cc |
| @@ -0,0 +1,485 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/media/router/offscreen_presentation_manager.h" |
| + |
| +#include "content/public/browser/presentation_session_state_listener.h" |
| +#include "content/public/browser/render_frame_host.h" |
| +#include "content/public/browser/render_process_host.h" |
| +#include "content/public/browser/web_contents.h" |
| + |
| +using content::PresentationSessionInfo; |
| +using content::PresentationReceiverSessionAvailableCallback; |
| + |
| +namespace media_router { |
| + |
| +namespace { |
| + |
| +RenderFrameHostId GetMainFrameRenderFrameHostId( |
| + content::WebContents* web_contents) { |
| + content::RenderFrameHost* render_frame_host = web_contents->GetMainFrame(); |
| + DCHECK(render_frame_host); |
| + return RenderFrameHostId(render_frame_host->GetProcess()->GetID(), |
| + render_frame_host->GetRoutingID()); |
| +} |
| + |
| +} // namespace |
| + |
| +OffscreenPresenterContext::OffscreenPresenterContext( |
| + content::WebContents* presenter_web_contents, |
| + const std::string& presentation_id, |
| + OffscreenPresentationManager* manager) |
| + : presenter_web_contents_(presenter_web_contents), |
| + presentation_id_(presentation_id), |
| + manager_(manager) { |
| + DCHECK(presenter_web_contents_); |
| + DCHECK(manager_); |
| +} |
| + |
| +OffscreenPresenterContext::~OffscreenPresenterContext() { |
| + manager_->UnregisterPresenterTab(presenter_web_contents_); |
| +} |
| + |
| +void OffscreenPresenterContext::GetPresentationReceiverSession( |
| + const RenderFrameHostId& frame_id, |
| + const content::PresentationReceiverSessionAvailableCallback& |
| + success_callback, |
| + const base::Callback<void(const std::string&)>& error_callback) { |
| + if (!IsPresenterFrame(frame_id)) { |
| + DVLOG(1) << "Not a presenter frame: " << frame_id.first << ", " |
| + << frame_id.second; |
| + error_callback.Run("Not a presenter frame"); |
| + return; |
| + } |
| + |
| + manager_->GetReceiverSession(frame_id, success_callback); |
| +} |
| + |
| +std::vector<content::PresentationSessionInfo> |
| +OffscreenPresenterContext::GetPresentationReceiverSessions( |
| + const RenderFrameHostId& frame_id) const { |
| + if (!IsPresenterFrame(frame_id)) { |
| + DVLOG(1) << "Not a presenter frame: " << frame_id.first << ", " |
| + << frame_id.second; |
| + return std::vector<content::PresentationSessionInfo>(); |
| + } |
| + |
| + return manager_->GetPresentationReceiverSessions(frame_id); |
| +} |
| + |
| +bool OffscreenPresenterContext::IsPresenterFrame( |
| + const RenderFrameHostId& frame_id) const { |
| + return GetMainFrameRenderFrameHostId(presenter_web_contents_) == frame_id; |
| +} |
| + |
| +void OffscreenPresenterContext::SendMessage( |
| + const RenderFrameHostId& frame_id, |
| + const content::PresentationSessionInfo& session, |
| + scoped_ptr<content::PresentationSessionMessage> message, |
| + const content::SendMessageCallback& callback) { |
| + DCHECK_EQ(presentation_id_, session.presentation_id); |
| + DCHECK(!session.IsController()); |
| + DCHECK(IsPresenterFrame(frame_id)); |
| + |
| + manager_->SendMessage(session, message.Pass(), callback); |
| +} |
| + |
| +void OffscreenPresenterContext::ListenForMessages( |
| + const RenderFrameHostId& frame_id, |
|
miu
2015/09/27 00:22:08
1. |frame_id| argument is not used, so can you rem
imcheng
2015/09/30 01:13:41
1. Removed. Also removed some other unused fields
|
| + const content::PresentationSessionInfo& session, |
| + const content::PresentationSessionMessageCallback& callback) { |
| + DCHECK_EQ(presentation_id_, session.presentation_id); |
| + DCHECK(!session.IsController()); |
| + DCHECK(IsPresenterFrame(frame_id)); |
| + |
| + manager_->ListenForMessages(session, callback); |
| +} |
| + |
| +void OffscreenPresenterContext::ListenForStateChanges( |
| + const RenderFrameHostId& frame_id, |
| + content::PresentationSessionStateListener* listener) { |
| + DCHECK(listener); |
| + DCHECK(IsPresenterFrame(frame_id)); |
| + |
| + const content::PresentationSessionInfo& session = listener->GetSessionInfo(); |
|
miu
2015/09/27 00:22:08
These three lines need to be wrapped in a #ifndef
imcheng
2015/09/30 01:13:41
Removed
|
| + DCHECK_EQ(presentation_id_, session.presentation_id); |
| + DCHECK(!session.IsController()); |
| + |
| + manager_->ListenForStateChanges(listener); |
| +} |
| + |
| +OffscreenController::OffscreenController( |
| + const RenderFrameHostId& controller_frame_id, |
| + const content::PresentationSessionInfo& session, |
| + OffscreenPresentationManager* manager) |
| + : controller_frame_id_(controller_frame_id), |
| + session_(session), |
| + manager_(manager) { |
| + DCHECK(manager_); |
| + DCHECK(session.IsController()); |
| +} |
| + |
| +OffscreenController::~OffscreenController() { |
| + manager_->UnregisterController(controller_frame_id_, |
| + session_.presentation_id); |
| +} |
| + |
| +void OffscreenController::SendMessage( |
| + scoped_ptr<content::PresentationSessionMessage> message, |
| + const content::SendMessageCallback& callback) { |
| + manager_->SendMessage(session_, message.Pass(), callback); |
| +} |
| + |
| +void OffscreenController::ListenForMessages( |
| + const content::PresentationSessionMessageCallback& callback) { |
| + manager_->ListenForMessages(session_, callback); |
| +} |
| + |
| +void OffscreenController::ListenForStateChanges( |
| + content::PresentationSessionStateListener* listener) { |
| + DCHECK(listener); |
| + DCHECK_EQ(session_.presentation_id, |
| + listener->GetSessionInfo().presentation_id); |
| + manager_->ListenForStateChanges(listener); |
| +} |
| + |
| +OffscreenPresentationManager::~OffscreenPresentationManager() {} |
| + |
| +void OffscreenPresentationManager::RegisterPresenterTab( |
| + const std::string& presentation_id, |
| + content::WebContents* presenter_web_contents) { |
| + DCHECK(presenter_web_contents); |
| + RenderFrameHostId presenter_frame_id( |
| + GetMainFrameRenderFrameHostId(presenter_web_contents)); |
| + DCHECK(!ContainsKey(presenter_frames_, presenter_frame_id)); |
| + presenter_frames_[presenter_frame_id] = presentation_id; |
| + |
| + auto* presentation_info = |
| + GetOrCreateOffscreenPresentationInfo(presentation_id); |
| + presentation_info->presenter_frame_id = presenter_frame_id; |
| + // We wait until the controller side has registered before setting up the |
| + // route. |
| +} |
| + |
| +void OffscreenPresentationManager::UnregisterPresenterTab( |
| + content::WebContents* presenter_web_contents) { |
| + DCHECK(presenter_web_contents); |
| + RenderFrameHostId presenter_frame_id( |
| + GetMainFrameRenderFrameHostId(presenter_web_contents)); |
| + |
| + auto it = presenter_frames_.find(presenter_frame_id); |
| + if (it == presenter_frames_.end()) |
| + return; |
| + |
| + std::string presentation_id = it->second; |
| + presenter_frames_.erase(it); |
| + |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + DCHECK(presentation_info); |
| + DCHECK(presenter_frame_id == presentation_info->presenter_frame_id); |
| + RemovePresenterAndNotifyStateChange(presentation_info); |
| + if (presentation_info->IsEmpty()) |
| + offscreen_presentation_infos_.erase(presentation_id); |
| +} |
| + |
| +scoped_ptr<OffscreenPresenterContext> |
| +OffscreenPresentationManager::GetOffscreenPresenterContext( |
| + content::WebContents* presenter_web_contents) { |
| + DCHECK(presenter_web_contents); |
| + RenderFrameHostId presenter_frame_id( |
| + GetMainFrameRenderFrameHostId(presenter_web_contents)); |
| + auto it = presenter_frames_.find(presenter_frame_id); |
| + if (it == presenter_frames_.end()) |
| + return scoped_ptr<OffscreenPresenterContext>(); |
| + |
| + return make_scoped_ptr( |
| + new OffscreenPresenterContext(presenter_web_contents, it->second, this)); |
| +} |
| + |
| +scoped_ptr<OffscreenController> |
| +OffscreenPresentationManager::RegisterController( |
| + const content::PresentationSessionInfo& session, |
| + const RenderFrameHostId& controller_frame_id) { |
| + const std::string& presentation_id = session.presentation_id; |
| + auto* presentation_info = |
| + GetOrCreateOffscreenPresentationInfo(presentation_id); |
| + |
| + auto& route = presentation_info->route; |
| + |
| + // Check that this (presentation, controller) is not already registered. |
| + DCHECK(!route.controller); |
| + DCHECK(!route.presenter); |
| + |
| + route.controller.reset( |
| + new OffscreenPresentationSessionInfo(controller_frame_id, session)); |
| + content::PresentationSessionInfo presenter_session(std::string(), |
| + presentation_id); |
| + route.presenter.reset(new OffscreenPresentationSessionInfo( |
| + controller_frame_id, presenter_session)); |
| + |
| + // Resolve pending getSession() request. |
| + // Note: session objects for presenters do not contain presentation URLs. |
| + if (!presentation_info->session_callback.is_null()) { |
| + presentation_info->session_callback.Run(presenter_session); |
| + presentation_info->session_callback.Reset(); |
| + } |
| + |
| + return make_scoped_ptr( |
| + new OffscreenController(controller_frame_id, session, this)); |
| +} |
| + |
| +void OffscreenPresentationManager::GetReceiverSession( |
| + const RenderFrameHostId& presenter_frame_id, |
| + const PresentationReceiverSessionAvailableCallback& callback) { |
| + DCHECK(!callback.is_null()); |
| + DCHECK(ContainsKey(presenter_frames_, presenter_frame_id)); |
| + |
| + const std::string& presentation_id = presenter_frames_[presenter_frame_id]; |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + DCHECK(presentation_info); |
| + DCHECK(presenter_frame_id == presentation_info->presenter_frame_id); |
| + |
| + // Resolve with the first route. |
| + const auto& route = presentation_info->route; |
| + if (route.presenter) { |
| + callback.Run(route.presenter->session); |
| + return; |
| + } else { |
| + presentation_info->session_callback = callback; |
| + } |
| +} |
| + |
| +std::vector<content::PresentationSessionInfo> |
| +OffscreenPresentationManager::GetPresentationReceiverSessions( |
| + const RenderFrameHostId& presenter_frame_id) const { |
| + DCHECK(ContainsKey(presenter_frames_, presenter_frame_id)); |
| + |
| + auto it = presenter_frames_.find(presenter_frame_id); |
| + DCHECK(it != presenter_frames_.end()); |
| + const std::string& presentation_id = it->second; |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + DCHECK(presentation_info); |
| + |
| + std::vector<content::PresentationSessionInfo> sessions; |
| + // TODO(imcheng): Support multiple controllers. |
| + if (presentation_info->route.presenter) |
| + sessions.push_back(presentation_info->route.presenter->session); |
| + return sessions; |
| +} |
| + |
| +void OffscreenPresentationManager::SendMessage( |
| + const PresentationSessionInfo& session, |
| + scoped_ptr<content::PresentationSessionMessage> message, |
| + const content::SendMessageCallback& callback) { |
| + const std::string& presentation_id = session.presentation_id; |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + if (!presentation_info) { |
| + LOG(ERROR) << "SendMessage: Unknown 1UA presentation " << presentation_id; |
| + callback.Run(false); |
| + return; |
| + } |
| + |
| + auto& route = presentation_info->route; |
| + // From presenter to controller. |
| + if (!session.IsController()) { |
| + auto* controller = route.controller.get(); |
| + if (controller && !controller->message_callback.is_null()) { |
| + ScopedVector<content::PresentationSessionMessage> messages; |
| + messages.push_back(message.release()); |
| + controller->message_callback.Run(messages.Pass(), true); |
| + } |
| + } else { |
| + auto* presenter = route.presenter.get(); |
| + // From controller to presenter. |
| + if (presenter && !presenter->message_callback.is_null()) { |
| + ScopedVector<content::PresentationSessionMessage> messages; |
| + messages.push_back(message.release()); |
| + presenter->message_callback.Run(messages.Pass(), true); |
| + } |
| + } |
| + |
| + callback.Run(true); |
| +} |
| + |
| +void OffscreenPresentationManager::ListenForMessages( |
| + const PresentationSessionInfo& session, |
| + const content::PresentationSessionMessageCallback& callback) { |
| + const std::string& presentation_id = session.presentation_id; |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + if (!presentation_info) { |
| + LOG(ERROR) << "ListenForMessages: Unknown 1UA presentation " |
| + << presentation_id; |
| + return; |
| + } |
| + |
| + auto& route = presentation_info->route; |
| + if (!session.IsController()) { |
| + auto* presenter = route.presenter.get(); |
| + if (!presenter) { |
| + LOG(ERROR) << "ListenForMessages: presenter frame not found for " |
| + << presentation_id; |
| + return; |
| + } |
| + DVLOG_IF(2, !presenter->message_callback.is_null()) |
| + << "Overwriting presenter frame message callback for " |
| + << presentation_id; |
| + presenter->message_callback = callback; |
| + } else { |
| + auto* controller = route.controller.get(); |
| + if (!controller) { |
| + LOG(ERROR) << "ListenForMessages: controller frame not found for " |
| + << presentation_id; |
| + return; |
| + } |
| + DVLOG_IF(2, !controller->message_callback.is_null()) |
| + << "Overwriting controller frame message callback for " |
| + << presentation_id; |
| + controller->message_callback = callback; |
| + } |
| +} |
| + |
| +void OffscreenPresentationManager::ListenForStateChanges( |
| + content::PresentationSessionStateListener* listener) { |
| + DCHECK(listener); |
| + |
| + const content::PresentationSessionInfo& session = listener->GetSessionInfo(); |
| + const std::string& presentation_id = session.presentation_id; |
| + auto* presentation_info = GetOffscreenPresentationInfo(presentation_id); |
| + if (!presentation_info) { |
| + LOG(ERROR) << "ListenForMessages: Unknown 1UA presentation " |
| + << presentation_id; |
| + return; |
| + } |
| + |
| + auto& route = presentation_info->route; |
| + if (!session.IsController()) { |
| + auto* presenter = route.presenter.get(); |
| + if (!presenter) { |
| + LOG(ERROR) << "ListenForMessages: presenter frame not found for " |
| + << presentation_id; |
| + return; |
| + } |
| + DVLOG_IF(2, presenter->state_change_listener) |
| + << "Overwriting presenter frame state change listener for " |
| + << presentation_id; |
| + presenter->state_change_listener = listener; |
| + } else { |
| + auto* controller = route.controller.get(); |
| + if (!controller) { |
| + LOG(ERROR) << "ListenForMessages: controller frame not found for " |
| + << presentation_id; |
| + return; |
| + } |
| + DVLOG_IF(2, controller->state_change_listener) |
| + << "Overwriting controller frame state change listener for " |
| + << presentation_id; |
| + controller->state_change_listener = listener; |
| + } |
| +} |
| + |
| +void OffscreenPresentationManager::UnregisterController( |
| + const RenderFrameHostId& controller_frame_id, |
| + const std::string& presentation_id) { |
| + auto it = offscreen_presentation_infos_.find(presentation_id); |
| + if (it == offscreen_presentation_infos_.end()) |
| + return; |
| + |
| + auto* presentation_info = it->second; |
| + RemoveControllerAndNotifyStateChange(presentation_info, controller_frame_id); |
| + if (presentation_info->IsEmpty()) |
| + offscreen_presentation_infos_.erase(it); |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationManager() {} |
| + |
| +void OffscreenPresentationManager::RemovePresenterAndNotifyStateChange( |
| + OffscreenPresentationInfo* presentation_info) { |
| + presentation_info->presenter_frame_id = RenderFrameHostId(); |
| + presentation_info->session_callback.Reset(); |
| + |
| + auto& route = presentation_info->route; |
| + if (route.presenter) { |
| + route.presenter.reset(); |
| + auto& controller = route.controller; |
| + if (controller && controller->state_change_listener) { |
| + controller->state_change_listener->OnSessionStateChanged( |
| + content::PRESENTATION_SESSION_STATE_DISCONNECTED); |
| + } |
| + } |
| +} |
| + |
| +void OffscreenPresentationManager::RemoveControllerAndNotifyStateChange( |
| + OffscreenPresentationInfo* presentation_info, |
| + const RenderFrameHostId& controller_frame_id) { |
| + auto& route = presentation_info->route; |
| + if (!route.controller || |
| + route.controller->controller_frame_id != controller_frame_id) |
| + return; |
| + |
| + route.controller.reset(); |
| + |
| + // Notify the corresponding presenter session object. |
| + auto& presenter = route.presenter; |
| + if (presenter && presenter->state_change_listener) { |
| + presenter->state_change_listener->OnSessionStateChanged( |
| + content::PRESENTATION_SESSION_STATE_DISCONNECTED); |
| + } |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationInfo* |
| +OffscreenPresentationManager::GetOrCreateOffscreenPresentationInfo( |
| + const std::string& presentation_id) { |
| + OffscreenPresentationInfo* info = nullptr; |
| + auto presentation_info_it = |
| + offscreen_presentation_infos_.find(presentation_id); |
| + if (presentation_info_it == offscreen_presentation_infos_.end()) { |
| + info = new OffscreenPresentationInfo; |
| + offscreen_presentation_infos_.insert(presentation_id, |
| + make_scoped_ptr(info)); |
| + } else { |
| + info = presentation_info_it->second; |
| + } |
| + DCHECK(info); |
| + return info; |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationInfo* |
| +OffscreenPresentationManager::GetOffscreenPresentationInfo( |
| + const std::string& presentation_id) const { |
| + OffscreenPresentationInfo* info = nullptr; |
| + auto presentation_info_it = |
| + offscreen_presentation_infos_.find(presentation_id); |
| + return presentation_info_it == offscreen_presentation_infos_.end() |
| + ? nullptr |
| + : presentation_info_it->second; |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationSessionInfo:: |
| + OffscreenPresentationSessionInfo( |
| + const RenderFrameHostId& controller_frame_id, |
| + const content::PresentationSessionInfo& session) |
| + : controller_frame_id(controller_frame_id), |
| + session(session), |
| + state_change_listener(nullptr) {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationSessionInfo:: |
| + ~OffscreenPresentationSessionInfo() {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationRoute:: |
| + OffscreenPresentationRoute() {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationRoute:: |
| + ~OffscreenPresentationRoute() {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationInfo:: |
| + OffscreenPresentationInfo() {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationInfo:: |
| + ~OffscreenPresentationInfo() {} |
| + |
| +bool OffscreenPresentationManager::OffscreenPresentationInfo::IsEmpty() const { |
| + return !route.presenter && !route.controller; |
| +} |
| + |
| +} // namespace media_router |