cef/libcef/browser/media_router/media_router_manager.cc
Marshall Greenblatt 48fc0bd220 Make CefBrowserContext an abstract base class (see issue #2969)
Existing CefBrowserContext functionality is now split between
CefBrowserContext and AlloyBrowserContext. Runtime implementations of
CefBrowserContext will provide access to the content::BrowserContext and
Profile types via different inheritance paths. For example, the Alloy
runtime uses ChromeProfileAlloy and the Chrome runtime uses ProfileImpl.

This change also renames CefResourceContext to CefIOThreadState to more
accurately represent its purpose as it no longer needs to extend
content::ResourceContext.
2020-07-01 15:35:07 -04:00

294 lines
9.7 KiB
C++

// Copyright (c) 2020 The Chromium Embedded Framework 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 "libcef/browser/media_router/media_router_manager.h"
#include "libcef/browser/browser_context.h"
#include "libcef/browser/thread_util.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/media_routes_observer.h"
#include "chrome/browser/media/router/route_message_observer.h"
#include "chrome/browser/media/router/route_message_util.h"
namespace {
const int kTimeoutMs = 5 * 1000;
const char kDefaultPresentationUrl[] = "https://google.com";
} // namespace
class CefMediaRoutesObserver : public media_router::MediaRoutesObserver {
public:
explicit CefMediaRoutesObserver(CefMediaRouterManager* manager)
: media_router::MediaRoutesObserver(manager->GetMediaRouter()),
manager_(manager) {}
void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
const std::vector<media_router::MediaRoute::Id>&
joinable_route_ids) override {
manager_->routes_ = routes;
manager_->NotifyCurrentRoutes();
}
private:
CefMediaRouterManager* const manager_;
DISALLOW_COPY_AND_ASSIGN(CefMediaRoutesObserver);
};
// Used to receive messages if PresentationConnection is not supported.
class CefRouteMessageObserver : public media_router::RouteMessageObserver {
public:
CefRouteMessageObserver(CefMediaRouterManager* manager,
const media_router::MediaRoute& route)
: media_router::RouteMessageObserver(manager->GetMediaRouter(),
route.media_route_id()),
manager_(manager),
route_(route) {}
void OnMessagesReceived(
CefMediaRouterManager::MediaMessageVector messages) override {
manager_->OnMessagesReceived(route_, messages);
}
private:
CefMediaRouterManager* const manager_;
const media_router::MediaRoute route_;
DISALLOW_COPY_AND_ASSIGN(CefRouteMessageObserver);
};
// Used for messaging and route status notifications with Cast.
class CefPresentationConnection : public blink::mojom::PresentationConnection {
public:
explicit CefPresentationConnection(
CefMediaRouterManager* manager,
const media_router::MediaRoute& route,
media_router::mojom::RoutePresentationConnectionPtr connections)
: manager_(manager),
route_(route),
connection_receiver_(this, std::move(connections->connection_receiver)),
connection_remote_(std::move(connections->connection_remote)) {}
void OnMessage(
blink::mojom::PresentationConnectionMessagePtr message) override {
CefMediaRouterManager::MediaMessageVector messages;
if (message->is_message()) {
messages.push_back(media_router::message_util::RouteMessageFromString(
message->get_message()));
} else if (message->is_data()) {
messages.push_back(media_router::message_util::RouteMessageFromData(
message->get_data()));
}
if (!messages.empty()) {
manager_->OnMessagesReceived(route_, messages);
}
}
void DidChangeState(
blink::mojom::PresentationConnectionState state) override {
// May result in |this| being deleted, so post async and allow the call
// stack to unwind.
CEF_POST_TASK(
CEF_UIT,
base::BindOnce(&CefMediaRouterManager::OnRouteStateChange,
manager_->weak_ptr_factory_.GetWeakPtr(), route_,
content::PresentationConnectionStateChangeInfo(state)));
}
void DidClose(
blink::mojom::PresentationConnectionCloseReason reason) override {
DidChangeState(blink::mojom::PresentationConnectionState::CLOSED);
}
void SendRouteMessage(const std::string& message) {
connection_remote_->OnMessage(
blink::mojom::PresentationConnectionMessage::NewMessage(message));
}
private:
CefMediaRouterManager* const manager_;
const media_router::MediaRoute route_;
// Used to receive messages from the MRP.
mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_;
// Used to send messages to the MRP.
mojo::Remote<blink::mojom::PresentationConnection> connection_remote_;
DISALLOW_COPY_AND_ASSIGN(CefPresentationConnection);
};
CefMediaRouterManager::CefMediaRouterManager(
content::BrowserContext* browser_context)
: browser_context_(browser_context),
query_result_manager_(GetMediaRouter()),
weak_ptr_factory_(this) {
// Perform initialization.
GetMediaRouter()->OnUserGesture();
query_result_manager_.AddObserver(this);
// A non-empty presentation URL to required for discovery of Cast devices.
query_result_manager_.SetSourcesForCastMode(
media_router::MediaCastMode::PRESENTATION,
{media_router::MediaSource::ForPresentationUrl(
GURL(kDefaultPresentationUrl))},
url::Origin());
routes_observer_ = std::make_unique<CefMediaRoutesObserver>(this);
}
CefMediaRouterManager::~CefMediaRouterManager() {
CEF_REQUIRE_UIT();
for (auto& observer : observers_) {
observers_.RemoveObserver(&observer);
observer.OnMediaRouterDestroyed();
}
query_result_manager_.RemoveObserver(this);
}
void CefMediaRouterManager::AddObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.AddObserver(observer);
}
void CefMediaRouterManager::RemoveObserver(Observer* observer) {
CEF_REQUIRE_UIT();
observers_.RemoveObserver(observer);
}
void CefMediaRouterManager::NotifyCurrentSinks() {
CEF_REQUIRE_UIT();
for (auto& observer : observers_) {
observer.OnMediaSinks(sinks_);
}
}
void CefMediaRouterManager::NotifyCurrentRoutes() {
CEF_REQUIRE_UIT();
for (auto& observer : observers_) {
observer.OnMediaRoutes(routes_);
}
}
void CefMediaRouterManager::CreateRoute(
const media_router::MediaSource::Id& source_id,
const media_router::MediaSink::Id& sink_id,
const url::Origin& origin,
CreateRouteResultCallback callback) {
GetMediaRouter()->CreateRoute(
source_id, sink_id, origin, nullptr /* web_contents */,
base::BindOnce(&CefMediaRouterManager::OnCreateRoute,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
base::TimeDelta::FromMilliseconds(kTimeoutMs), false /* incognito */);
}
void CefMediaRouterManager::SendRouteMessage(
const media_router::MediaRoute::Id& route_id,
const std::string& message) {
// Must use PresentationConnection to send messages if it exists.
auto state = GetRouteState(route_id);
if (state && state->presentation_connection_) {
state->presentation_connection_->SendRouteMessage(message);
return;
}
GetMediaRouter()->SendRouteMessage(route_id, message);
}
void CefMediaRouterManager::TerminateRoute(
const media_router::MediaRoute::Id& route_id) {
GetMediaRouter()->TerminateRoute(route_id);
}
void CefMediaRouterManager::OnResultsUpdated(const MediaSinkVector& sinks) {
sinks_ = sinks;
NotifyCurrentSinks();
}
media_router::MediaRouter* CefMediaRouterManager::GetMediaRouter() const {
CEF_REQUIRE_UIT();
return media_router::MediaRouterFactory::GetApiForBrowserContext(
browser_context_);
}
void CefMediaRouterManager::OnCreateRoute(
CreateRouteResultCallback callback,
media_router::mojom::RoutePresentationConnectionPtr connection,
const media_router::RouteRequestResult& result) {
CEF_REQUIRE_UIT();
if (result.route()) {
CreateRouteState(*result.route(), std::move(connection));
}
std::move(callback).Run(result);
}
void CefMediaRouterManager::OnRouteStateChange(
const media_router::MediaRoute& route,
const content::PresentationConnectionStateChangeInfo& info) {
CEF_REQUIRE_UIT();
if (info.state == blink::mojom::PresentationConnectionState::CLOSED ||
info.state == blink::mojom::PresentationConnectionState::TERMINATED) {
RemoveRouteState(route.media_route_id());
}
for (auto& observer : observers_) {
observer.OnMediaRouteStateChange(route, info);
}
}
void CefMediaRouterManager::OnMessagesReceived(
const media_router::MediaRoute& route,
const MediaMessageVector& messages) {
CEF_REQUIRE_UIT();
for (auto& observer : observers_) {
observer.OnMediaRouteMessages(route, messages);
}
}
void CefMediaRouterManager::CreateRouteState(
const media_router::MediaRoute& route,
media_router::mojom::RoutePresentationConnectionPtr connection) {
const auto route_id = route.media_route_id();
auto state = std::make_unique<RouteState>();
if (!connection.is_null()) {
// PresentationConnection must be used for messaging and status
// notifications if it exists.
state->presentation_connection_ =
std::make_unique<CefPresentationConnection>(this, route,
std::move(connection));
} else {
// Fallback if PresentationConnection is not supported.
state->message_observer_ =
std::make_unique<CefRouteMessageObserver>(this, route);
state->state_subscription_ =
GetMediaRouter()->AddPresentationConnectionStateChangedCallback(
route_id,
base::BindRepeating(&CefMediaRouterManager::OnRouteStateChange,
weak_ptr_factory_.GetWeakPtr(), route));
}
route_state_map_.insert(std::make_pair(route_id, std::move(state)));
}
CefMediaRouterManager::RouteState* CefMediaRouterManager::GetRouteState(
const media_router::MediaRoute::Id& route_id) {
const auto it = route_state_map_.find(route_id);
if (it != route_state_map_.end())
return it->second.get();
return nullptr;
}
void CefMediaRouterManager::RemoveRouteState(
const media_router::MediaRoute::Id& route_id) {
auto it = route_state_map_.find(route_id);
if (it != route_state_map_.end())
route_state_map_.erase(it);
}