blob: 6a642fa391ebef7a2b53fc633a7366da8e13d8d5 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
#define CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
#include <set>
#include <string>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chrome/browser/ui/media_router/cast_dialog_controller.h"
#include "chrome/browser/ui/media_router/cast_dialog_model.h"
#include "chrome/browser/ui/media_router/media_cast_mode.h"
#include "chrome/browser/ui/media_router/media_route_starter.h"
#include "chrome/browser/ui/media_router/media_router_ui_helper.h"
#include "chrome/browser/ui/media_router/media_sink_with_cast_modes.h"
#include "chrome/browser/ui/media_router/media_sink_with_cast_modes_observer.h"
#include "chrome/browser/ui/media_router/presentation_request_source_observer.h"
#include "components/media_router/browser/issues_observer.h"
#include "components/media_router/browser/media_router_dialog_controller.h"
#include "components/media_router/browser/mirroring_media_controller_host.h"
#include "components/media_router/common/issue.h"
#include "components/media_router/common/media_source.h"
namespace content {
struct PresentationRequest;
class WebContents;
} // namespace content
namespace U_ICU_NAMESPACE {
class Collator;
}
namespace media_router {
class MediaRoute;
class MediaRouter;
class MediaRoutesObserver;
class MediaSink;
class RouteRequestResult;
class WebContentsDisplayObserver;
// Functions as an intermediary between MediaRouter and Views Cast dialog.
class MediaRouterUI : public CastDialogController,
public MediaSinkWithCastModesObserver,
public PresentationRequestSourceObserver,
public MirroringMediaControllerHost::Observer {
public:
// MediaRouterUI's are typically created with one of the CreateWith* methods
// below.
explicit MediaRouterUI(
std::unique_ptr<MediaRouteStarter> media_route_starter);
MediaRouterUI(const MediaRouterUI&) = delete;
MediaRouterUI& operator=(const MediaRouterUI&) = delete;
~MediaRouterUI() override;
// Creates a |MediaRouterUI| (e.g. starts listening for MediaSinks) for
// targeting the default MediaSource (if any) of |initiator|. The contents
// of the UI will change as the default MediaSource changes. If there is a
// default MediaSource, then PRESENTATION MediaCastMode will be added to
// |cast_modes_|.
static std::unique_ptr<MediaRouterUI> CreateWithDefaultMediaSource(
content::WebContents* initiator);
// Initializes mirroring sources of the tab in addition to what is done by
// |CreateWithDefaultMediaSource()|.
static std::unique_ptr<MediaRouterUI>
CreateWithDefaultMediaSourceAndMirroring(content::WebContents* initiator);
// Initializes internal state targeting the presentation specified in
// |context|. This is different from CreateWithDefaultMediaSource*() in that
// it does not listen for default media source changes, as the UI is fixed to
// the source in |context|. Init* methods can only be called once.
// |context|: Context object for the PresentationRequest. This instance will
// take ownership of it. Must not be null.
static std::unique_ptr<MediaRouterUI> CreateWithStartPresentationContext(
content::WebContents* initiator,
std::unique_ptr<StartPresentationContext> context);
// Initializes mirroring sources of the tab in addition to what is done by
// |CreateWithStartPresentationContext()|.
static std::unique_ptr<MediaRouterUI>
CreateWithStartPresentationContextAndMirroring(
content::WebContents* initiator,
std::unique_ptr<StartPresentationContext> context);
// Initializes RemotePlayback source only.
static std::unique_ptr<MediaRouterUI> CreateWithMediaSessionRemotePlayback(
content::WebContents* initiator,
media::VideoCodec video_codec,
media::AudioCodec audio_codec);
// CastDialogController:
void AddObserver(CastDialogController::Observer* observer) override;
void RemoveObserver(CastDialogController::Observer* observer) override;
void StartCasting(const std::string& sink_id,
MediaCastMode cast_mode) override;
void StopCasting(const std::string& route_id) override;
void ClearIssue(const Issue::Id& issue_id) override;
void FreezeRoute(const std::string& route_id) override;
void UnfreezeRoute(const std::string& route_id) override;
// Note that |MediaRouterUI| should not be used after |TakeMediaRouteStarter|
// is called. To enforce that, |TakeMediaRouteStarter| calls the destructor
// callback given to |RegisterDestructor| to destroy itself.
std::unique_ptr<MediaRouteStarter> TakeMediaRouteStarter() override;
void RegisterDestructor(base::OnceClosure destructor) override;
// Requests a route be created from the source mapped to
// |cast_mode|, to the sink given by |sink_id|.
// Returns true if a route request is successfully submitted.
// |OnRouteResponseReceived()| will be invoked when the route request
// completes.
virtual bool CreateRoute(const MediaSink::Id& sink_id,
MediaCastMode cast_mode);
// Calls MediaRouter to terminate the given route.
void TerminateRoute(const MediaRoute::Id& route_id);
// Returns a subset of |sinks_| that should be listed in the dialog. This
// excludes the wired display that the initiator WebContents is on.
// Also filters cloud sinks in incognito windows.
std::vector<MediaSinkWithCastModes> GetEnabledSinks() const;
// Calls MediaRouter to add the given issue.
void AddIssue(const IssueInfo& issue);
// Calls MediaRouter to remove the given issue.
void RemoveIssue(const Issue::Id& issue_id);
// Uses LoggerImpl to log current available sinks.
void LogMediaSinkStatus();
const std::vector<MediaRoute>& routes() const { return routes_; }
content::WebContents* initiator() const {
return media_route_starter()->GetWebContents();
}
private:
friend class MediaRouterViewsUITest;
friend class MediaRouterCastUiForTest;
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, SetDialogHeader);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest,
UpdateSinksWhenDialogMovesToAnotherDisplay);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, NotifyObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, SinkFriendlyName);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, ConnectingState);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, DisconnectingState);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, AddAndRemoveIssue);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, ShowDomainForHangouts);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUIIncognitoTest,
HidesCloudSinksForIncognito);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, RouteCreationTimeout);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest,
RouteCreationTimeoutIssueTitle);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest,
DesktopMirroringFailsWhenDisallowedOnMac);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest,
RouteCreationLocalFileModeInTab);
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
UIMediaRoutesObserverAssignsCurrentCastModes);
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
UIMediaRoutesObserverSkipsUnavailableCastModes);
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
UpdateSinksWhenDialogMovesToAnotherDisplay);
FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, OnFreezeInfoChanged);
class WebContentsFullscreenOnLoadedObserver;
// This class calls to refresh the UI when the highest priority issue is
// updated.
class UiIssuesObserver : public IssuesObserver {
public:
UiIssuesObserver(IssueManager* issue_manager, MediaRouterUI* ui);
UiIssuesObserver(const UiIssuesObserver&) = delete;
UiIssuesObserver& operator=(const UiIssuesObserver&) = delete;
~UiIssuesObserver() override;
// IssuesObserver:
void OnIssue(const Issue& issue) override;
void OnIssuesCleared() override;
private:
// Reference back to the owning MediaRouterUI instance.
const raw_ptr<MediaRouterUI> ui_;
};
class UIMediaRoutesObserver : public MediaRoutesObserver {
public:
using RoutesUpdatedCallback =
base::RepeatingCallback<void(const std::vector<MediaRoute>&)>;
UIMediaRoutesObserver(MediaRouter* router,
const RoutesUpdatedCallback& callback);
UIMediaRoutesObserver(const UIMediaRoutesObserver&) = delete;
UIMediaRoutesObserver& operator=(const UIMediaRoutesObserver&) = delete;
~UIMediaRoutesObserver() override;
// MediaRoutesObserver:
void OnRoutesUpdated(const std::vector<MediaRoute>& routes) override;
private:
// Callback to the owning MediaRouterUI instance.
RoutesUpdatedCallback callback_;
};
virtual void Init();
// Removes |MediaRouteStarter| listeners and alerts observers that this
// controller is now invalid.
void DetachFromMediaRouteStarter();
// PresentationRequestSourceObserver
void OnSourceUpdated(std::u16string& source_name) override;
// MirroringObserver
void OnFreezeInfoChanged() override;
// Called to update the dialog with the current list of of enabled sinks.
void UpdateSinks();
std::u16string GetSinkFriendlyNameFromId(const MediaSink::Id& sink_id);
// Creates and sends an issue if route creation timed out.
void SendIssueForRouteTimeout(
MediaCastMode cast_mode,
const MediaSink::Id& sink_id,
const std::u16string& presentation_request_source_name);
// Creates and sends an issue if casting fails due to lack of screen
// permissions.
void SendIssueForScreenPermission(const MediaSink::Id& sink_id);
// Creates and sends an issue if casting fails for any reason other than
// those above.
void SendIssueForUnableToCast(MediaCastMode cast_mode,
const MediaSink::Id& sink_id);
// Creates and sends an issue for notifying the user that the tab audio cannot
// be mirrored from their device.
void SendIssueForTabAudioNotSupported(const MediaSink::Id& sink_id);
// Creates and sends an issue when the user rejects the Cast request on the
// receiver device.
void SendIssueForUserNotAllowed(const MediaSink::Id& sink_id);
// Creates and send an issue when casting did not start because notifications
// are disabled on the receiver device.
void SendIssueForNotificationDisabled(const MediaSink::Id& sink_id);
// Returns the IssueManager associated with |router_|.
IssueManager* GetIssueManager();
// Instantiates and initializes the issues observer.
void StartObservingIssues();
void OnIssue(const Issue& issue);
void OnIssueCleared();
// Called by |routes_observer_| when the set of active routes has changed.
void OnRoutesUpdated(const std::vector<MediaRoute>& routes);
// MediaSinkWithCastModesObserver:
void OnSinksUpdated(
const std::vector<MediaSinkWithCastModes>& sinks) override;
// Callback passed to MediaRouter to receive response to route creation
// requests.
virtual void OnRouteResponseReceived(
int route_request_id,
const MediaSink::Id& sink_id,
MediaCastMode cast_mode,
const std::u16string& presentation_request_source_name,
const RouteRequestResult& result);
// Update the header text in the dialog model and notify observers.
void UpdateModelHeader(const std::u16string& source_name);
UIMediaSink ConvertToUISink(const MediaSinkWithCastModes& sink,
const MediaRoute* route,
const std::optional<Issue>& issue);
void StopObservingMirroringMediaControllerHosts();
// Returns the MediaRouter for this instance's BrowserContext.
virtual MediaRouter* GetMediaRouter() const;
const std::optional<RouteRequest> current_route_request() const {
return current_route_request_;
}
MediaRouteStarter* media_route_starter() const {
DCHECK(media_route_starter_)
<< "can't call media_route_starter() after TakeMediaRouteStarter()!";
return media_route_starter_.get();
}
// Helper factory that creates both the |MediaRouteStarter| and then a
// |MediaRouterUI| in a single step.
static std::unique_ptr<MediaRouterUI> CreateMediaRouterUI(
MediaRouterUIParameters params);
raw_ptr<content::WebContentsObserver> web_contents_observer_for_test_ =
nullptr;
// This value is set whenever there is an outstanding issue.
std::optional<Issue> issue_;
// Contains up-to-date data to show in the dialog.
CastDialogModel model_;
// This value is set when the UI requests a route to be terminated, and gets
// reset when the route is removed.
std::optional<MediaRoute::Id> terminating_route_id_;
// Observers for dialog model updates.
// TODO(takumif): CastDialogModel should manage the observers.
base::ObserverList<CastDialogController::Observer>::Unchecked observers_;
// This is non-null while this instance is registered to receive
// updates from them.
std::unique_ptr<MediaRoutesObserver> routes_observer_;
// This contains a value only when tracking a pending route request.
std::optional<RouteRequest> current_route_request_;
// Used for locale-aware sorting of sinks by name. Set during
// Init() using the current locale.
std::unique_ptr<icu::Collator> collator_;
std::vector<MediaSinkWithCastModes> sinks_;
std::vector<MediaRoute> routes_;
std::unique_ptr<MediaRouteStarter> media_route_starter_;
std::unique_ptr<IssuesObserver> issues_observer_;
// Keeps track of which display the initiator WebContents is on. This is used
// to make sure we don't show a wired display presentation over the
// controlling window.
std::unique_ptr<WebContentsDisplayObserver> display_observer_;
raw_ptr<MediaRouter> router_;
raw_ptr<LoggerImpl> logger_;
base::OnceClosure destructor_;
// NOTE: Weak pointers must be invalidated before all other member variables.
// Therefore |weak_factory_| must be placed at the end.
base::WeakPtrFactory<MediaRouterUI> weak_factory_{this};
};
} // namespace media_router
#endif // CHROME_BROWSER_UI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_