blob: 6d4aa38b187247581a20b18d37758dbd1cea107c [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_service_delegate_impl.h"
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/media/router/media_source.h"
#include "chrome/browser/media/router/media_source_helper.h"
#include "chrome/browser/media/router/mock_media_router.h"
#include "chrome/browser/media/router/mock_screen_availability_listener.h"
#include "chrome/browser/media/router/route_request_result.h"
#include "chrome/browser/media/router/test_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/presentation_screen_availability_listener.h"
#include "content/public/browser/presentation_session.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 "testing/gmock/include/gmock/gmock.h"
#include "ui/base/l10n/l10n_util.h"
using ::testing::_;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace media_router {
class MockDelegateObserver
: public content::PresentationServiceDelegate::Observer {
public:
MOCK_METHOD0(OnDelegateDestroyed, void());
MOCK_METHOD1(OnDefaultPresentationStarted,
void(const content::PresentationSessionInfo&));
};
class MockDefaultPresentationRequestObserver
: public PresentationServiceDelegateImpl::
DefaultPresentationRequestObserver {
public:
MOCK_METHOD1(OnDefaultPresentationChanged, void(const PresentationRequest&));
MOCK_METHOD0(OnDefaultPresentationRemoved, void());
};
class MockCreatePresentationConnnectionCallbacks {
public:
MOCK_METHOD1(OnCreateConnectionSuccess,
void(const content::PresentationSessionInfo& connection));
MOCK_METHOD1(OnCreateConnectionError,
void(const content::PresentationError& error));
};
class PresentationServiceDelegateImplTest
: public ChromeRenderViewHostTestHarness {
public:
PresentationServiceDelegateImplTest() : delegate_impl_(nullptr) {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
content::WebContents* wc = GetWebContents();
ASSERT_TRUE(wc);
PresentationServiceDelegateImpl::CreateForWebContents(wc);
delegate_impl_ = PresentationServiceDelegateImpl::FromWebContents(wc);
delegate_impl_->SetMediaRouterForTest(&router_);
}
MOCK_METHOD1(OnDefaultPresentationStarted,
void(const content::PresentationSessionInfo& session_info));
protected:
virtual content::WebContents* GetWebContents() { return web_contents(); }
void RunDefaultPresentationUrlCallbackTest(bool incognito) {
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int routing_id = main_frame->GetRoutingID();
auto callback = base::Bind(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
std::string presentation_url1("http://foo.fakeUrl");
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id,
presentation_url1, callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
PresentationRequest 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, *result);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
// Should not trigger callback since request doesn't match.
std::string presentation_url2("http://bar.fakeUrl");
PresentationRequest different_request(
RenderFrameHostId(100, 200), presentation_url2,
GURL("http://anotherFrameUrl.fakeUrl"));
MediaRoute* media_route = new MediaRoute(
"differentRouteId", MediaSourceForPresentationUrl(presentation_url2),
"mediaSinkId", "", true, "", true);
media_route->set_incognito(incognito);
result = RouteRequestResult::FromSuccess(base::WrapUnique(media_route),
"differentPresentationId");
delegate_impl_->OnRouteResponse(different_request, *result);
EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
// Should trigger callback since request matches.
EXPECT_CALL(*this, OnDefaultPresentationStarted(_)).Times(1);
MediaRoute* media_route2 = new MediaRoute(
"routeId", MediaSourceForPresentationUrl(presentation_url1),
"mediaSinkId", "", true, "", true);
media_route2->set_incognito(incognito);
result = RouteRequestResult::FromSuccess(base::WrapUnique(media_route2),
"presentationId");
delegate_impl_->OnRouteResponse(request, *result);
}
PresentationServiceDelegateImpl* delegate_impl_;
MockMediaRouter router_;
};
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_;
}
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().
delete incognito_web_contents_;
PresentationServiceDelegateImplTest::TearDown();
}
content::WebContents* incognito_web_contents_;
};
TEST_F(PresentationServiceDelegateImplTest, AddScreenAvailabilityListener) {
std::string presentation_url1("http://url1.fakeUrl");
std::string presentation_url2("http://url2.fakeUrl");
MediaSource source1 = MediaSourceForPresentationUrl(presentation_url1);
MediaSource source2 = MediaSourceForPresentationUrl(presentation_url2);
MockScreenAvailabilityListener listener1(presentation_url1);
MockScreenAvailabilityListener listener2(presentation_url2);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int render_frame_id1 = main_frame->GetRoutingID();
// Note that |render_frame_id2| does not correspond to a real frame. As a
// result, the observer added with have an empty GURL as origin.
int render_frame_id2 = 2;
EXPECT_CALL(router_, RegisterMediaSinksObserver(_))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id1, &listener1));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id2, &listener2));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id1, source1.id()))
<< "Mapping not found for " << source1.ToString();
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id2, source2.id()))
<< "Mapping not found for " << source2.ToString();
EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(2);
delegate_impl_->RemoveScreenAvailabilityListener(
render_process_id, render_frame_id1, &listener1);
delegate_impl_->RemoveScreenAvailabilityListener(
render_process_id, render_frame_id2, &listener2);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id1, source1.id()));
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id2, source2.id()));
}
TEST_F(PresentationServiceDelegateImplTest, AddMultipleListenersToFrame) {
ON_CALL(router_, RegisterMediaSinksObserver(_)).WillByDefault(Return(true));
std::string presentation_url1("http://url1.com");
std::string presentation_url2("http://url2.com");
MediaSource source1 = MediaSourceForPresentationUrl(presentation_url1);
MediaSource source2 = MediaSourceForPresentationUrl(presentation_url2);
MockScreenAvailabilityListener listener1(presentation_url1);
MockScreenAvailabilityListener listener2(presentation_url2);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int render_frame_id = main_frame->GetRoutingID();
EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).Times(2);
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener1));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener2));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source1.id()))
<< "Mapping not found for " << source1.ToString();
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source2.id()))
<< "Mapping not found for " << source2.ToString();
EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(2);
delegate_impl_->RemoveScreenAvailabilityListener(
render_process_id, render_frame_id, &listener1);
delegate_impl_->RemoveScreenAvailabilityListener(
render_process_id, render_frame_id, &listener2);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source1.id()));
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source2.id()));
}
TEST_F(PresentationServiceDelegateImplTest, AddSameListenerTwice) {
std::string presentation_url1("http://url1.fakeUrl");
MediaSource source1(MediaSourceForPresentationUrl(presentation_url1));
MockScreenAvailabilityListener listener1(presentation_url1);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int render_frame_id = main_frame->GetRoutingID();
EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).WillOnce(Return(true));
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener1));
EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener1));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source1.id()));
EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(1);
delegate_impl_->RemoveScreenAvailabilityListener(render_process_id,
render_frame_id, &listener1);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source1.id()));
}
// TODO(imcheng): Add a test to set default presentation URL in a different
// RenderFrameHost and verify that it is ignored.
TEST_F(PresentationServiceDelegateImplTest, SetDefaultPresentationUrl) {
EXPECT_FALSE(delegate_impl_->HasDefaultPresentationRequest());
GURL frame_url("http://www.google.com");
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int routing_id = main_frame->GetRoutingID();
auto callback = base::Bind(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
std::string presentation_url1("http://foo.fakeUrl");
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id,
presentation_url1, callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
PresentationRequest request1 =
delegate_impl_->GetDefaultPresentationRequest();
EXPECT_EQ(presentation_url1, request1.presentation_url());
EXPECT_EQ(RenderFrameHostId(render_process_id, routing_id),
request1.render_frame_host_id());
EXPECT_EQ(frame_url, request1.frame_url());
// Set to a new default presentation URL
std::string presentation_url2("https://youtube.com");
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id,
presentation_url2, callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
PresentationRequest request2 =
delegate_impl_->GetDefaultPresentationRequest();
EXPECT_EQ(presentation_url2, request2.presentation_url());
EXPECT_EQ(RenderFrameHostId(render_process_id, routing_id),
request2.render_frame_host_id());
EXPECT_EQ(frame_url, request2.frame_url());
// Remove default presentation URL.
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id, "",
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::Bind(
&PresentationServiceDelegateImplTest::OnDefaultPresentationStarted,
base::Unretained(this));
StrictMock<MockDefaultPresentationRequestObserver> observer;
delegate_impl_->AddDefaultPresentationRequestObserver(&observer);
GURL frame_url("http://www.google.com");
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int routing_id = main_frame->GetRoutingID();
std::string url1("http://foo.fakeUrl");
PresentationRequest observed_request1(
RenderFrameHostId(render_process_id, routing_id), url1, frame_url);
EXPECT_CALL(observer, OnDefaultPresentationChanged(Equals(observed_request1)))
.Times(1);
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id, url1,
callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
PresentationRequest request1 =
delegate_impl_->GetDefaultPresentationRequest();
EXPECT_TRUE(request1.Equals(observed_request1));
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer));
std::string url2("http://youtube.com");
PresentationRequest observed_request2(
RenderFrameHostId(render_process_id, routing_id), url2, frame_url);
EXPECT_CALL(observer, OnDefaultPresentationChanged(Equals(observed_request2)))
.Times(1);
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id, url2,
callback);
ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
PresentationRequest request2 =
delegate_impl_->GetDefaultPresentationRequest();
EXPECT_TRUE(request2.Equals(observed_request2));
EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer));
// Remove default presentation URL.
EXPECT_CALL(observer, OnDefaultPresentationRemoved()).Times(1);
delegate_impl_->SetDefaultPresentationUrl(render_process_id, routing_id, "",
callback);
}
TEST_F(PresentationServiceDelegateImplTest, ListenForConnnectionStateChange) {
GURL frame_url("http://www.google.com");
content::WebContentsTester::For(GetWebContents())
->NavigateAndCommit(frame_url);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int routing_id = main_frame->GetRoutingID();
// Set up a PresentationConnection so we can listen to it.
std::vector<MediaRouteResponseCallback> route_response_callbacks;
EXPECT_CALL(router_, JoinRoute(_, _, _, _, _, _, false))
.WillOnce(SaveArg<4>(&route_response_callbacks));
const std::string kPresentationUrl("http://url1.fakeUrl");
const std::string kPresentationId("pid");
MockCreatePresentationConnnectionCallbacks mock_create_connection_callbacks;
delegate_impl_->JoinSession(
render_process_id, routing_id, kPresentationUrl, kPresentationId,
base::Bind(&MockCreatePresentationConnnectionCallbacks::
OnCreateConnectionSuccess,
base::Unretained(&mock_create_connection_callbacks)),
base::Bind(
&MockCreatePresentationConnnectionCallbacks::OnCreateConnectionError,
base::Unretained(&mock_create_connection_callbacks)));
EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionSuccess(_))
.Times(1);
std::unique_ptr<RouteRequestResult> result = RouteRequestResult::FromSuccess(
base::WrapUnique(new MediaRoute(
"routeId", MediaSourceForPresentationUrl(kPresentationUrl),
"mediaSinkId", "description", true, "", true)),
kPresentationId);
for (const auto& route_response_callback : route_response_callbacks)
route_response_callback.Run(*result);
MockPresentationConnectionStateChangedCallback mock_callback;
content::PresentationConnectionStateChangedCallback callback =
base::Bind(&MockPresentationConnectionStateChangedCallback::Run,
base::Unretained(&mock_callback));
content::PresentationSessionInfo connection(kPresentationUrl,
kPresentationId);
EXPECT_CALL(router_, OnAddPresentationConnectionStateChangedCallbackInvoked(
Equals(callback)));
delegate_impl_->ListenForConnectionStateChange(render_process_id, routing_id,
connection, callback);
}
TEST_F(PresentationServiceDelegateImplTest, Reset) {
EXPECT_CALL(router_, RegisterMediaSinksObserver(_))
.WillRepeatedly(Return(true));
std::string presentation_url1("http://url1.fakeUrl");
MediaSource source = MediaSourceForPresentationUrl(presentation_url1);
MockScreenAvailabilityListener listener1(presentation_url1);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int render_frame_id = main_frame->GetRoutingID();
EXPECT_TRUE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener1));
EXPECT_TRUE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source.id()));
EXPECT_CALL(router_, UnregisterMediaSinksObserver(_)).Times(1);
delegate_impl_->Reset(render_process_id, render_frame_id);
EXPECT_FALSE(delegate_impl_->HasScreenAvailabilityListenerForTest(
render_process_id, render_frame_id, source.id()));
}
TEST_F(PresentationServiceDelegateImplTest, DelegateObservers) {
std::unique_ptr<PresentationServiceDelegateImpl> manager(
new PresentationServiceDelegateImpl(GetWebContents()));
manager->SetMediaRouterForTest(&router_);
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) {
const std::string presentation_url("http://url1.fakeUrl");
MockScreenAvailabilityListener listener(presentation_url);
content::RenderFrameHost* main_frame = GetWebContents()->GetMainFrame();
ASSERT_TRUE(main_frame);
int render_process_id = main_frame->GetProcess()->GetID();
int render_frame_id = main_frame->GetRoutingID();
EXPECT_CALL(router_, RegisterMediaSinksObserver(_)).WillOnce(Return(false));
EXPECT_CALL(listener, OnScreenAvailabilityNotSupported());
EXPECT_FALSE(delegate_impl_->AddScreenAvailabilityListener(
render_process_id, render_frame_id, &listener));
}
} // namespace media_router