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..111c3d58ab58ff37bccd87759ae2b3743ef0db6f |
| --- /dev/null |
| +++ b/chrome/browser/media/router/offscreen_presentation_manager.cc |
| @@ -0,0 +1,221 @@ |
| +// 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 { |
| + |
| +// OffscreenPresentationSession implementation. //////////////////////////////// |
| + |
| +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()); |
| + DCHECK(!callback.is_null()); |
| + messages_callback_ = callback; |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + ListenForStateChanges(content::PresentationSessionStateListener* listener) { |
| + DCHECK(!state_change_listener_); |
| + DCHECK(listener); |
| + state_change_listener_ = listener; |
| +} |
| + |
| +void OffscreenPresentationManager::OffscreenPresentationSession:: |
| + OnSessionStateChanged(content::PresentationSessionState state) { |
| + if (state_change_listener_) |
| + 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()) |
| + return; |
| + |
| + // TODO(imcheng): Implement message queueing/batching. |
| + ScopedVector<content::PresentationSessionMessage> messages; |
| + messages.push_back(message.release()); |
| + messages_callback_.Run(messages.Pass(), true); |
| +} |
| + |
| + |
| +// OffscreenPresentationManager implementation. //////////////////////////////// |
| + |
| +OffscreenPresentationManager::~OffscreenPresentationManager() {} |
| + |
| +void OffscreenPresentationManager::RegisterOffscreenPresentationReceiver( |
| + const std::string& presentation_id, |
| + const ReceiverSessionAvailableCallback& receiver_available_callback) { |
| + DCHECK(!ContainsKey(offscreen_presentations_, presentation_id)); |
| + |
| + 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)); |
| + 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() {} |
| + |
| + |
| +// OffscreenPresentationConnection implementation. ///////////////////////////// |
| + |
| +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); |
| + 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) { |
| + OffscreenPresentationSession* other_session = |
| + session == controller_ ? receiver_ : controller_; |
| + DCHECK(other_session); |
| + |
| + other_session->OnMessageReceived(message.Pass()); |
| + callback.Run(true); |
| +} |
| + |
| + |
| +// OffscreenPresentation implementation. /////////////////////////////////////// |
| + |
| +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. |
| + DCHECK(connections_.empty()); |
| +} |
| + |
| +scoped_ptr<OffscreenPresentationManager::OffscreenPresentationSession> |
| +OffscreenPresentationManager::OffscreenPresentation::AddConnection( |
| + const RenderFrameHostId& controller_frame_id) { |
| + if (ContainsKey(connections_, controller_frame_id)) { |
| + LOG(ERROR) << "Frame " << controller_frame_id.first << ", " |
|
miu
2015/10/20 00:56:50
nit: LOG(DFATAL) instead?
imcheng
2016/06/13 22:30:01
Changed to DLOG(ERROR). We probably do not need th
|
| + << controller_frame_id.second << " already registered as a " |
| + << "controller for presentation " << presentation_id_; |
| + return scoped_ptr<OffscreenPresentationSession>(); |
| + } |
| + |
| + 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)); |
| + connections_.erase(controller_frame_id); |
| +} |
| + |
| +} // namespace media_router |