// 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. #ifndef CONTENT_BROWSER_PRESENTATION_PRESENTATION_SERVICE_IMPL_H_ #define CONTENT_BROWSER_PRESENTATION_PRESENTATION_SERVICE_IMPL_H_ #include <deque> #include <map> #include <string> #include "base/compiler_specific.h" #include "base/containers/hash_tables.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/common/presentation/presentation_service.mojom.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/presentation_screen_availability_listener.h" #include "content/public/browser/presentation_service_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/frame_navigate_params.h" #include "mojo/public/cpp/bindings/binding.h" namespace content { struct FrameNavigateParams; struct LoadCommittedDetails; struct PresentationSessionMessage; class RenderFrameHost; using NewSessionMojoCallback = mojo::Callback<void(mojom::PresentationSessionInfoPtr, mojom::PresentationErrorPtr)>; // Implementation of Mojo PresentationService. // It handles Presentation API requests coming from Blink / renderer process // and delegates the requests to the embedder's media router via // PresentationServiceDelegate. // An instance of this class tied to a RenderFrameHost and listens to events // related to the RFH via implementing WebContentsObserver. // This class is instantiated on-demand via Mojo's ConnectToRemoteService // from the renderer when the first presentation API request is handled. class CONTENT_EXPORT PresentationServiceImpl : public NON_EXPORTED_BASE(mojom::PresentationService), public WebContentsObserver, public PresentationServiceDelegate::Observer { public: ~PresentationServiceImpl() override; // Static factory method to create an instance of PresentationServiceImpl. // |render_frame_host|: The RFH the instance is associated with. // |request|: The instance will be bound to this request. Used for Mojo setup. static void CreateMojoService( RenderFrameHost* render_frame_host, mojo::InterfaceRequest<mojom::PresentationService> request); private: friend class PresentationServiceImplTest; FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, Reset); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DidNavigateThisFrame); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DidNavigateOtherFrame); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ThisRenderFrameDeleted); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, OtherRenderFrameDeleted); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DelegateFails); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, SetDefaultPresentationUrl); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, SetSameDefaultPresentationUrl); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ClearDefaultPresentationUrl); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ListenForDefaultSessionStart); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ListenForDefaultSessionStartAfterSet); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, DefaultSessionStartReset); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ReceiveSessionMessagesAfterReset); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, MaxPendingStartSessionRequests); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, MaxPendingJoinSessionRequests); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ListenForConnectionStateChange); FRIEND_TEST_ALL_PREFIXES(PresentationServiceImplTest, ListenForConnectionClose); // Maximum number of pending JoinSession requests at any given time. static const int kMaxNumQueuedSessionRequests = 10; using PresentationSessionMojoCallback = mojo::Callback<void(mojom::PresentationSessionInfoPtr)>; using SessionMessagesCallback = mojo::Callback<void(mojo::Array<mojom::SessionMessagePtr>)>; using SendMessageMojoCallback = mojo::Callback<void(bool)>; // Listener implementation owned by PresentationServiceImpl. An instance of // this is created when PresentationRequest.getAvailability() is resolved. // The instance receives screen availability results from the embedder and // propagates results back to PresentationServiceImpl. class CONTENT_EXPORT ScreenAvailabilityListenerImpl : public PresentationScreenAvailabilityListener { public: ScreenAvailabilityListenerImpl( const std::string& availability_url, PresentationServiceImpl* service); ~ScreenAvailabilityListenerImpl() override; // PresentationScreenAvailabilityListener implementation. std::string GetAvailabilityUrl() const override; void OnScreenAvailabilityChanged(bool available) override; void OnScreenAvailabilityNotSupported() override; private: const std::string availability_url_; PresentationServiceImpl* const service_; }; // Ensures the provided NewSessionMojoCallback is invoked exactly once // before it goes out of scope. class NewSessionMojoCallbackWrapper { public: explicit NewSessionMojoCallbackWrapper( const NewSessionMojoCallback& callback); ~NewSessionMojoCallbackWrapper(); void Run(mojom::PresentationSessionInfoPtr session, mojom::PresentationErrorPtr error); private: NewSessionMojoCallback callback_; DISALLOW_COPY_AND_ASSIGN(NewSessionMojoCallbackWrapper); }; // |render_frame_host|: The RFH this instance is associated with. // |web_contents|: The WebContents to observe. // |delegate|: Where Presentation API requests are delegated to. Not owned // by this class. PresentationServiceImpl( RenderFrameHost* render_frame_host, WebContents* web_contents, PresentationServiceDelegate* delegate); // PresentationService implementation. void SetDefaultPresentationURL(const mojo::String& url) override; void SetClient(mojom::PresentationServiceClientPtr client) override; void ListenForScreenAvailability(const mojo::String& url) override; void StopListeningForScreenAvailability(const mojo::String& url) override; void StartSession( const mojo::String& presentation_url, const NewSessionMojoCallback& callback) override; void JoinSession( const mojo::String& presentation_url, const mojo::String& presentation_id, const NewSessionMojoCallback& callback) override; void SendSessionMessage(mojom::PresentationSessionInfoPtr session_info, mojom::SessionMessagePtr session_message, const SendMessageMojoCallback& callback) override; void CloseConnection(const mojo::String& presentation_url, const mojo::String& presentation_id) override; void Terminate(const mojo::String& presentation_url, const mojo::String& presentation_id) override; void ListenForSessionMessages( mojom::PresentationSessionInfoPtr session) override; // Creates a binding between this object and |request|. void Bind(mojo::InterfaceRequest<mojom::PresentationService> request); // WebContentsObserver override. void DidNavigateAnyFrame( content::RenderFrameHost* render_frame_host, const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; // PresentationServiceDelegate::Observer void OnDelegateDestroyed() override; // Passed to embedder's implementation of PresentationServiceDelegate for // later invocation when default presentation has started. void OnDefaultPresentationStarted( const PresentationSessionInfo& session_info); // Finds the callback from |pending_join_session_cbs_| using // |request_session_id|. // If it exists, invoke it with |session| and |error|, then erase it from // |pending_join_session_cbs_|. // Returns true if the callback was found. bool RunAndEraseJoinSessionMojoCallback( int request_session_id, mojom::PresentationSessionInfoPtr session, mojom::PresentationErrorPtr error); // Removes all listeners and resets default presentation URL on this instance // and informs the PresentationServiceDelegate of such. void Reset(); // These functions are bound as base::Callbacks and passed to // embedder's implementation of PresentationServiceDelegate for later // invocation. void OnStartSessionSucceeded( int request_session_id, const PresentationSessionInfo& session_info); void OnStartSessionError( int request_session_id, const PresentationError& error); void OnJoinSessionSucceeded( int request_session_id, const PresentationSessionInfo& session_info); void OnJoinSessionError( int request_session_id, const PresentationError& error); void OnSendMessageCallback(bool sent); // Calls to |delegate_| to start listening for state changes for |connection|. // State changes will be returned via |OnConnectionStateChanged|. void ListenForConnectionStateChange( const PresentationSessionInfo& connection); // Passed to embedder's implementation of PresentationServiceDelegate for // later invocation when session messages arrive. void OnSessionMessages( const content::PresentationSessionInfo& session, const ScopedVector<PresentationSessionMessage>& messages, bool pass_ownership); // Associates a JoinSession |callback| with a unique request ID and // stores it in a map. // Returns a positive value on success. int RegisterJoinSessionCallback(const NewSessionMojoCallback& callback); // Invoked by the embedder's PresentationServiceDelegate when a // PresentationConnection's state has changed. void OnConnectionStateChanged( const PresentationSessionInfo& connection, const PresentationConnectionStateChangeInfo& info); // Returns true if this object is associated with |render_frame_host|. bool FrameMatches(content::RenderFrameHost* render_frame_host) const; // Embedder-specific delegate to forward Presentation requests to. // May be null if embedder does not support Presentation API. PresentationServiceDelegate* delegate_; // Proxy to the PresentationServiceClient to send results (e.g., screen // availability) to. mojom::PresentationServiceClientPtr client_; std::string default_presentation_url_; using ScreenAvailabilityListenerMap = std::map<std::string, scoped_ptr<ScreenAvailabilityListenerImpl>>; ScreenAvailabilityListenerMap screen_availability_listeners_; // For StartSession requests. // Set to a positive value when a StartSession request is being processed. int start_session_request_id_; scoped_ptr<NewSessionMojoCallbackWrapper> pending_start_session_cb_; // For JoinSession requests. base::hash_map<int, linked_ptr<NewSessionMojoCallbackWrapper>> pending_join_session_cbs_; // RAII binding of |this| to an Presentation interface request. // The binding is removed when binding_ is cleared or goes out of scope. scoped_ptr<mojo::Binding<mojom::PresentationService>> binding_; // There can be only one send message request at a time. scoped_ptr<SendMessageMojoCallback> send_message_callback_; scoped_ptr<SessionMessagesCallback> on_session_messages_callback_; // ID of the RenderFrameHost this object is associated with. int render_process_id_; int render_frame_id_; // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<PresentationServiceImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(PresentationServiceImpl); }; } // namespace content #endif // CONTENT_BROWSER_PRESENTATION_PRESENTATION_SERVICE_IMPL_H_