Commit 89346464 authored by Jordan Bayles's avatar Jordan Bayles Committed by Commit Bot

Media Router: Simplify app availability

This patch fixes some issues with tab sink availability by modifying the
MediaRouterMojoImpl to have a single SinksQuery for all tabs,
tabs_sinks_query_, instead of registering a unique sinks query for each
unique tab media source id.

In places where a media source id is expected, specifically for the
media router providers themselves, a hardcoded media source id for tab 0
is passed.

Bug: 1013769, b/154115531
Change-Id: I8cdb05fba9aa813153e66bf3af8e5f3d17581903
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2200015
Commit-Queue: Jordan Bayles <jophba@chromium.org>
Reviewed-by: default avatarTakumi Fujimoto <takumif@chromium.org>
Cr-Commit-Position: refs/heads/master@{#771963}
parent d99c3910
...@@ -205,7 +205,7 @@ void MediaRouterMojoImpl::OnSinksReceived( ...@@ -205,7 +205,7 @@ void MediaRouterMojoImpl::OnSinksReceived(
const std::vector<url::Origin>& origins) { const std::vector<url::Origin>& origins) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DVLOG_WITH_INSTANCE(1) << "OnSinksReceived"; DVLOG_WITH_INSTANCE(1) << "OnSinksReceived";
auto it = sinks_queries_.find(media_source); auto it = sinks_queries_.find(MediaSinksQuery::GetKey(media_source).id());
if (it == sinks_queries_.end()) { if (it == sinks_queries_.end()) {
DVLOG_WITH_INSTANCE(1) << "Received sink list without MediaSinksQuery."; DVLOG_WITH_INSTANCE(1) << "Received sink list without MediaSinksQuery.";
return; return;
...@@ -486,6 +486,25 @@ void MediaRouterMojoImpl::GetMediaController( ...@@ -486,6 +486,25 @@ void MediaRouterMojoImpl::GetMediaController(
std::move(callback)); std::move(callback));
} }
// static
MediaSource MediaRouterMojoImpl::MediaSinksQuery::GetKey(
const MediaSource::Id& id) {
MediaSource source(id);
if (source.IsTabMirroringSource()) {
return MediaSource::ForAnyTab();
}
return source;
}
// static
MediaSource MediaRouterMojoImpl::MediaSinksQuery::GetKey(
const MediaSinksObserver& observer) {
if (!observer.source()) {
return MediaSource{""};
}
return GetKey(observer.source()->id());
}
void MediaRouterMojoImpl::MediaSinksQuery::SetSinksForProvider( void MediaRouterMojoImpl::MediaSinksQuery::SetSinksForProvider(
MediaRouteProviderId provider_id, MediaRouteProviderId provider_id,
const std::vector<MediaSink>& sinks) { const std::vector<MediaSink>& sinks) {
...@@ -652,12 +671,11 @@ void MediaRouterMojoImpl::ProviderSinkAvailability:: ...@@ -652,12 +671,11 @@ void MediaRouterMojoImpl::ProviderSinkAvailability::
bool MediaRouterMojoImpl::RegisterMediaSinksObserver( bool MediaRouterMojoImpl::RegisterMediaSinksObserver(
MediaSinksObserver* observer) { MediaSinksObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Create an observer list for the media source and add |observer| // Create an observer list for the media source and add |observer|
// to it. Fail if |observer| is already registered. // to it. Fail if |observer| is already registered.
const std::string& source_id =
observer->source() ? observer->source()->id() : ""; const MediaSource source = MediaSinksQuery::GetKey(*observer);
std::unique_ptr<MediaSinksQuery>& sinks_query = sinks_queries_[source_id]; std::unique_ptr<MediaSinksQuery>& sinks_query = sinks_queries_[source.id()];
bool is_new_query = false; bool is_new_query = false;
if (!sinks_query) { if (!sinks_query) {
is_new_query = true; is_new_query = true;
...@@ -672,7 +690,7 @@ bool MediaRouterMojoImpl::RegisterMediaSinksObserver( ...@@ -672,7 +690,7 @@ bool MediaRouterMojoImpl::RegisterMediaSinksObserver(
if (is_new_query) { if (is_new_query) {
for (const auto& provider : media_route_providers_) { for (const auto& provider : media_route_providers_) {
if (sink_availability_.IsAvailableForProvider(provider.first)) { if (sink_availability_.IsAvailableForProvider(provider.first)) {
provider.second->StartObservingMediaSinks(source_id); provider.second->StartObservingMediaSinks(source.id());
} }
} }
} }
...@@ -683,9 +701,8 @@ void MediaRouterMojoImpl::UnregisterMediaSinksObserver( ...@@ -683,9 +701,8 @@ void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
MediaSinksObserver* observer) { MediaSinksObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const std::string& source_id = const MediaSource source = MediaSinksQuery::GetKey(*observer);
observer->source() ? observer->source()->id() : ""; auto it = sinks_queries_.find(source.id());
auto it = sinks_queries_.find(source_id);
if (it == sinks_queries_.end() || !it->second->HasObserver(observer)) if (it == sinks_queries_.end() || !it->second->HasObserver(observer))
return; return;
...@@ -694,15 +711,17 @@ void MediaRouterMojoImpl::UnregisterMediaSinksObserver( ...@@ -694,15 +711,17 @@ void MediaRouterMojoImpl::UnregisterMediaSinksObserver(
// HasObservers() is reliable here on the assumption that this call // HasObservers() is reliable here on the assumption that this call
// is not inside the ObserverList iteration. // is not inside the ObserverList iteration.
it->second->RemoveObserver(observer); it->second->RemoveObserver(observer);
if (!it->second->HasObservers()) { // Since all tabs share the tab sinks query, we don't want to delete it
// here.
if (!it->second->HasObservers() && !source.IsTabMirroringSource()) {
// Only ask MRPs to stop observing media sinks if there are sinks available. // Only ask MRPs to stop observing media sinks if there are sinks available.
// Otherwise, the MRPs would have discarded the queries already. // Otherwise, the MRPs would have discarded the queries already.
for (const auto& provider : media_route_providers_) { for (const auto& provider : media_route_providers_) {
if (sink_availability_.IsAvailableForProvider(provider.first)) { if (sink_availability_.IsAvailableForProvider(provider.first)) {
provider.second->StopObservingMediaSinks(source_id); provider.second->StopObservingMediaSinks(source.id());
} }
} }
sinks_queries_.erase(source_id); sinks_queries_.erase(source.id());
} }
} }
......
...@@ -150,43 +150,14 @@ class MediaRouterMojoImpl : public MediaRouterBase, public mojom::MediaRouter { ...@@ -150,43 +150,14 @@ class MediaRouterMojoImpl : public MediaRouterBase, public mojom::MediaRouter {
friend class MediaRouterFactory; friend class MediaRouterFactory;
friend class MediaRouterMojoImplTest; friend class MediaRouterMojoImplTest;
friend class MediaRouterMojoTest; friend class MediaRouterMojoTest;
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRoute);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRouteTimedOutFails); FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, JoinRouteTimedOutFails);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
JoinRouteIncognitoMismatchFails); JoinRouteIncognitoMismatchFails);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
IncognitoRoutesTerminatedOnProfileShutdown);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterAndUnregisterMediaSinksObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterMediaSinksObserverWithAvailabilityChange);
FRIEND_TEST_ALL_PREFIXES(
MediaRouterMojoImplTest,
RegisterAndUnregisterMediaSinksObserverWithAvailabilityChange);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterAndUnregisterMediaRoutesObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RouteMessagesSingleObserver);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RouteMessagesMultipleObservers);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, HandleIssue); FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, HandleIssue);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, GetMediaController);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
FailToCreateRouteController);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
RegisterMediaRoutesObserver_DedupingWithCache);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
PresentationConnectionStateChangedCallback); PresentationConnectionStateChangedCallback);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest, FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
PresentationConnectionStateChangedCallbackRemoved); PresentationConnectionStateChangedCallbackRemoved);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
SendSinkRequestsToMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
SendRouteRequestsToMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
ObserveSinksFromMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
ObserveRoutesFromMultipleProviders);
FRIEND_TEST_ALL_PREFIXES(MediaRouterDesktopTest, FRIEND_TEST_ALL_PREFIXES(MediaRouterDesktopTest,
SyncStateToMediaRouteProvider); SyncStateToMediaRouteProvider);
FRIEND_TEST_ALL_PREFIXES(ExtensionMediaRouteProviderProxyTest, FRIEND_TEST_ALL_PREFIXES(ExtensionMediaRouteProviderProxyTest,
...@@ -199,6 +170,9 @@ class MediaRouterMojoImpl : public MediaRouterBase, public mojom::MediaRouter { ...@@ -199,6 +170,9 @@ class MediaRouterMojoImpl : public MediaRouterBase, public mojom::MediaRouter {
MediaSinksQuery(); MediaSinksQuery();
~MediaSinksQuery(); ~MediaSinksQuery();
static MediaSource GetKey(const MediaSource::Id& source_id);
static MediaSource GetKey(const MediaSinksObserver& observer);
// Caches the list of sinks for the provider returned from the query. // Caches the list of sinks for the provider returned from the query.
void SetSinksForProvider(MediaRouteProviderId provider_id, void SetSinksForProvider(MediaRouteProviderId provider_id,
const std::vector<MediaSink>& sinks); const std::vector<MediaSink>& sinks);
......
...@@ -74,6 +74,8 @@ const char kDescription[] = "description"; ...@@ -74,6 +74,8 @@ const char kDescription[] = "description";
const char kError[] = "error"; const char kError[] = "error";
const char kSource[] = "source1"; const char kSource[] = "source1";
const char kSource2[] = "source2"; const char kSource2[] = "source2";
const char kTabSourceOne[] = "urn:x-org.chromium.media:source:tab:1";
const char kTabSourceTwo[] = "urn:x-org.chromium.media:source:tab:2";
const char kRouteId[] = "routeId"; const char kRouteId[] = "routeId";
const char kRouteId2[] = "routeId2"; const char kRouteId2[] = "routeId2";
const char kJoinableRouteId[] = "joinableRouteId"; const char kJoinableRouteId[] = "joinableRouteId";
...@@ -130,6 +132,17 @@ std::string RouteMessageToString(const RouteMessagePtr& message) { ...@@ -130,6 +132,17 @@ std::string RouteMessageToString(const RouteMessagePtr& message) {
return result; return result;
} }
std::vector<MediaSinkInternal> ToInternalSinks(
const std::vector<MediaSink>& sinks) {
std::vector<MediaSinkInternal> internal_sinks;
internal_sinks.reserve(sinks.size());
for (const auto& sink : sinks) {
MediaSinkInternal internal_sink;
internal_sink.set_sink(sink);
internal_sinks.emplace_back(std::move(internal_sink));
}
return internal_sinks;
}
} // namespace } // namespace
class MediaRouterMojoImplTest : public MediaRouterMojoTest { class MediaRouterMojoImplTest : public MediaRouterMojoTest {
...@@ -151,6 +164,52 @@ class MediaRouterMojoImplTest : public MediaRouterMojoTest { ...@@ -151,6 +164,52 @@ class MediaRouterMojoImplTest : public MediaRouterMojoTest {
new MediaRouterMojoImpl(profile())); new MediaRouterMojoImpl(profile()));
} }
// Helper methods for interacting with private properties of the MediaRouter.
// This is a rather large list of methods. When writing tests that need
// additional private access to the Media Router, consider refactoring the
// Media Router into more testable classes with more cleanly defined
// responsibilities.
void SetSinkAvailabilityAvailable() {
router()->OnSinkAvailabilityUpdated(
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::AVAILABLE);
}
void SetSinkAvailabilityUnavailable() {
router()->OnSinkAvailabilityUpdated(
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::UNAVAILABLE);
}
void ReceiveSinks(MediaRouteProviderId provider_id,
const std::string& media_source,
const std::vector<MediaSinkInternal>& sinks) {
router()->OnSinksReceived(
provider_id, media_source, sinks,
std::vector<url::Origin>{1, url::Origin::Create(GURL(kOrigin))});
}
void ReceiveRouteMessages(const std::string& route_id,
std::vector<mojom::RouteMessagePtr> messages) {
router()->OnRouteMessagesReceived(route_id, std::move(messages));
}
void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) {
router()->RegisterMediaRoutesObserver(observer);
}
void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) {
router()->UnregisterMediaRoutesObserver(observer);
}
void UpdateRoutes(MediaRouteProviderId provider_id,
const std::vector<MediaRoute>& routes,
const std::string& media_source,
const std::vector<std::string>& joinable_route_ids) {
router()->OnRoutesUpdated(provider_id, routes, media_source,
joinable_route_ids);
}
private: private:
base::HistogramTester histogram_tester_; base::HistogramTester histogram_tester_;
}; };
...@@ -294,10 +353,9 @@ TEST_F(MediaRouterMojoImplTest, IncognitoRoutesTerminatedOnProfileShutdown) { ...@@ -294,10 +353,9 @@ TEST_F(MediaRouterMojoImplTest, IncognitoRoutesTerminatedOnProfileShutdown) {
nullptr, base::DoNothing(), nullptr, base::DoNothing(),
base::TimeDelta::FromMilliseconds(kTimeoutMillis), base::TimeDelta::FromMilliseconds(kTimeoutMillis),
true); true);
std::vector<MediaRoute> routes; const std::vector<MediaRoute> routes{route};
routes.push_back(route); UpdateRoutes(MediaRouteProviderId::EXTENSION, routes, std::string(),
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, routes, std::vector<std::string>());
std::string(), std::vector<std::string>());
// TODO(mfoltz): Where possible, convert other tests to use RunUntilIdle // TODO(mfoltz): Where possible, convert other tests to use RunUntilIdle
// instead of manually calling Run/Quit on the run loop. // instead of manually calling Run/Quit on the run loop.
...@@ -338,10 +396,9 @@ TEST_F(MediaRouterMojoImplTest, JoinRouteNotFoundFails) { ...@@ -338,10 +396,9 @@ TEST_F(MediaRouterMojoImplTest, JoinRouteNotFoundFails) {
TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) { TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) {
// Make sure the MR has received an update with the route, so it knows there // Make sure the MR has received an update with the route, so it knows there
// is a route to join. // is a route to join.
std::vector<MediaRoute> routes; const std::vector<MediaRoute> routes{CreateMediaRoute()};
routes.push_back(CreateMediaRoute()); UpdateRoutes(MediaRouteProviderId::EXTENSION, routes, std::string(),
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, routes, std::vector<std::string>());
std::string(), std::vector<std::string>());
EXPECT_TRUE(router()->HasJoinableRoute()); EXPECT_TRUE(router()->HasJoinableRoute());
EXPECT_CALL(mock_extension_provider_, EXPECT_CALL(mock_extension_provider_,
...@@ -372,14 +429,13 @@ TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) { ...@@ -372,14 +429,13 @@ TEST_F(MediaRouterMojoImplTest, JoinRouteTimedOutFails) {
} }
TEST_F(MediaRouterMojoImplTest, JoinRouteIncognitoMismatchFails) { TEST_F(MediaRouterMojoImplTest, JoinRouteIncognitoMismatchFails) {
MediaRoute route = CreateMediaRoute(); const MediaRoute route = CreateMediaRoute();
// Make sure the MR has received an update with the route, so it knows there // Make sure the MR has received an update with the route, so it knows there
// is a route to join. // is a route to join.
std::vector<MediaRoute> routes; const std::vector<MediaRoute> routes{route};
routes.push_back(route); UpdateRoutes(MediaRouteProviderId::EXTENSION, routes, std::string(),
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, routes, std::vector<std::string>());
std::string(), std::vector<std::string>());
EXPECT_TRUE(router()->HasJoinableRoute()); EXPECT_TRUE(router()->HasJoinableRoute());
// Use a lambda function as an invocation target here to work around // Use a lambda function as an invocation target here to work around
...@@ -537,9 +593,7 @@ TEST_F(MediaRouterMojoImplTest, HandleIssue) { ...@@ -537,9 +593,7 @@ TEST_F(MediaRouterMojoImplTest, HandleIssue) {
} }
TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) { TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityAvailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::AVAILABLE);
MediaSource media_source(kSource); MediaSource media_source(kSource);
// These should only be called once even if there is more than one observer // These should only be called once even if there is more than one observer
...@@ -558,29 +612,21 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) { ...@@ -558,29 +612,21 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
EXPECT_TRUE(unrelated_sinks_observer->Init()); EXPECT_TRUE(unrelated_sinks_observer->Init());
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
std::vector<MediaSink> expected_sinks; const std::vector<MediaSink> kExpectedSinks{
expected_sinks.push_back(MediaSink(kSinkId, kSinkName, SinkIconType::CAST)); MediaSink{kSinkId, kSinkName, SinkIconType::CAST},
expected_sinks.push_back(MediaSink(kSinkId2, kSinkName, SinkIconType::CAST)); MediaSink{kSinkId2, kSinkName, SinkIconType::CAST}};
std::vector<MediaSinkInternal> sinks; EXPECT_CALL(*sinks_observer, OnSinksReceived(kExpectedSinks));
for (const auto& expected_sink : expected_sinks) { EXPECT_CALL(*extra_sinks_observer, OnSinksReceived(kExpectedSinks));
MediaSinkInternal sink_internal; ReceiveSinks(MediaRouteProviderId::EXTENSION, media_source.id(),
sink_internal.set_sink(expected_sink); ToInternalSinks(kExpectedSinks));
sinks.push_back(std::move(sink_internal));
}
EXPECT_CALL(*sinks_observer, OnSinksReceived(expected_sinks));
EXPECT_CALL(*extra_sinks_observer, OnSinksReceived(expected_sinks));
router()->OnSinksReceived(
MediaRouteProviderId::EXTENSION, media_source.id(), sinks,
std::vector<url::Origin>(1, url::Origin::Create(GURL(kOrigin))));
// Since the MediaRouterMojoImpl has already received results for // Since the MediaRouterMojoImpl has already received results for
// |media_source|, return cached results to observers that are subsequently // |media_source|, return cached results to observers that are subsequently
// registered. // registered.
auto cached_sinks_observer = std::make_unique<MockMediaSinksObserver>( auto cached_sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), media_source, url::Origin::Create(GURL(kOrigin))); router(), media_source, url::Origin::Create(GURL(kOrigin)));
EXPECT_CALL(*cached_sinks_observer, OnSinksReceived(expected_sinks)); EXPECT_CALL(*cached_sinks_observer, OnSinksReceived(kExpectedSinks));
EXPECT_TRUE(cached_sinks_observer->Init()); EXPECT_TRUE(cached_sinks_observer->Init());
// Different origin from cached result. Empty list will be returned. // Different origin from cached result. Empty list will be returned.
...@@ -599,12 +645,79 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) { ...@@ -599,12 +645,79 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
TEST_F(MediaRouterMojoImplTest, TabSinksObserverIsShared) {
SetSinkAvailabilityAvailable();
MediaSource tab_source_one(kTabSourceOne);
MediaSource tab_source_two(kTabSourceTwo);
// Media router should not try to start observing for each tab, but instead
// only once for all tabs.
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kTabSourceOne))
.Times(0);
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kTabSourceTwo))
.Times(0);
EXPECT_CALL(mock_extension_provider_,
StartObservingMediaSinks(MediaSource::ForAnyTab().id()))
.Times(1);
const auto origin = url::Origin::Create(GURL(kOrigin));
auto sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), tab_source_one, origin);
EXPECT_TRUE(sinks_observer->Init());
auto extra_sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), tab_source_one, origin);
EXPECT_TRUE(extra_sinks_observer->Init());
auto second_tab_sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), MediaSource(kTabSourceTwo), origin);
EXPECT_TRUE(second_tab_sinks_observer->Init());
base::RunLoop().RunUntilIdle();
const std::vector<MediaSink> kExpectedSinks{
MediaSink(kSinkId, kSinkName, SinkIconType::CAST),
MediaSink(kSinkId2, kSinkName, SinkIconType::CAST)};
// All tabs should get the same updates.
EXPECT_CALL(*sinks_observer, OnSinksReceived(kExpectedSinks));
EXPECT_CALL(*extra_sinks_observer, OnSinksReceived(kExpectedSinks));
EXPECT_CALL(*second_tab_sinks_observer, OnSinksReceived(kExpectedSinks));
ReceiveSinks(MediaRouteProviderId::EXTENSION, tab_source_one.id(),
ToInternalSinks(kExpectedSinks));
// Since the MediaRouterMojoImpl has already received results for
// |media_source|, return cached results to observers that are subsequently
// registered.
auto cached_sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), tab_source_one, url::Origin::Create(GURL(kOrigin)));
EXPECT_CALL(*cached_sinks_observer, OnSinksReceived(kExpectedSinks));
EXPECT_TRUE(cached_sinks_observer->Init());
// Different origin from cached result. Empty list will be returned.
auto cached_sinks_observer2 = std::make_unique<MockMediaSinksObserver>(
router(), tab_source_one,
url::Origin::Create(GURL("https://youtube.com")));
EXPECT_CALL(*cached_sinks_observer2, OnSinksReceived(IsEmpty()));
EXPECT_TRUE(cached_sinks_observer2->Init());
// Since tabs share observation, stop observing should not be called.
EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kTabSourceOne))
.Times(0);
EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kTabSourceTwo))
.Times(0);
EXPECT_CALL(mock_extension_provider_,
StopObservingMediaSinks(MediaSource::ForAnyTab().id()))
.Times(0);
sinks_observer.reset();
extra_sinks_observer.reset();
second_tab_sinks_observer.reset();
cached_sinks_observer.reset();
cached_sinks_observer2.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(MediaRouterMojoImplTest, TEST_F(MediaRouterMojoImplTest,
RegisterMediaSinksObserverWithAvailabilityChange) { RegisterMediaSinksObserverWithAvailabilityChange) {
// When availability is UNAVAILABLE, no calls should be made to MRPM. // When availability is UNAVAILABLE, no calls should be made to MRPM.
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityUnavailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::UNAVAILABLE);
MediaSource media_source(kSource); MediaSource media_source(kSource);
auto sinks_observer = std::make_unique<MockMediaSinksObserver>( auto sinks_observer = std::make_unique<MockMediaSinksObserver>(
router(), media_source, url::Origin::Create(GURL(kOrigin))); router(), media_source, url::Origin::Create(GURL(kOrigin)));
...@@ -624,9 +737,7 @@ TEST_F(MediaRouterMojoImplTest, ...@@ -624,9 +737,7 @@ TEST_F(MediaRouterMojoImplTest,
// When availability transitions AVAILABLE, existing sink queries should be // When availability transitions AVAILABLE, existing sink queries should be
// sent to MRPM. // sent to MRPM.
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityAvailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::AVAILABLE);
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource)) EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource))
.Times(1); .Times(1);
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource2)) EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource2))
...@@ -635,9 +746,7 @@ TEST_F(MediaRouterMojoImplTest, ...@@ -635,9 +746,7 @@ TEST_F(MediaRouterMojoImplTest,
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_extension_provider_)); EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_extension_provider_));
// No change in availability status; no calls should be made to MRPM. // No change in availability status; no calls should be made to MRPM.
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityAvailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::AVAILABLE);
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource)) EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource))
.Times(0); .Times(0);
EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource2)) EXPECT_CALL(mock_extension_provider_, StartObservingMediaSinks(kSource2))
...@@ -647,9 +756,7 @@ TEST_F(MediaRouterMojoImplTest, ...@@ -647,9 +756,7 @@ TEST_F(MediaRouterMojoImplTest,
// When availability is UNAVAILABLE, queries are already removed from MRPM. // When availability is UNAVAILABLE, queries are already removed from MRPM.
// Unregistering observer won't result in call to MRPM to remove query. // Unregistering observer won't result in call to MRPM to remove query.
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityUnavailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::UNAVAILABLE);
EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kSource)) EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kSource))
.Times(0); .Times(0);
sinks_observer.reset(); sinks_observer.reset();
...@@ -658,9 +765,7 @@ TEST_F(MediaRouterMojoImplTest, ...@@ -658,9 +765,7 @@ TEST_F(MediaRouterMojoImplTest,
// When availability is AVAILABLE, call is made to MRPM to remove query when // When availability is AVAILABLE, call is made to MRPM to remove query when
// observer is unregistered. // observer is unregistered.
router()->OnSinkAvailabilityUpdated( SetSinkAvailabilityAvailable();
MediaRouteProviderId::EXTENSION,
mojom::MediaRouter::SinkAvailability::AVAILABLE);
EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kSource2)); EXPECT_CALL(mock_extension_provider_, StopObservingMediaSinks(kSource2));
sinks_observer2.reset(); sinks_observer2.reset();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
...@@ -689,30 +794,29 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) { ...@@ -689,30 +794,29 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
MockMediaRoutesObserver different_routes_observer( MockMediaRoutesObserver different_routes_observer(
&mock_router, different_media_source.id()); &mock_router, different_media_source.id());
EXPECT_EQ(observer_captured, &different_routes_observer); EXPECT_EQ(observer_captured, &different_routes_observer);
router()->RegisterMediaRoutesObserver(&routes_observer); RegisterMediaRoutesObserver(&routes_observer);
router()->RegisterMediaRoutesObserver(&extra_routes_observer); RegisterMediaRoutesObserver(&extra_routes_observer);
router()->RegisterMediaRoutesObserver(&different_routes_observer); RegisterMediaRoutesObserver(&different_routes_observer);
std::vector<MediaRoute> expected_routes{
MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false)};
std::vector<MediaRoute> expected_routes;
expected_routes.push_back(
MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false));
MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId, MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId,
kDescription, false, false); kDescription, false, false);
incognito_expected_route.set_incognito(true); incognito_expected_route.set_incognito(true);
expected_routes.push_back(incognito_expected_route); expected_routes.emplace_back(incognito_expected_route);
std::vector<MediaRoute::Id> expected_joinable_route_ids;
expected_joinable_route_ids.push_back(kJoinableRouteId);
expected_joinable_route_ids.push_back(kJoinableRouteId2);
const std::vector<MediaRoute::Id> kExpectedJoinableRouteIds{
kJoinableRouteId, kJoinableRouteId2};
EXPECT_CALL(routes_observer, EXPECT_CALL(routes_observer,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)); OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds));
EXPECT_CALL(extra_routes_observer, EXPECT_CALL(extra_routes_observer,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)); OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds));
EXPECT_CALL(different_routes_observer, EXPECT_CALL(different_routes_observer,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)) OnRoutesUpdated(expected_routes, kExpectedJoinableRouteIds))
.Times(0); .Times(0);
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, expected_routes, UpdateRoutes(MediaRouteProviderId::EXTENSION, expected_routes,
media_source.id(), expected_joinable_route_ids); media_source.id(), kExpectedJoinableRouteIds);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer)); EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer));
...@@ -720,9 +824,9 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) { ...@@ -720,9 +824,9 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
UnregisterMediaRoutesObserver(&extra_routes_observer)); UnregisterMediaRoutesObserver(&extra_routes_observer));
EXPECT_CALL(mock_router, EXPECT_CALL(mock_router,
UnregisterMediaRoutesObserver(&different_routes_observer)); UnregisterMediaRoutesObserver(&different_routes_observer));
router()->UnregisterMediaRoutesObserver(&routes_observer); UnregisterMediaRoutesObserver(&routes_observer);
router()->UnregisterMediaRoutesObserver(&extra_routes_observer); UnregisterMediaRoutesObserver(&extra_routes_observer);
router()->UnregisterMediaRoutesObserver(&different_routes_observer); UnregisterMediaRoutesObserver(&different_routes_observer);
EXPECT_CALL(mock_extension_provider_, EXPECT_CALL(mock_extension_provider_,
StopObservingMediaRoutes(media_source.id())) StopObservingMediaRoutes(media_source.id()))
.Times(1); .Times(1);
...@@ -735,11 +839,9 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) { ...@@ -735,11 +839,9 @@ TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
// extra extension wake-ups because the OnRoutesUpdated() results are cached. // extra extension wake-ups because the OnRoutesUpdated() results are cached.
TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) { TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) {
const MediaSource media_source = MediaSource(kSource); const MediaSource media_source = MediaSource(kSource);
std::vector<MediaRoute> expected_routes; const std::vector<MediaRoute> kExpectedRoutes{
expected_routes.push_back( MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false)};
MediaRoute(kRouteId, media_source, kSinkId, kDescription, false, false)); const std::vector<MediaRoute::Id> kExpectedJoinableRouteIds{kJoinableRouteId};
std::vector<MediaRoute::Id> expected_joinable_route_ids;
expected_joinable_route_ids.push_back(kJoinableRouteId);
Sequence sequence; Sequence sequence;
...@@ -753,10 +855,10 @@ TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) { ...@@ -753,10 +855,10 @@ TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) {
std::make_unique<MockMediaRoutesObserver>(router(), media_source.id()); std::make_unique<MockMediaRoutesObserver>(router(), media_source.id());
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_CALL(*observer1, EXPECT_CALL(*observer1,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)) OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
.Times(1); .Times(1);
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, expected_routes, UpdateRoutes(MediaRouteProviderId::EXTENSION, kExpectedRoutes,
media_source.id(), expected_joinable_route_ids); media_source.id(), kExpectedJoinableRouteIds);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
// Creating two more observers will not wake up the provider. Instead, the // Creating two more observers will not wake up the provider. Instead, the
...@@ -766,10 +868,10 @@ TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) { ...@@ -766,10 +868,10 @@ TEST_F(MediaRouterMojoImplTest, RegisterMediaRoutesObserver_DedupingWithCache) {
auto observer3 = auto observer3 =
std::make_unique<MockMediaRoutesObserver>(router(), media_source.id()); std::make_unique<MockMediaRoutesObserver>(router(), media_source.id());
EXPECT_CALL(*observer2, EXPECT_CALL(*observer2,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)) OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
.Times(1); .Times(1);
EXPECT_CALL(*observer3, EXPECT_CALL(*observer3,
OnRoutesUpdated(expected_routes, expected_joinable_route_ids)) OnRoutesUpdated(kExpectedRoutes, kExpectedJoinableRouteIds))
.Times(1); .Times(1);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
...@@ -876,9 +978,9 @@ TEST_F(MediaRouterMojoImplTest, RouteMessagesSingleObserver) { ...@@ -876,9 +978,9 @@ TEST_F(MediaRouterMojoImplTest, RouteMessagesSingleObserver) {
ExpectedMessagesObserver observer(router(), kRouteId, ExpectedMessagesObserver observer(router(), kRouteId,
std::move(all_messages)); std::move(all_messages));
run_loop.Run(); // Will quit when StartListeningForRouteMessages() is called. run_loop.Run(); // Will quit when StartListeningForRouteMessages() is called.
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch1)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch1));
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch2)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch2));
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch3)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch3));
// When |observer| goes out-of-scope, its destructor will ensure all expected // When |observer| goes out-of-scope, its destructor will ensure all expected
// messages have been received. // messages have been received.
} }
...@@ -905,9 +1007,9 @@ TEST_F(MediaRouterMojoImplTest, RouteMessagesMultipleObservers) { ...@@ -905,9 +1007,9 @@ TEST_F(MediaRouterMojoImplTest, RouteMessagesMultipleObservers) {
ExpectedMessagesObserver observer2(router(), kRouteId, ExpectedMessagesObserver observer2(router(), kRouteId,
std::move(all_messages2)); std::move(all_messages2));
run_loop.Run(); // Will quit when StartListeningForRouteMessages() is called. run_loop.Run(); // Will quit when StartListeningForRouteMessages() is called.
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch1)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch1));
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch2)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch2));
router()->OnRouteMessagesReceived(kRouteId, std::move(incoming_batch3)); ReceiveRouteMessages(kRouteId, std::move(incoming_batch3));
// As each |observer| goes out-of-scope, its destructor will ensure all // As each |observer| goes out-of-scope, its destructor will ensure all
// expected messages have been received. // expected messages have been received.
} }
...@@ -981,8 +1083,7 @@ TEST_F(MediaRouterMojoImplTest, GetMediaController) { ...@@ -981,8 +1083,7 @@ TEST_F(MediaRouterMojoImplTest, GetMediaController) {
MockMediaStatusObserver mock_observer( MockMediaStatusObserver mock_observer(
observer_remote.InitWithNewPipeAndPassReceiver()); observer_remote.InitWithNewPipeAndPassReceiver());
mojo::Remote<mojom::MediaStatusObserver> observer_remote_held_by_controller; mojo::Remote<mojom::MediaStatusObserver> observer_remote_held_by_controller;
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, UpdateRoutes(MediaRouteProviderId::EXTENSION, {CreateMediaRoute()}, "", {});
{CreateMediaRoute()}, "", {});
EXPECT_CALL(mock_extension_provider_, EXPECT_CALL(mock_extension_provider_,
CreateMediaRouteControllerInternal(kRouteId, _, _, _)) CreateMediaRouteControllerInternal(kRouteId, _, _, _))
...@@ -1106,25 +1207,22 @@ TEST_F(MediaRouterMojoImplTest, ObserveSinksFromMultipleProviders) { ...@@ -1106,25 +1207,22 @@ TEST_F(MediaRouterMojoImplTest, ObserveSinksFromMultipleProviders) {
// Have the extension MRP report sinks. // Have the extension MRP report sinks.
EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink1a.sink(), EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink1a.sink(),
sink1b.sink()))); sink1b.sink())));
router()->OnSinksReceived(MediaRouteProviderId::EXTENSION, kSource, ReceiveSinks(MediaRouteProviderId::EXTENSION, kSource, {sink1a, sink1b});
{sink1a, sink1b}, {});
// Have the wired display MRP report sinks. // Have the wired display MRP report sinks.
EXPECT_CALL(observer, EXPECT_CALL(observer,
OnSinksReceived(UnorderedElementsAre( OnSinksReceived(UnorderedElementsAre(
sink1a.sink(), sink1b.sink(), sink2a.sink(), sink2b.sink()))); sink1a.sink(), sink1b.sink(), sink2a.sink(), sink2b.sink())));
router()->OnSinksReceived(MediaRouteProviderId::WIRED_DISPLAY, kSource, ReceiveSinks(MediaRouteProviderId::WIRED_DISPLAY, kSource, {sink2a, sink2b});
{sink2a, sink2b}, {});
// Have the extension MRP report an empty list of sinks. // Have the extension MRP report an empty list of sinks.
EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink2a.sink(), EXPECT_CALL(observer, OnSinksReceived(UnorderedElementsAre(sink2a.sink(),
sink2b.sink()))); sink2b.sink())));
router()->OnSinksReceived(MediaRouteProviderId::EXTENSION, kSource, {}, {}); ReceiveSinks(MediaRouteProviderId::EXTENSION, kSource, {});
// Have the wired display MRP report an empty list of sinks. // Have the wired display MRP report an empty list of sinks.
EXPECT_CALL(observer, OnSinksReceived(IsEmpty())); EXPECT_CALL(observer, OnSinksReceived(IsEmpty()));
router()->OnSinksReceived(MediaRouteProviderId::WIRED_DISPLAY, kSource, {}, ReceiveSinks(MediaRouteProviderId::WIRED_DISPLAY, kSource, {});
{});
} }
TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) { TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) {
...@@ -1142,8 +1240,8 @@ TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) { ...@@ -1142,8 +1240,8 @@ TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) {
EXPECT_CALL(observer, EXPECT_CALL(observer,
OnRoutesUpdated(UnorderedElementsAre(route1a, route1b), OnRoutesUpdated(UnorderedElementsAre(route1a, route1b),
UnorderedElementsAre(route1a.media_route_id()))); UnorderedElementsAre(route1a.media_route_id())));
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, {route1a, route1b}, UpdateRoutes(MediaRouteProviderId::EXTENSION, {route1a, route1b}, kSource,
kSource, {route1a.media_route_id()}); {route1a.media_route_id()});
// Have the wired display MRP report routes. // Have the wired display MRP report routes.
EXPECT_CALL( EXPECT_CALL(
...@@ -1151,20 +1249,18 @@ TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) { ...@@ -1151,20 +1249,18 @@ TEST_F(MediaRouterMojoImplTest, ObserveRoutesFromMultipleProviders) {
OnRoutesUpdated(UnorderedElementsAre(route1a, route1b, route2a, route2b), OnRoutesUpdated(UnorderedElementsAre(route1a, route1b, route2a, route2b),
UnorderedElementsAre(route1a.media_route_id(), UnorderedElementsAre(route1a.media_route_id(),
route2a.media_route_id()))); route2a.media_route_id())));
router()->OnRoutesUpdated(MediaRouteProviderId::WIRED_DISPLAY, UpdateRoutes(MediaRouteProviderId::WIRED_DISPLAY, {route2a, route2b}, kSource,
{route2a, route2b}, kSource,
{route2a.media_route_id()}); {route2a.media_route_id()});
// Have the extension MRP report an empty list of routes. // Have the extension MRP report an empty list of routes.
EXPECT_CALL(observer, EXPECT_CALL(observer,
OnRoutesUpdated(UnorderedElementsAre(route2a, route2b), OnRoutesUpdated(UnorderedElementsAre(route2a, route2b),
UnorderedElementsAre(route2a.media_route_id()))); UnorderedElementsAre(route2a.media_route_id())));
router()->OnRoutesUpdated(MediaRouteProviderId::EXTENSION, {}, kSource, {}); UpdateRoutes(MediaRouteProviderId::EXTENSION, {}, kSource, {});
// Have the wired display MRP report an empty list of routes. // Have the wired display MRP report an empty list of routes.
EXPECT_CALL(observer, OnRoutesUpdated(IsEmpty(), IsEmpty())); EXPECT_CALL(observer, OnRoutesUpdated(IsEmpty(), IsEmpty()));
router()->OnRoutesUpdated(MediaRouteProviderId::WIRED_DISPLAY, {}, kSource, UpdateRoutes(MediaRouteProviderId::WIRED_DISPLAY, {}, kSource, {});
{});
} }
} // namespace media_router } // namespace media_router
...@@ -21,6 +21,7 @@ namespace { ...@@ -21,6 +21,7 @@ namespace {
// Prefixes used to format and detect various protocols' media source URNs. // Prefixes used to format and detect various protocols' media source URNs.
// See: https://www.ietf.org/rfc/rfc3406.txt // See: https://www.ietf.org/rfc/rfc3406.txt
constexpr char kAnyTabMediaUrn[] = "urn:x-org.chromium.media:source:tab:*";
constexpr char kTabMediaUrnFormat[] = "urn:x-org.chromium.media:source:tab:%d"; constexpr char kTabMediaUrnFormat[] = "urn:x-org.chromium.media:source:tab:%d";
constexpr base::StringPiece kDesktopMediaUrnPrefix = constexpr base::StringPiece kDesktopMediaUrnPrefix =
"urn:x-org.chromium.media:source:desktop:"; "urn:x-org.chromium.media:source:desktop:";
...@@ -67,6 +68,11 @@ MediaSource::MediaSource(const GURL& presentation_url) ...@@ -67,6 +68,11 @@ MediaSource::MediaSource(const GURL& presentation_url)
MediaSource::~MediaSource() = default; MediaSource::~MediaSource() = default;
// static
MediaSource MediaSource::ForAnyTab() {
return MediaSource(std::string(kAnyTabMediaUrn));
}
// static // static
MediaSource MediaSource::ForTab(int tab_id) { MediaSource MediaSource::ForTab(int tab_id) {
return MediaSource(base::StringPrintf(kTabMediaUrnFormat, tab_id)); return MediaSource(base::StringPrintf(kTabMediaUrnFormat, tab_id));
...@@ -87,18 +93,19 @@ MediaSource MediaSource::ForPresentationUrl(const GURL& presentation_url) { ...@@ -87,18 +93,19 @@ MediaSource MediaSource::ForPresentationUrl(const GURL& presentation_url) {
return MediaSource(presentation_url); return MediaSource(presentation_url);
} }
bool MediaSource::IsTabMirroringSource() const {
int tab_id;
return id() == kAnyTabMediaUrn ||
(std::sscanf(id().c_str(), kTabMediaUrnFormat, &tab_id) == 1 &&
tab_id > 0);
}
bool MediaSource::IsDesktopMirroringSource() const { bool MediaSource::IsDesktopMirroringSource() const {
return id() == kUnknownDesktopMediaUrn || return id() == kUnknownDesktopMediaUrn ||
base::StartsWith(id(), kDesktopMediaUrnPrefix, base::StartsWith(id(), kDesktopMediaUrnPrefix,
base::CompareCase::SENSITIVE); base::CompareCase::SENSITIVE);
} }
bool MediaSource::IsTabMirroringSource() const {
int tab_id;
return std::sscanf(id_.c_str(), kTabMediaUrnFormat, &tab_id) == 1 &&
tab_id > 0;
}
bool MediaSource::IsMirroringSource() const { bool MediaSource::IsMirroringSource() const {
return IsDesktopMirroringSource() || IsTabMirroringSource(); return IsDesktopMirroringSource() || IsTabMirroringSource();
} }
...@@ -125,7 +132,7 @@ base::Optional<std::string> MediaSource::DesktopStreamId() const { ...@@ -125,7 +132,7 @@ base::Optional<std::string> MediaSource::DesktopStreamId() const {
} }
bool MediaSource::IsValid() const { bool MediaSource::IsValid() const {
return TabId() > 0 || IsDesktopMirroringSource() || return IsTabMirroringSource() || IsDesktopMirroringSource() ||
IsValidPresentationUrl(GURL(id_)); IsValidPresentationUrl(GURL(id_));
} }
......
...@@ -90,6 +90,7 @@ class MediaSource { ...@@ -90,6 +90,7 @@ class MediaSource {
// Protocol-specific media source object creation. // Protocol-specific media source object creation.
// Returns MediaSource URI depending on the type of source. // Returns MediaSource URI depending on the type of source.
static MediaSource ForAnyTab();
static MediaSource ForTab(int tab_id); static MediaSource ForTab(int tab_id);
static MediaSource ForPresentationUrl(const GURL& presentation_url); static MediaSource ForPresentationUrl(const GURL& presentation_url);
...@@ -107,9 +108,8 @@ class MediaSource { ...@@ -107,9 +108,8 @@ class MediaSource {
// extension-based Cast MRP is removed. // extension-based Cast MRP is removed.
static MediaSource ForDesktop(); static MediaSource ForDesktop();
// Returns true if source outputs its content via mirroring.
bool IsDesktopMirroringSource() const;
bool IsTabMirroringSource() const; bool IsTabMirroringSource() const;
bool IsDesktopMirroringSource() const;
bool IsMirroringSource() const; bool IsMirroringSource() const;
// Returns true if this is represents a Cast Presentation URL. // Returns true if this is represents a Cast Presentation URL.
......
...@@ -54,6 +54,18 @@ TEST(MediaSourceTest, ConstructorWithURLString) { ...@@ -54,6 +54,18 @@ TEST(MediaSourceTest, ConstructorWithURLString) {
EXPECT_EQ(test_url, source1.url()); EXPECT_EQ(test_url, source1.url());
} }
TEST(MediaSourceTest, ForAnyTab) {
auto source = MediaSource::ForAnyTab();
EXPECT_EQ("urn:x-org.chromium.media:source:tab:*", source.id());
EXPECT_EQ(-1, source.TabId());
EXPECT_TRUE(source.IsValid());
EXPECT_FALSE(source.IsDesktopMirroringSource());
EXPECT_TRUE(source.IsTabMirroringSource());
EXPECT_TRUE(source.IsMirroringSource());
EXPECT_FALSE(source.IsCastPresentationUrl());
EXPECT_FALSE(source.IsDialSource());
}
TEST(MediaSourceTest, ForTab) { TEST(MediaSourceTest, ForTab) {
auto source = MediaSource::ForTab(123); auto source = MediaSource::ForTab(123);
EXPECT_EQ("urn:x-org.chromium.media:source:tab:123", source.id()); EXPECT_EQ("urn:x-org.chromium.media:source:tab:123", source.id());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment