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 |