Index: chrome/browser/media/router/presentation_service_delegate_impl.cc |
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc |
index e29a9057d8c2ed3661a075a7aaa743ec3b4b43f6..93b0f0bc9b534e297d214e9424ea046d6c67dbe4 100644 |
--- a/chrome/browser/media/router/presentation_service_delegate_impl.cc |
+++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc |
@@ -10,7 +10,6 @@ |
#include "base/containers/scoped_ptr_hash_map.h" |
#include "base/containers/small_map.h" |
-#include "base/guid.h" |
#include "base/strings/string_util.h" |
#include "base/strings/stringprintf.h" |
#include "chrome/browser/media/router/create_presentation_connection_request.h" |
@@ -20,6 +19,8 @@ |
#include "chrome/browser/media/router/media_router_factory.h" |
#include "chrome/browser/media/router/media_sink.h" |
#include "chrome/browser/media/router/media_source_helper.h" |
+#include "chrome/browser/media/router/offscreen_presentation_manager.h" |
+#include "chrome/browser/media/router/offscreen_presentation_manager_factory.h" |
#include "chrome/browser/media/router/presentation_media_sinks_observer.h" |
#include "chrome/browser/media/router/presentation_session_messages_observer.h" |
#include "chrome/browser/media/router/route_request_result.h" |
@@ -37,6 +38,9 @@ using content::RenderFrameHost; |
namespace media_router { |
+using OffscreenPresentationConnection = |
+ OffscreenPresentationManager::OffscreenPresentationConnection; |
+ |
namespace { |
using DelegateObserver = content::PresentationServiceDelegate::Observer; |
@@ -79,6 +83,9 @@ class PresentationFrame { |
bool HasScreenAvailabilityListenerForTest( |
const MediaSource::Id& source_id) const; |
std::string GetDefaultPresentationId() const; |
+ void SendMessage(const content::PresentationSessionInfo& session, |
+ std::unique_ptr<content::PresentationSessionMessage> message, |
+ const content::SendMessageCallback send_message_cb); |
void ListenForConnectionStateChange( |
const content::PresentationSessionInfo& connection, |
const content::PresentationConnectionStateChangedCallback& |
@@ -88,15 +95,16 @@ class PresentationFrame { |
const content::PresentationSessionMessageCallback& message_cb); |
void Reset(); |
- void RemoveConnection(const std::string& presentation_id, |
- const MediaRoute::Id& route_id); |
+ void RemoveConnection( |
+ const std::string& presentation_id, |
+ const content::PresentationConnectionStateChangeInfo& state_change_info); |
const MediaRoute::Id GetRouteId(const std::string& presentation_id) const; |
const std::vector<MediaRoute::Id> GetRouteIds() const; |
void OnPresentationSessionStarted( |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id); |
+ const MediaRoute& route); |
void OnPresentationServiceDelegateDestroyed() const; |
void set_delegate_observer(DelegateObserver* observer) { |
@@ -106,11 +114,25 @@ class PresentationFrame { |
private: |
MediaSource GetMediaSourceFromListener( |
content::PresentationScreenAvailabilityListener* listener) const; |
+ // Gets the OffscreenPresentationConnection associated with |presentation_id| |
+ // within this frame. Returns nullptr if |presentation_id| is not |
+ // registered as an offscreen presentation. |
+ OffscreenPresentationConnection* FindOffscreenConnection( |
+ const std::string& presentation_id) const; |
+ |
base::SmallMap<std::map<std::string, MediaRoute::Id>> |
presentation_id_to_route_id_; |
base::SmallMap< |
std::map<std::string, std::unique_ptr<PresentationMediaSinksObserver>>> |
url_to_sinks_observer_; |
+ |
+ // Controller objects for offscreen presentations. Note that offscreen |
+ // presentations are manipulated with these objects instead of the observers |
+ // and MediaRouter objects below. Maps from presentation ID to the |
+ // corresponding OffscreenPresentationConnection within this frame. |
+ std::map<std::string, std::unique_ptr<OffscreenPresentationConnection>> |
+ offscreen_presentation_connections_; |
+ |
base::ScopedPtrHashMap< |
MediaRoute::Id, |
std::unique_ptr<PresentationConnectionStateSubscription>> |
@@ -119,9 +141,11 @@ class PresentationFrame { |
RenderFrameHostId render_frame_host_id_; |
- // References to the owning WebContents, and the corresponding MediaRouter. |
+ // References to the owning WebContents, the corresponding MediaRouter, and |
+ // OffscreenPresentationManager. |
const content::WebContents* web_contents_; |
- MediaRouter* router_; |
+ MediaRouter* const router_; |
+ OffscreenPresentationManager* const offscreen_presentation_manager_; |
DelegateObserver* delegate_observer_; |
}; |
@@ -133,9 +157,13 @@ PresentationFrame::PresentationFrame( |
: render_frame_host_id_(render_frame_host_id), |
web_contents_(web_contents), |
router_(router), |
+ offscreen_presentation_manager_( |
+ OffscreenPresentationManagerFactory::GetOrCreateForBrowserContext( |
+ web_contents_->GetBrowserContext())), |
delegate_observer_(nullptr) { |
DCHECK(web_contents_); |
DCHECK(router_); |
+ DCHECK(offscreen_presentation_manager_); |
} |
PresentationFrame::~PresentationFrame() { |
@@ -148,8 +176,31 @@ void PresentationFrame::OnPresentationServiceDelegateDestroyed() const { |
void PresentationFrame::OnPresentationSessionStarted( |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id) { |
- presentation_id_to_route_id_[session.presentation_id] = route_id; |
+ const MediaRoute& route) { |
+ const std::string& presentation_id = session.presentation_id; |
+ presentation_id_to_route_id_[presentation_id] = route.media_route_id(); |
+ if (route.is_offscreen_presentation()) { |
+ DCHECK(!ContainsKey(offscreen_presentation_connections_, presentation_id)); |
+ std::unique_ptr<OffscreenPresentationConnection> offscreen_connection = |
+ offscreen_presentation_manager_->ConnectToOffscreenPresentation( |
+ presentation_id, render_frame_host_id_); |
+ |
+ // |offscreen_connection| could be nullptr for a couple of reasons: |
+ // 1) An OffscreenPresentationConnection for presentation_id already exists. |
+ // This could be due to a race between two or more route requests. |
+ // 2) The receiver is already gone, so the route will die soon. This should |
+ // happen very rarely since the receiver has to unregister itself between |
+ // when offscreen presentation was created and when the resulting route |
+ // arrived at MR. |
+ if (!offscreen_connection) { |
+ // TODO(imcheng): we should probably reject the route request and detach |
+ // the route in this case. crbug.com/513859 |
+ DLOG(ERROR) << "CreateOffscreenPresentationConnection returned nullptr."; |
+ } else { |
+ offscreen_presentation_connections_.insert( |
+ std::make_pair(presentation_id, std::move(offscreen_connection))); |
+ } |
+ } |
} |
const MediaRoute::Id PresentationFrame::GetRouteId( |
@@ -158,13 +209,6 @@ const MediaRoute::Id PresentationFrame::GetRouteId( |
return it != presentation_id_to_route_id_.end() ? it->second : ""; |
} |
-const std::vector<MediaRoute::Id> PresentationFrame::GetRouteIds() const { |
- std::vector<MediaRoute::Id> route_ids; |
- for (const auto& e : presentation_id_to_route_id_) |
- route_ids.push_back(e.second); |
- return route_ids; |
-} |
- |
bool PresentationFrame::SetScreenAvailabilityListener( |
content::PresentationScreenAvailabilityListener* listener) { |
MediaSource source(GetMediaSourceFromListener(listener)); |
@@ -205,18 +249,65 @@ bool PresentationFrame::HasScreenAvailabilityListenerForTest( |
void PresentationFrame::Reset() { |
for (const auto& pid_route_id : presentation_id_to_route_id_) |
router_->DetachRoute(pid_route_id.second); |
- |
presentation_id_to_route_id_.clear(); |
url_to_sinks_observer_.clear(); |
+ offscreen_presentation_connections_.clear(); |
connection_state_subscriptions_.clear(); |
session_messages_observers_.clear(); |
} |
-void PresentationFrame::RemoveConnection(const std::string& presentation_id, |
- const MediaRoute::Id& route_id) { |
+void PresentationFrame::SendMessage( |
+ const content::PresentationSessionInfo& session, |
+ std::unique_ptr<content::PresentationSessionMessage> message, |
+ const content::SendMessageCallback send_message_cb) { |
+ OffscreenPresentationConnection* offscreen_connection = |
+ FindOffscreenConnection(session.presentation_id); |
+ if (offscreen_connection) { |
+ offscreen_connection->SendMessage(std::move(message), send_message_cb); |
+ } else { |
+ auto it = presentation_id_to_route_id_.find(session.presentation_id); |
+ if (it == presentation_id_to_route_id_.end()) { |
+ DVLOG(2) << "ListenForSessionMessages: no route for " |
+ << session.presentation_id; |
+ return; |
+ } |
+ if (message->is_binary()) { |
+ router_->SendRouteBinaryMessage(it->second, std::move(message->data), |
+ send_message_cb); |
+ } else { |
+ router_->SendRouteMessage(it->second, message->message, send_message_cb); |
+ } |
+ } |
+} |
+ |
+OffscreenPresentationConnection* PresentationFrame::FindOffscreenConnection( |
+ const std::string& presentation_id) const { |
+ auto offscreen_connection_it = |
+ offscreen_presentation_connections_.find(presentation_id); |
+ return offscreen_connection_it != offscreen_presentation_connections_.end() |
+ ? offscreen_connection_it->second.get() |
+ : nullptr; |
+} |
+ |
+void PresentationFrame::RemoveConnection( |
+ const std::string& presentation_id, |
+ const content::PresentationConnectionStateChangeInfo& state_change_info) { |
+ auto it = presentation_id_to_route_id_.find(presentation_id); |
+ if (it == presentation_id_to_route_id_.end()) |
+ return; |
+ |
// Remove the presentation id mapping so a later call to Reset is a no-op. |
- presentation_id_to_route_id_.erase(presentation_id); |
+ MediaRoute::Id route_id = it->second; |
+ presentation_id_to_route_id_.erase(it); |
+ |
+ if (OffscreenPresentationConnection* offscreen_connection = |
+ FindOffscreenConnection(presentation_id)) { |
+ offscreen_connection->RemoveFromPresentation(state_change_info); |
+ offscreen_presentation_connections_.erase(presentation_id); |
+ return; |
+ } |
+ // Not an offscreen presentation. |
// We no longer need to observe route messages. |
auto observer_iter = std::find_if( |
session_messages_observers_.begin(), session_messages_observers_.end(), |
@@ -241,6 +332,14 @@ void PresentationFrame::ListenForConnectionStateChange( |
return; |
} |
+ // An offscreen controller presentation listens for state change from both |
+ // the receiver and MR. |
+ OffscreenPresentationConnection* offscreen_connection = |
+ FindOffscreenConnection(connection.presentation_id); |
+ if (offscreen_connection) { |
+ offscreen_connection->ListenForStateChange(state_changed_cb); |
+ } |
+ |
const MediaRoute::Id& route_id = it->second; |
if (connection_state_subscriptions_.contains(route_id)) { |
DLOG(ERROR) << __FUNCTION__ << "Already listening connection state change " |
@@ -264,6 +363,15 @@ void PresentationFrame::ListenForSessionMessages( |
return; |
} |
+ // An offscreen controller presentation listens for messages from both the |
+ // receiver and MR. |
+ OffscreenPresentationConnection* offscreen_connection = |
+ FindOffscreenConnection(session.presentation_id); |
+ if (offscreen_connection) { |
+ offscreen_connection->ListenForMessages(message_cb); |
+ } |
+ |
+ // TODO(imcheng): Limit to 1 observer per presentation. |
session_messages_observers_.push_back( |
new PresentationSessionMessagesObserver(message_cb, it->second, router_)); |
} |
@@ -291,6 +399,10 @@ class PresentationFrameManager { |
bool RemoveScreenAvailabilityListener( |
const RenderFrameHostId& render_frame_host_id, |
content::PresentationScreenAvailabilityListener* listener); |
+ void SendMessage(const RenderFrameHostId& render_frame_host_id, |
+ const content::PresentationSessionInfo& session, |
+ std::unique_ptr<content::PresentationSessionMessage> message, |
+ const content::SendMessageCallback send_message_cb); |
void ListenForConnectionStateChange( |
const RenderFrameHostId& render_frame_host_id, |
const content::PresentationSessionInfo& connection, |
@@ -318,9 +430,10 @@ class PresentationFrameManager { |
PresentationServiceDelegateImpl::DefaultPresentationRequestObserver* |
observer); |
void Reset(const RenderFrameHostId& render_frame_host_id); |
- void RemoveConnection(const RenderFrameHostId& render_frame_host_id, |
- const MediaRoute::Id& route_id, |
- const std::string& presentation_id); |
+ void RemoveConnection( |
+ const RenderFrameHostId& render_frame_host_id, |
+ const std::string& presentation_id, |
+ const content::PresentationConnectionStateChangeInfo& state_change_info); |
bool HasScreenAvailabilityListenerForTest( |
const RenderFrameHostId& render_frame_host_id, |
const MediaSource::Id& source_id) const; |
@@ -329,16 +442,14 @@ class PresentationFrameManager { |
void OnPresentationSessionStarted( |
const RenderFrameHostId& render_frame_host_id, |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id); |
+ const MediaRoute& route); |
void OnDefaultPresentationSessionStarted( |
const PresentationRequest& request, |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id); |
+ const MediaRoute& route); |
const MediaRoute::Id GetRouteId(const RenderFrameHostId& render_frame_host_id, |
const std::string& presentation_id) const; |
- const std::vector<MediaRoute::Id> GetRouteIds( |
- const RenderFrameHostId& render_frame_host_id) const; |
const PresentationRequest* default_presentation_request() const { |
return default_presentation_request_.get(); |
@@ -379,14 +490,14 @@ class PresentationFrameManager { |
default_presentation_request_observers_; |
// References to the owning WebContents, and the corresponding MediaRouter. |
+ content::WebContents* const web_contents_; |
MediaRouter* router_; |
- content::WebContents* web_contents_; |
}; |
PresentationFrameManager::PresentationFrameManager( |
content::WebContents* web_contents, |
MediaRouter* router) |
- : router_(router), web_contents_(web_contents) { |
+ : web_contents_(web_contents), router_(router) { |
DCHECK(web_contents_); |
DCHECK(router_); |
} |
@@ -399,19 +510,19 @@ PresentationFrameManager::~PresentationFrameManager() { |
void PresentationFrameManager::OnPresentationSessionStarted( |
const RenderFrameHostId& render_frame_host_id, |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id) { |
+ const MediaRoute& route) { |
auto presentation_frame = GetOrAddPresentationFrame(render_frame_host_id); |
- presentation_frame->OnPresentationSessionStarted(session, route_id); |
+ presentation_frame->OnPresentationSessionStarted(session, route); |
} |
void PresentationFrameManager::OnDefaultPresentationSessionStarted( |
const PresentationRequest& request, |
const content::PresentationSessionInfo& session, |
- const MediaRoute::Id& route_id) { |
+ const MediaRoute& route) { |
auto presentation_frame = |
presentation_frames_.get(request.render_frame_host_id()); |
if (presentation_frame) |
- presentation_frame->OnPresentationSessionStarted(session, route_id); |
+ presentation_frame->OnPresentationSessionStarted(session, route); |
if (default_presentation_request_ && |
default_presentation_request_->Equals(request)) { |
@@ -427,13 +538,6 @@ const MediaRoute::Id PresentationFrameManager::GetRouteId( |
: ""; |
} |
-const std::vector<MediaRoute::Id> PresentationFrameManager::GetRouteIds( |
- const RenderFrameHostId& render_frame_host_id) const { |
- auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
- return presentation_frame ? presentation_frame->GetRouteIds() |
- : std::vector<MediaRoute::Id>(); |
-} |
- |
bool PresentationFrameManager::SetScreenAvailabilityListener( |
const RenderFrameHostId& render_frame_host_id, |
content::PresentationScreenAvailabilityListener* listener) { |
@@ -459,6 +563,24 @@ bool PresentationFrameManager::HasScreenAvailabilityListenerForTest( |
presentation_frame->HasScreenAvailabilityListenerForTest(source_id); |
} |
+void PresentationFrameManager::SendMessage( |
+ const RenderFrameHostId& render_frame_host_id, |
+ const content::PresentationSessionInfo& session, |
+ std::unique_ptr<content::PresentationSessionMessage> message, |
+ const content::SendMessageCallback send_message_cb) { |
+ PresentationFrame* presentation_frame = |
+ presentation_frames_.get(render_frame_host_id); |
+ if (!presentation_frame) { |
+ DVLOG(2) << "SendMessage: PresentationFrame does not exist " |
+ << "for: (" << render_frame_host_id.first << ", " |
+ << render_frame_host_id.second << ")"; |
+ send_message_cb.Run(false); |
+ return; |
+ } |
+ |
+ presentation_frame->SendMessage(session, std::move(message), send_message_cb); |
+} |
+ |
void PresentationFrameManager::ListenForConnectionStateChange( |
const RenderFrameHostId& render_frame_host_id, |
const content::PresentationSessionInfo& connection, |
@@ -548,11 +670,11 @@ void PresentationFrameManager::Reset( |
void PresentationFrameManager::RemoveConnection( |
const RenderFrameHostId& render_frame_host_id, |
- const MediaRoute::Id& route_id, |
- const std::string& presentation_id) { |
+ const std::string& presentation_id, |
+ const content::PresentationConnectionStateChangeInfo& state_change_info) { |
auto presentation_frame = presentation_frames_.get(render_frame_host_id); |
if (presentation_frame) |
- presentation_frame->RemoveConnection(route_id, presentation_id); |
+ presentation_frame->RemoveConnection(presentation_id, state_change_info); |
} |
PresentationFrame* PresentationFrameManager::GetOrAddPresentationFrame( |
@@ -686,11 +808,12 @@ void PresentationServiceDelegateImpl::OnJoinRouteResponse( |
DVLOG(1) << "OnJoinRouteResponse: " |
<< "route_id: " << result.route()->media_route_id() |
<< ", presentation URL: " << session.presentation_url |
- << ", presentation ID: " << session.presentation_id; |
+ << ", presentation ID: " << session.presentation_id |
+ << ", offscreen? " << result.route()->is_offscreen_presentation(); |
DCHECK_EQ(session.presentation_id, result.presentation_id()); |
frame_manager_->OnPresentationSessionStarted( |
RenderFrameHostId(render_process_id, render_frame_id), session, |
- result.route()->media_route_id()); |
+ *result.route()); |
success_cb.Run(session); |
} |
} |
@@ -700,14 +823,15 @@ void PresentationServiceDelegateImpl::OnStartSessionSucceeded( |
int render_frame_id, |
const content::PresentationSessionStartedCallback& success_cb, |
const content::PresentationSessionInfo& new_session, |
- const MediaRoute::Id& route_id) { |
+ const MediaRoute& route) { |
+ const MediaRoute::Id& route_id = route.media_route_id(); |
DVLOG(1) << "OnStartSessionSucceeded: " |
<< "route_id: " << route_id |
<< ", presentation URL: " << new_session.presentation_url |
<< ", presentation ID: " << new_session.presentation_id; |
frame_manager_->OnPresentationSessionStarted( |
RenderFrameHostId(render_process_id, render_frame_id), new_session, |
- route_id); |
+ route); |
success_cb.Run(new_session); |
} |
@@ -778,7 +902,12 @@ void PresentationServiceDelegateImpl::CloseConnection( |
} |
router_->DetachRoute(route_id); |
- frame_manager_->RemoveConnection(rfh_id, presentation_id, route_id); |
+ |
+ content::PresentationConnectionStateChangeInfo state_change_info( |
+ content::PRESENTATION_CONNECTION_STATE_CLOSED); |
+ state_change_info.close_reason = |
+ content::PRESENTATION_CONNECTION_CLOSE_REASON_CLOSED; |
+ frame_manager_->RemoveConnection(rfh_id, presentation_id, state_change_info); |
// TODO(mfoltz): close() should always succeed so there is no need to keep the |
// state_changed_cb around - remove it and fire the ChangeEvent on the |
// PresentationConnection in Blink. |
@@ -795,8 +924,12 @@ void PresentationServiceDelegateImpl::Terminate( |
DVLOG(1) << "No active route for: " << presentation_id; |
return; |
} |
+ |
router_->TerminateRoute(route_id); |
- frame_manager_->RemoveConnection(rfh_id, presentation_id, route_id); |
+ |
+ content::PresentationConnectionStateChangeInfo state_change_info( |
+ content::PRESENTATION_CONNECTION_STATE_TERMINATED); |
+ frame_manager_->RemoveConnection(rfh_id, presentation_id, state_change_info); |
} |
void PresentationServiceDelegateImpl::ListenForSessionMessages( |
@@ -804,9 +937,9 @@ void PresentationServiceDelegateImpl::ListenForSessionMessages( |
int render_frame_id, |
const content::PresentationSessionInfo& session, |
const content::PresentationSessionMessageCallback& message_cb) { |
- frame_manager_->ListenForSessionMessages( |
- RenderFrameHostId(render_process_id, render_frame_id), session, |
- message_cb); |
+ RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id); |
+ frame_manager_->ListenForSessionMessages(render_frame_host_id, session, |
+ message_cb); |
} |
void PresentationServiceDelegateImpl::SendMessage( |
@@ -814,22 +947,10 @@ void PresentationServiceDelegateImpl::SendMessage( |
int render_frame_id, |
const content::PresentationSessionInfo& session, |
std::unique_ptr<content::PresentationSessionMessage> message, |
- const SendMessageCallback& send_message_cb) { |
- const MediaRoute::Id& route_id = frame_manager_->GetRouteId( |
- RenderFrameHostId(render_process_id, render_frame_id), |
- session.presentation_id); |
- if (route_id.empty()) { |
- DVLOG(1) << "No active route for " << session.presentation_id; |
- send_message_cb.Run(false); |
- return; |
- } |
- |
- if (message->is_binary()) { |
- router_->SendRouteBinaryMessage(route_id, std::move(message->data), |
- send_message_cb); |
- } else { |
- router_->SendRouteMessage(route_id, message->message, send_message_cb); |
- } |
+ const content::SendMessageCallback& send_message_cb) { |
+ RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id); |
+ frame_manager_->SendMessage(render_frame_host_id, session, std::move(message), |
+ send_message_cb); |
} |
void PresentationServiceDelegateImpl::ListenForConnectionStateChange( |
@@ -843,6 +964,18 @@ void PresentationServiceDelegateImpl::ListenForConnectionStateChange( |
state_changed_cb); |
} |
+std::vector<content::PresentationSessionInfo> |
+PresentationServiceDelegateImpl::GetReceiverConnections( |
+ int render_process_id, |
+ int render_frame_id, |
+ const content::PresentationSessionStartedCallback& callback) { |
+ // We only support receiver APIs in offscreen tabs created for offscreen |
+ // presentations. |
+ // See ReceiverPresentationServiceDelegateImpl for details. |
+ NOTIMPLEMENTED(); |
+ return std::vector<content::PresentationSessionInfo>(); |
+} |
+ |
void PresentationServiceDelegateImpl::OnRouteResponse( |
const PresentationRequest& presentation_request, |
const RouteRequestResult& result) { |
@@ -852,7 +985,7 @@ void PresentationServiceDelegateImpl::OnRouteResponse( |
content::PresentationSessionInfo session_info( |
presentation_request.presentation_url(), result.presentation_id()); |
frame_manager_->OnDefaultPresentationSessionStarted( |
- presentation_request, session_info, result.route()->media_route_id()); |
+ presentation_request, session_info, *result.route()); |
} |
void PresentationServiceDelegateImpl::AddDefaultPresentationRequestObserver( |