blob: b63462acf7367ce9590d923207ee7960595e7dc2 [file] [log] [blame]
// 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/presentation/presentation_service_delegate_impl.h"
#include "base/bind.h"
#include "base/test/mock_callback.h"
#include "build/build_config.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/presentation/local_presentation_manager.h"
#include "chrome/browser/media/router/presentation/local_presentation_manager_factory.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/media/router/test/mock_screen_availability_listener.h"
#include "chrome/browser/media/router/test/test_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/media_router/media_source.h"
#include "chrome/common/media_router/media_source_helper.h"
#include "chrome/common/media_router/route_request_result.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/presentation_request.h"
#include "content/public/browser/presentation_screen_availability_listener.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/origin.h"
using blink::mojom::PresentationConnection;
using blink::mojom::PresentationConnectionMessage;
using blink::mojom::PresentationConnectionResultPtr;
using blink::mojom::PresentationInfo;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::WithArgs;
namespace {
constexpr char kPresentationUrl1[] = "https://foo.fakeurl.com/";
constexpr char kPresentationUrl2[] = "https://bar.fakeurl.com/";
constexpr char kPresentationUrl3[] = "cast:233637DE";
constexpr char kFrameUrl[] = "http://anotherframeurl.fakeurl.com/";
constexpr char kPresentationId[] = "presentation_id";
// Matches blink::mojom::PresentationInfo.
MATCHER_P(InfoEquals, expected, "") {
return expected.url == arg.url && expected.id == arg.id;
}
} // namespace
namespace media_router {
class MockDelegateObserver
: public content::PresentationServiceDelegate::Observer {
public:
MOCK_METHOD0(OnDelegateDestroyed, void());
MOCK_METHOD1(OnDefaultPresentationStarted, void(const PresentationInfo&));
};
class MockDefaultPresentationRequestObserver
: public PresentationServiceDelegateImpl::
DefaultPresentationRequestObserver {
public:
MOCK_METHOD1(OnDefaultPresentationChanged,
void(const content::PresentationRequest&));
MOCK_METHOD0(OnDefaultPresentationRemoved, void());
};
class MockCreatePresentationConnnectionCallbacks {
public:
MOCK_METHOD1(OnCreateConnectionSuccess,
void(PresentationConnectionResultPtr result));
MOCK_METHOD1(OnCreateConnectionError,
void(const blink::mojom::PresentationError& error));
};
class MockLocalPresentationManager : public LocalPresentationManager {
public:
void RegisterLocalPresentationController(
const PresentationInfo& presentation_info,
const content::GlobalFrameRoutingId& render_frame_id,
content::PresentationConnectionPtr controller,
content::PresentationConnectionRequest request,
const MediaRoute& route) override {
RegisterLocalPresentationControllerInternal(
presentation_info, render_frame_id, controller, request, route);
}
MOCK_METHOD5(RegisterLocalPresentationControllerInternal,
void(const PresentationInfo& presentation_info,
const content::GlobalFrameRoutingId& render_frame_id,
content::PresentationConnectionPtr& controller,
content::PresentationConnectionRequest& request,
const MediaRoute& route));
MOCK_METHOD2(UnregisterLocalPresentationController,
void(const std::string& presentation_id,
const content::GlobalFrameRoutingId& render_frame_id));
MOCK_METHOD2(OnLocalPresentationReceiverCreated,
void(const PresentationInfo& presentation_info,
const content::ReceiverConnectionAvailableCallback&
receiver_callback));
MOCK_METHOD1(OnLocalPresentationReceiverTerminated,
void(const std::string& presentation_id));
MOCK_METHOD1(IsLocalPresentation, bool(const std::string& presentation_id));
MOCK_METHOD1(GetRoute, MediaRoute*(const std::string& presentation_id));
};
std::unique_ptr<KeyedService> BuildMockLocalPresentationManager(
content::BrowserContext* context) {
return std::make_unique<MockLocalPresentationManager>();
}
class PresentationServiceDelegateImplTest
: public ChromeRenderViewHostTestHarness {
public:
PresentationServiceDelegateImplTest()
: router_(nullptr),
delegate_impl_(nullptr),
presentation_url1_(kPresentationUrl1),
presentation_url2_(kPresentationUrl2),
presentation_urls_({presentation_url1_}),
frame_url_(kFrameUrl),
frame_origin_(url::Origin::Create(GURL(frame_url_))),
source1_(MediaSourceForPresentationUrl(presentation_url1_)),
source2_(MediaSourceForPresentationUrl(presentation_url2_)),
listener1_(presentation_url1_),
listener2_(presentation_url2_) {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
content::WebContents* wc = GetWebContents();
router_ = static_cast<MockMediaRouter*>(
MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
web_contents()->GetBrowserContext(),
base::BindRepeating(&MockMediaRouter::Create)));
ASSERT_TRUE(wc);
PresentationServiceDelegateImpl::CreateForWebContents(wc);
delegate_impl_ = PresentationServiceDelegateImpl::FromWebContents(wc);
SetMainFrame();
presentation_request_ = std::make_unique<content::PresentationRequest>(
content::GlobalFrameRoutingId(main_frame_process_id_,
main_frame_routing_id_),
presentation_urls_, frame_origin_);
SetMockLocalPresentationManager();
}
MOCK_METHOD1(OnDefaultPresentationStarted,
void(PresentationConnectionResultPtr result));
protected:
virtual content::WebContents* GetWebContents() { return web_contents(); }
MockLocalPresentationManager& GetMockLocalPresentationManager() {
return *mock_local_manager_;
}
void RunDefaultPresentationUrlCallbackTest(bool incognito) {
auto callback = base::BindRepeating(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
std::vector<std::string> urls({kPresentationUrl1});
delegate_impl_->SetDefaultPresentationUrls(*presentation_request_,
callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
const auto& request = delegate_impl_->GetDefaultPresentationRequest();
// Should not trigger callback since route response is error.
std::unique_ptr<RouteRequestResult> result = RouteRequestResult::FromError(
"Error", RouteRequestResult::UNKNOWN_ERROR);
delegate_impl_->OnRouteResponse(request, /** connection */ nullptr,
*result);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
// Should not trigger callback since request doesn't match.
content::PresentationRequest different_request(
content::GlobalFrameRoutingId(100, 200), {presentation_url2_},
frame_origin_);
MediaRoute media_route("differentRouteId", source2_, "mediaSinkId", "",
true, true);
media_route.set_incognito(incognito);
result =
RouteRequestResult::FromSuccess(media_route, "differentPresentationId");
delegate_impl_->OnRouteResponse(different_request,
/** connection */ nullptr, *result);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
// Should trigger callback since request matches.
EXPECT_CALL(*this, OnDefaultPresentationStarted(_)).Times(1);
MediaRoute media_route2("routeId", source1_, "mediaSinkId", "", true, true);
media_route2.set_incognito(incognito);
result = RouteRequestResult::FromSuccess(media_route2, "presentationId");
delegate_impl_->OnRouteResponse(request, /** connection */ nullptr,
*result);
}
void SetMainFrame() {
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
main_frame_process_id_ = main_frame->GetProcess()->GetID();
main_frame_routing_id_ = main_frame->GetRoutingID();
}
void SetMockLocalPresentationManager() {
LocalPresentationManagerFactory::GetInstanceForTest()->SetTestingFactory(
profile(), base::BindRepeating(&BuildMockLocalPresentationManager));
mock_local_manager_ = static_cast<MockLocalPresentationManager*>(
LocalPresentationManagerFactory::GetOrCreateForBrowserContext(
profile()));
}
MockMediaRouter* router_;
PresentationServiceDelegateImpl* delegate_impl_;
const GURL presentation_url1_;
const GURL presentation_url2_;
std::vector<GURL> presentation_urls_;
const GURL frame_url_;
const url::Origin frame_origin_;
MockLocalPresentationManager* mock_local_manager_;
// |source1_| and |source2_| correspond to |presentation_url1_| and
// |presentation_url2_|, respectively.
MediaSource source1_;
MediaSource source2_;
// |listener1_| and |listener2_| correspond to |presentation_url1_| and
// |presentation_url2_|, respectively.
MockScreenAvailabilityListener listener1_;
MockScreenAvailabilityListener listener2_;
// Set in SetMainFrame().
int main_frame_process_id_ = 0;
int main_frame_routing_id_ = 0;
// Set in SetUp().
std::unique_ptr<content::PresentationRequest> presentation_request_;
};
class PresentationServiceDelegateImplIncognitoTest
: public PresentationServiceDelegateImplTest {
public:
PresentationServiceDelegateImplIncognitoTest()
: incognito_web_contents_(nullptr) {}
protected:
content::WebContents* GetWebContents() override {
if (!incognito_web_contents_) {
Profile* incognito_profile = profile()->GetOffTheRecordProfile();
incognito_web_contents_ =
content::WebContentsTester::CreateTestWebContents(incognito_profile,
nullptr);
}
return incognito_web_contents_.get();
}
void TearDown() override {
// We must delete the incognito WC first, as that triggers observers which
// require RenderViewHost, etc., that in turn are deleted by
// RenderViewHostTestHarness::TearDown().
incognito_web_contents_.reset();
PresentationServiceDelegateImplTest::TearDown();
}
std::unique_ptr<content::WebContents> incognito_web_contents_;
};
TEST_F(PresentationServiceDelegateImplTest, AddScreenAvailabilityListener) {
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).WillOnce(Return(true));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()))
<< "Mapping not found for " << source1_;
EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_));
delegate_impl_->RemoveScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
}
TEST_F(PresentationServiceDelegateImplTest, AddMultipleListenersToFrame) {
ON_CALL(*router_, RegisterMediaSinksObserver(_)).WillByDefault(Return(true));
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).Times(2);
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener2_));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()))
<< "Mapping not found for " << source1_;
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source2_.id()))
<< "Mapping not found for " << source2_;
EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(2);
delegate_impl_->RemoveScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_);
delegate_impl_->RemoveScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener2_);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source2_.id()));
}
TEST_F(PresentationServiceDelegateImplTest, AddSameListenerTwice) {
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).WillOnce(Return(true));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(1);
delegate_impl_->RemoveScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
}
TEST_F(PresentationServiceDelegateImplTest, AddListenerForInvalidUrl) {
MockScreenAvailabilityListener listener(GURL("unsupported-url://foo"));
EXPECT_CALL(listener,
OnScreenAvailabilityChanged(
blink::mojom::ScreenAvailability::SOURCE_NOT_SUPPORTED));
EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener));
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).Times(0);
}
TEST_F(PresentationServiceDelegateImplTest, SetDefaultPresentationUrl) {
EXPECT_FALSE(delegate_impl_->HasDefaultPresentationRequest());
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url_);
auto callback = base::BindRepeating(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
delegate_impl_->SetDefaultPresentationUrls(*presentation_request_, callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
const auto& request1 = delegate_impl_->GetDefaultPresentationRequest();
EXPECT_EQ(presentation_urls_, request1.presentation_urls);
// Set to a new default presentation URL
std::vector<GURL> new_urls = {presentation_url2_};
presentation_request_->presentation_urls = new_urls;
delegate_impl_->SetDefaultPresentationUrls(*presentation_request_, callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
const auto& request2 = delegate_impl_->GetDefaultPresentationRequest();
EXPECT_EQ(new_urls, request2.presentation_urls);
// Remove default presentation URL.
presentation_request_->presentation_urls.clear();
delegate_impl_->SetDefaultPresentationUrls(*presentation_request_, callback);
EXPECT_FALSE(delegate_impl_->HasDefaultPresentationRequest());
}
TEST_F(PresentationServiceDelegateImplTest, DefaultPresentationUrlCallback) {
RunDefaultPresentationUrlCallbackTest(false);
}
TEST_F(PresentationServiceDelegateImplIncognitoTest,
DefaultPresentationUrlCallback) {
RunDefaultPresentationUrlCallbackTest(true);
}
TEST_F(PresentationServiceDelegateImplTest,
DefaultPresentationRequestObserver) {
auto callback = base::BindRepeating(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
StrictMock<MockDefaultPresentationRequestObserver> observer;
delegate_impl_->AddDefaultPresentationRequestObserver(&observer);
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url_);
std::vector<GURL> request1_urls = {presentation_url1_};
content::PresentationRequest observed_request1(
{main_frame_process_id_, main_frame_routing_id_}, request1_urls,
frame_origin_);
EXPECT_CALL(observer, OnDefaultPresentationChanged(_)).Times(1);
delegate_impl_->SetDefaultPresentationUrls(std::move(observed_request1),
callback);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer));
std::vector<GURL> request2_urls = {presentation_url2_};
content::PresentationRequest observed_request2(
{main_frame_process_id_, main_frame_routing_id_}, request2_urls,
frame_origin_);
EXPECT_CALL(observer, OnDefaultPresentationChanged(_)).Times(1);
delegate_impl_->SetDefaultPresentationUrls(std::move(observed_request2),
callback);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer));
// Remove default presentation URL.
EXPECT_CALL(observer, OnDefaultPresentationRemoved()).Times(1);
content::PresentationRequest empty_request(
{main_frame_process_id_, main_frame_routing_id_}, std::vector<GURL>(),
frame_origin_);
delegate_impl_->SetDefaultPresentationUrls(std::move(empty_request),
callback);
}
TEST_F(PresentationServiceDelegateImplTest, ListenForConnnectionStateChange) {
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url_);
// Set up a PresentationConnection so we can listen to it.
MediaRouteResponseCallback route_response_callback;
EXPECT_CALL(*router_, JoinRouteInternal(_, _, _, _, _, _, false))
.WillOnce(WithArgs<4>(Invoke(
[&route_response_callback](MediaRouteResponseCallback& callback) {
route_response_callback = std::move(callback);
})));
const std::string kPresentationId("pid");
presentation_urls_.push_back(GURL(kPresentationUrl3));
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, IsLocalPresentation(kPresentationId))
.WillRepeatedly(Return(false));
MockCreatePresentationConnnectionCallbacks mock_create_connection_callbacks;
delegate_impl_->ReconnectPresentation(
*presentation_request_, kPresentationId,
base::BindOnce(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::BindOnce(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionSuccess(_))
.Times(1);
std::unique_ptr<RouteRequestResult> result = RouteRequestResult::FromSuccess(
MediaRoute("routeId", source1_, "mediaSinkId", "description", true, true),
kPresentationId);
std::move(route_response_callback).Run(/** connection */ nullptr, *result);
base::MockCallback<content::PresentationConnectionStateChangedCallback>
mock_callback;
auto callback = mock_callback.Get();
PresentationInfo connection(presentation_url1_, kPresentationId);
EXPECT_CALL(*router_, OnAddPresentationConnectionStateChangedCallbackInvoked(
Equals(callback)));
delegate_impl_->ListenForConnectionStateChange(
main_frame_process_id_, main_frame_routing_id_, connection, callback);
}
TEST_F(PresentationServiceDelegateImplTest, Reset) {
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
.WillRepeatedly(Return(true));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
EXPECT_CALL(*router_, UnregisterMediaSinksObserver(_)).Times(1);
delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
main_frame_process_id_, main_frame_routing_id_, source1_.id()));
}
TEST_F(PresentationServiceDelegateImplTest, DelegateObservers) {
std::unique_ptr<PresentationServiceDelegateImpl> manager(
new PresentationServiceDelegateImpl(GetWebContents()));
StrictMock<MockDelegateObserver> delegate_observer1;
StrictMock<MockDelegateObserver> delegate_observer2;
manager->AddObserver(123, 234, &delegate_observer1);
manager->AddObserver(345, 456, &delegate_observer2);
// Removes |delegate_observer2|.
manager->RemoveObserver(345, 456);
EXPECT_CALL(delegate_observer1, OnDelegateDestroyed()).Times(1);
manager.reset();
}
TEST_F(PresentationServiceDelegateImplTest, SinksObserverCantRegister) {
EXPECT_CALL(*router_, RegisterMediaSinksObserver(_)).WillOnce(Return(false));
EXPECT_CALL(listener1_, OnScreenAvailabilityChanged(
blink::mojom::ScreenAvailability::DISABLED));
EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
main_frame_process_id_, main_frame_routing_id_, &listener1_));
}
TEST_F(PresentationServiceDelegateImplTest,
TestCloseConnectionForLocalPresentation) {
GURL presentation_url = GURL("http://www.example.com/presentation.html");
PresentationInfo presentation_info(presentation_url, kPresentationId);
content::GlobalFrameRoutingId rfh_id(main_frame_process_id_,
main_frame_routing_id_);
MediaRoute media_route("route_id",
MediaSourceForPresentationUrl(presentation_url),
"mediaSinkId", "", true, true);
media_route.set_local_presentation(true);
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, IsLocalPresentation(kPresentationId))
.WillRepeatedly(Return(true));
base::MockCallback<content::PresentationConnectionCallback> success_cb;
EXPECT_CALL(success_cb, Run(_));
delegate_impl_->OnStartPresentationSucceeded(
rfh_id, success_cb.Get(), presentation_info, /** connection */ nullptr,
media_route);
EXPECT_CALL(mock_local_manager,
UnregisterLocalPresentationController(kPresentationId, rfh_id))
.Times(1);
EXPECT_CALL(*router_, DetachRoute(_)).Times(0);
delegate_impl_->CloseConnection(main_frame_process_id_,
main_frame_routing_id_, kPresentationId);
}
TEST_F(PresentationServiceDelegateImplTest,
TestReconnectPresentationForLocalPresentation) {
MediaRoute media_route("route_id",
MediaSourceForPresentationUrl(presentation_url1_),
"mediaSinkId", "", true, true);
media_route.set_local_presentation(true);
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, IsLocalPresentation(kPresentationId))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_local_manager, GetRoute(kPresentationId))
.WillRepeatedly(Return(&media_route));
base::MockCallback<content::PresentationConnectionCallback> success_cb;
base::MockCallback<content::PresentationConnectionErrorCallback> error_cb;
EXPECT_CALL(success_cb, Run(_));
EXPECT_CALL(mock_local_manager,
UnregisterLocalPresentationController(
kPresentationId,
content::GlobalFrameRoutingId(main_frame_process_id_,
main_frame_routing_id_)));
delegate_impl_->ReconnectPresentation(*presentation_request_, kPresentationId,
success_cb.Get(), error_cb.Get());
delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
}
TEST_F(PresentationServiceDelegateImplTest, ConnectToLocalPresentation) {
content::GlobalFrameRoutingId rfh_id(main_frame_process_id_,
main_frame_routing_id_);
PresentationInfo presentation_info(presentation_url1_, kPresentationId);
MediaRoute media_route("route_id",
MediaSourceForPresentationUrl(presentation_info.url),
"mediaSinkId", "", true, true);
media_route.set_local_presentation(true);
content::PresentationConnectionPtr receiver_ptr;
MockPresentationConnectionProxy controller_proxy;
mojo::Binding<PresentationConnection> controller_binding(&controller_proxy);
auto success_cb = [&controller_binding,
&receiver_ptr](PresentationConnectionResultPtr result) {
controller_binding.Bind(std::move(result->connection_request));
receiver_ptr =
content::PresentationConnectionPtr(std::move(result->connection_ptr));
};
content::PresentationConnectionPtr controller_ptr;
MockPresentationConnectionProxy receiver_proxy;
mojo::Binding<PresentationConnection> receiver_binding(&receiver_proxy);
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, RegisterLocalPresentationControllerInternal(
InfoEquals(presentation_info), rfh_id, _,
_, Equals(media_route)))
.WillOnce([&receiver_binding, &controller_ptr](
const PresentationInfo&,
const content::GlobalFrameRoutingId&,
content::PresentationConnectionPtr& controller,
content::PresentationConnectionRequest& request,
const MediaRoute&) {
ASSERT_TRUE(controller && request);
receiver_binding.Bind(std::move(request));
controller_ptr = std::move(controller);
});
delegate_impl_->OnStartPresentationSucceeded(
rfh_id,
base::BindOnce(&decltype(success_cb)::operator(),
base::Unretained(&success_cb)),
presentation_info, /** connection */ nullptr, media_route);
EXPECT_CALL(controller_proxy, OnMessage(_)).WillOnce([](auto message) {
EXPECT_TRUE(
message->Equals(*PresentationConnectionMessage::NewMessage("alpha")));
});
controller_ptr->OnMessage(PresentationConnectionMessage::NewMessage("alpha"));
base::RunLoop().RunUntilIdle();
EXPECT_CALL(receiver_proxy, OnMessage(_)).WillOnce([](auto message) {
EXPECT_TRUE(
message->Equals(*PresentationConnectionMessage::NewMessage("beta")));
});
receiver_ptr->OnMessage(PresentationConnectionMessage::NewMessage("beta"));
base::RunLoop().RunUntilIdle();
EXPECT_CALL(mock_local_manager,
UnregisterLocalPresentationController(kPresentationId, rfh_id));
EXPECT_CALL(*router_, DetachRoute(_)).Times(0);
delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
}
TEST_F(PresentationServiceDelegateImplTest, ConnectToPresentation) {
content::GlobalFrameRoutingId rfh_id(main_frame_process_id_,
main_frame_routing_id_);
PresentationInfo presentation_info(presentation_url1_, kPresentationId);
MediaRoute media_route("route_id",
MediaSourceForPresentationUrl(presentation_info.url),
"mediaSinkId", "", true, true);
content::PresentationConnectionPtr connection_ptr;
MockPresentationConnectionProxy mock_proxy;
mojo::Binding<PresentationConnection> binding(&mock_proxy);
auto success_cb = [&binding,
&connection_ptr](PresentationConnectionResultPtr result) {
binding.Bind(std::move(result->connection_request));
connection_ptr =
content::PresentationConnectionPtr(std::move(result->connection_ptr));
};
RouteMessageObserver* proxy_message_observer = nullptr;
EXPECT_CALL(*router_, RegisterRouteMessageObserver(_))
.WillOnce(::testing::SaveArg<0>(&proxy_message_observer));
// Note: This specifically tests the messaging case where no mojo pipe is
// returned to PresentationServiceDelegateImpl and it is not a local
// presentation. If a mojo PresentationConnection _were_ returned, the
// following route message calls would not take place.
delegate_impl_->OnStartPresentationSucceeded(
rfh_id,
base::BindOnce(&decltype(success_cb)::operator(),
base::Unretained(&success_cb)),
presentation_info, /** connection */ nullptr, media_route);
EXPECT_CALL(*router_,
SendRouteMessage(media_route.media_route_id(), "alpha"));
connection_ptr->OnMessage(PresentationConnectionMessage::NewMessage("alpha"));
base::RunLoop().RunUntilIdle();
EXPECT_CALL(mock_proxy, OnMessage(_)).WillOnce([](auto message) {
EXPECT_TRUE(
message->Equals(*PresentationConnectionMessage::NewMessage("beta")));
});
std::vector<mojom::RouteMessagePtr> messages;
messages.emplace_back(mojom::RouteMessage::New(
mojom::RouteMessage::Type::TEXT, "beta", base::nullopt));
proxy_message_observer->OnMessagesReceived(std::move(messages));
base::RunLoop().RunUntilIdle();
EXPECT_CALL(*router_, UnregisterRouteMessageObserver(_));
EXPECT_CALL(*router_, DetachRoute("route_id")).Times(1);
delegate_impl_->Reset(main_frame_process_id_, main_frame_routing_id_);
}
#if !defined(OS_ANDROID)
TEST_F(PresentationServiceDelegateImplTest, AutoJoinRequest) {
std::string origin(frame_origin_.Serialize());
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url_);
MockCreatePresentationConnnectionCallbacks mock_create_connection_callbacks;
const std::string kPresentationId("auto-join");
ASSERT_TRUE(IsAutoJoinPresentationId(kPresentationId));
// Set the user preference for |origin| to prefer tab mirroring.
{
ListPrefUpdate update(profile()->GetPrefs(),
prefs::kMediaRouterTabMirroringSources);
update->AppendIfNotPresent(std::make_unique<base::Value>(origin));
}
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, IsLocalPresentation(kPresentationId))
.WillRepeatedly(Return(false));
// Auto-join requests should be rejected.
EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionError(_));
EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
.Times(0);
delegate_impl_->ReconnectPresentation(
*presentation_request_, kPresentationId,
base::BindOnce(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::BindOnce(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
// Remove the user preference for |origin|.
{
ListPrefUpdate update(profile()->GetPrefs(),
prefs::kMediaRouterTabMirroringSources);
update->Remove(base::Value(origin), nullptr);
}
// Auto-join requests should now go through.
EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
.Times(1);
delegate_impl_->ReconnectPresentation(
*presentation_request_, kPresentationId,
base::BindOnce(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::BindOnce(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
}
TEST_F(PresentationServiceDelegateImplIncognitoTest, AutoJoinRequest) {
std::string origin(frame_origin_.Serialize());
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url_);
MockCreatePresentationConnnectionCallbacks mock_create_connection_callbacks;
const std::string kPresentationId("auto-join");
ASSERT_TRUE(IsAutoJoinPresentationId(kPresentationId));
// Set the user preference for |origin| to prefer tab mirroring.
{
ListPrefUpdate update(profile()->GetOffTheRecordProfile()->GetPrefs(),
prefs::kMediaRouterTabMirroringSources);
update->AppendIfNotPresent(std::make_unique<base::Value>(origin));
}
auto& mock_local_manager = GetMockLocalPresentationManager();
EXPECT_CALL(mock_local_manager, IsLocalPresentation(kPresentationId))
.WillRepeatedly(Return(false));
// Setting the pref in incognito shouldn't set it for the non-incognito
// profile.
const base::ListValue* non_incognito_origins =
profile()->GetPrefs()->GetList(prefs::kMediaRouterTabMirroringSources);
EXPECT_EQ(non_incognito_origins->Find(base::Value(origin)),
non_incognito_origins->end());
// Auto-join requests should be rejected.
EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionError(_));
EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
.Times(0);
delegate_impl_->ReconnectPresentation(
*presentation_request_, kPresentationId,
base::BindOnce(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::BindOnce(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
// Remove the user preference for |origin| in incognito.
{
ListPrefUpdate update(profile()->GetOffTheRecordProfile()->GetPrefs(),
prefs::kMediaRouterTabMirroringSources);
update->Remove(base::Value(origin), nullptr);
}
// Auto-join requests should now go through.
EXPECT_CALL(*router_, JoinRouteInternal(_, kPresentationId, _, _, _, _, _))
.Times(1);
delegate_impl_->ReconnectPresentation(
*presentation_request_, kPresentationId,
base::BindOnce(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::BindOnce(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
}
#endif // !defined(OS_ANDROID)
} // namespace media_router