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..83c5835e107fac2d65e6db36cd08d24181c138f7 |
| --- /dev/null |
| +++ b/chrome/browser/media/router/offscreen_presentation_manager.cc |
| @@ -0,0 +1,203 @@ |
| +// 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" |
| + |
| +namespace media_router { |
| + |
| +OffscreenPresentationManager::OffscreenPresentationSession:: |
| + OffscreenPresentationSession( |
| + OffscreenPresentationManager::OffscreenPresentationConnection* |
| + connection) |
| + : state_change_listener_(nullptr), connection_(connection) { |
| + DCHECK(connection_); |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationSession:: |
| + ~OffscreenPresentationSession() { |
| + if (connection_) |
| + connection_->RemoveSession(this); |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession::SendMessage( |
| + scoped_ptr<content::PresentationSessionMessage> message, |
| + const content::SendMessageCallback& callback) { |
| + if (!connection_) { |
| + callback.Run(false); |
| + return; |
| + } |
| + |
| + connection_->SendMessageFrom(this, message.Pass(), callback); |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + ListenForMessages( |
| + const content::PresentationSessionMessageCallback& callback) { |
| + DCHECK(messages_callback_.is_null()); |
| + messages_callback_ = callback; |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + ListenForStateChanges(content::PresentationSessionStateListener* listener) { |
| + DCHECK(!state_change_listener_); |
| + state_change_listener_ = listener; |
|
whywhat
2015/10/13 15:21:30
nit: DCHECK(listener);
imcheng
2015/10/17 01:00:23
Done.
|
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + OnSessionStateChanged(content::PresentationSessionState state) { |
| + if (state_change_listener_) |
|
whywhat
2015/10/13 15:21:30
Shouldn't be a DCHECK rather so we always have a l
imcheng
2015/10/17 01:00:23
I wouldn't expect a listener to always exist when
|
| + state_change_listener_->OnSessionStateChanged(state); |
| + |
| + if (state == content::PRESENTATION_SESSION_STATE_DISCONNECTED) |
| + connection_ = nullptr; |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + OnMessageReceived(scoped_ptr<content::PresentationSessionMessage> message) { |
| + if (!messages_callback_.is_null()) { |
|
whywhat
2015/10/13 15:21:30
nit: Seems like the inverse condition would be mor
imcheng
2015/10/17 01:00:23
Done. For queueing, it can be done along with batc
|
| + ScopedVector<content::PresentationSessionMessage> messages; |
|
mark a. foltz
2015/10/12 21:56:10
Please add a TODO to handle multiple messages at o
imcheng
2015/10/17 01:00:23
Moved comment from OffscreenPresentationConnection
|
| + messages.push_back(message.release()); |
| + messages_callback_.Run(messages.Pass(), true); |
| + } |
| +} |
| + |
| +OffscreenPresentationManager::~OffscreenPresentationManager() {} |
| + |
| +void OffscreenPresentationManager::RegisterOffscreenPresentationReceiver( |
| + const std::string& presentation_id, |
| + const ReceiverSessionAvailableCallback& receiver_available_callback) { |
| + DCHECK(!ContainsKey(offscreen_presentations_, presentation_id)); |
|
mark a. foltz
2015/10/12 21:56:10
How do you intend to handle this case in release b
imcheng
2015/10/17 01:00:23
This is a "can't happen" situation since the prese
|
| + |
| + offscreen_presentations_.insert( |
| + presentation_id, |
| + make_scoped_ptr(new OffscreenPresentation( |
| + presentation_id, receiver_available_callback, this))); |
| +} |
| + |
| +void OffscreenPresentationManager::UnregisterOffscreenPresentationReceiver( |
| + const std::string& presentation_id) { |
| + DCHECK(ContainsKey(offscreen_presentations_, presentation_id)); |
|
mark a. foltz
2015/10/12 21:56:10
Similar comments
imcheng
2015/10/17 01:00:23
Ditto.
|
| + offscreen_presentations_.erase(presentation_id); |
| +} |
| + |
| +scoped_ptr<OffscreenPresentationManager::OffscreenPresentationSession> |
| +OffscreenPresentationManager::ConnectToOffscreenPresentation( |
| + const std::string& presentation_id, |
| + const RenderFrameHostId& controller_frame_id) { |
| + auto it = offscreen_presentations_.find(presentation_id); |
| + if (it == offscreen_presentations_.end()) |
| + return scoped_ptr<OffscreenPresentationSession>(); |
| + |
| + return it->second->AddConnection(controller_frame_id); |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationManager() {} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationConnection:: |
| + OffscreenPresentationConnection( |
| + const RenderFrameHostId& controller_frame_id, |
| + OffscreenPresentation* presentation) |
| + : controller_frame_id_(controller_frame_id), |
| + presentation_(presentation), |
| + controller_(nullptr), |
| + receiver_(nullptr) { |
| + DCHECK(presentation_); |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationConnection::Init( |
| + OffscreenPresentationManager::OffscreenPresentationSession* controller, |
| + OffscreenPresentationManager::OffscreenPresentationSession* receiver) { |
| + DCHECK(!controller_); |
| + DCHECK(!receiver_); |
| + DCHECK(controller); |
| + DCHECK(receiver); |
| + controller_ = controller; |
| + receiver_ = receiver; |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentationConnection:: |
| + ~OffscreenPresentationConnection() { |
| + DCHECK(!controller_ || !receiver_); |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationConnection:: |
| + RemoveSession(OffscreenPresentationSession* session) { |
| + DCHECK(session); |
| + OffscreenPresentationManager::OffscreenPresentationSession* other_session = |
| + nullptr; |
| + if (session == controller_) { |
| + controller_ = nullptr; |
| + other_session = receiver_; |
| + } else { |
| + DCHECK(session == receiver_); |
| + receiver_ = nullptr; |
| + other_session = controller_; |
| + } |
| + |
| + DCHECK(other_session); |
| + other_session->OnSessionStateChanged( |
| + content::PRESENTATION_SESSION_STATE_DISCONNECTED); |
| + presentation_->RemoveConnection(controller_frame_id_); |
| + // |this| is deleted beyond this point. |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationConnection:: |
| + SendMessageFrom(OffscreenPresentationSession* session, |
| + scoped_ptr<content::PresentationSessionMessage> message, |
| + const content::SendMessageCallback& callback) { |
| + OffscreenPresentationManager::OffscreenPresentationSession* other_session = |
| + session == controller_ ? receiver_ : controller_; |
| + DCHECK(other_session); |
| + |
| + // TODO(imcheng): Implement message batching. |
| + session->OnMessageReceived(message.Pass()); |
|
whywhat
2015/10/13 15:21:30
Did you mean other_session?
imcheng
2015/10/17 01:00:23
Done. Good catch, thanks!
|
| + callback.Run(true); |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentation::OffscreenPresentation( |
| + const std::string& presentation_id, |
| + const ReceiverSessionAvailableCallback& receiver_available_callback, |
| + OffscreenPresentationManager* manager) |
| + : presentation_id_(presentation_id), |
| + receiver_available_callback_(receiver_available_callback), |
| + manager_(manager) { |
| + DCHECK(!receiver_available_callback_.is_null()); |
| + DCHECK(manager_); |
| +} |
| + |
| +OffscreenPresentationManager::OffscreenPresentation::~OffscreenPresentation() { |
| + // The receiver must have destroyed all connections before unregistration. |
|
mark a. foltz
2015/10/12 21:56:10
Can unregistration do this as a side effect?
It w
imcheng
2015/10/17 01:00:23
All of the scenarios should already be covered by
|
| + DCHECK(connections_.empty()); |
| +} |
| + |
| +scoped_ptr<OffscreenPresentationManager::OffscreenPresentationSession> |
| +OffscreenPresentationManager::OffscreenPresentation::AddConnection( |
| + const RenderFrameHostId& controller_frame_id) { |
| + DCHECK(!ContainsKey(connections_, controller_frame_id)); |
|
mark a. foltz
2015/10/12 21:56:10
Behavior in release builds?
imcheng
2015/10/17 01:00:23
Changed this to LOG(ERROR) and return nullptr. In
|
| + |
| + scoped_ptr<OffscreenPresentationConnection> connection( |
| + new OffscreenPresentationConnection(controller_frame_id, this)); |
| + scoped_ptr<OffscreenPresentationSession> controller_session( |
| + new OffscreenPresentationSession(connection.get())); |
| + scoped_ptr<OffscreenPresentationSession> receiver_session( |
| + new OffscreenPresentationSession(connection.get())); |
| + |
| + connection->Init(controller_session.get(), receiver_session.get()); |
| + connections_.insert(controller_frame_id, connection.Pass()); |
| + receiver_available_callback_.Run(receiver_session.Pass()); |
| + return controller_session.Pass(); |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentation::RemoveConnection( |
| + const RenderFrameHostId& controller_frame_id) { |
| + DCHECK(ContainsKey(connections_, controller_frame_id)); |
|
mark a. foltz
2015/10/12 21:56:10
Ditto
imcheng
2015/10/17 01:00:23
erase() is no-op is key is not found so we should
|
| + connections_.erase(controller_frame_id); |
| +} |
| + |
| +} // namespace media_router |