1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
6
7 #include "base/bind.h"
8 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
9 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
10 #include "content/public/test/web_contents_tester.h"
11 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/blink/public/common/tokens/tokens.h"
14 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
15
16 namespace {
17
18 class LenientMockObserver : public MediaStreamCaptureIndicator::Observer {
19 public:
20 LenientMockObserver() = default;
~LenientMockObserver()21 ~LenientMockObserver() override {}
22
23 // Helper functions used to set the expectations of the mock methods. This
24 // allows passing function pointers to
25 // MediaStreamCaptureIndicatorTest::TestObserverMethod.
26
SetOnIsCapturingVideoChangedExpectation(content::WebContents * contents,bool is_capturing_video)27 void SetOnIsCapturingVideoChangedExpectation(content::WebContents* contents,
28 bool is_capturing_video) {
29 EXPECT_CALL(*this, OnIsCapturingVideoChanged(contents, is_capturing_video));
30 }
31
SetOnIsCapturingAudioChangedExpectation(content::WebContents * contents,bool is_capturing_audio)32 void SetOnIsCapturingAudioChangedExpectation(content::WebContents* contents,
33 bool is_capturing_audio) {
34 EXPECT_CALL(*this, OnIsCapturingAudioChanged(contents, is_capturing_audio));
35 }
36
SetOnIsBeingMirroredChangedExpectation(content::WebContents * contents,bool is_being_mirrored)37 void SetOnIsBeingMirroredChangedExpectation(content::WebContents* contents,
38 bool is_being_mirrored) {
39 EXPECT_CALL(*this, OnIsBeingMirroredChanged(contents, is_being_mirrored));
40 }
41
SetOnIsCapturingWindowChangedExpectation(content::WebContents * contents,bool is_capturing_window)42 void SetOnIsCapturingWindowChangedExpectation(content::WebContents* contents,
43 bool is_capturing_window) {
44 EXPECT_CALL(*this,
45 OnIsCapturingWindowChanged(contents, is_capturing_window));
46 }
47
SetOnIsCapturingDisplayChangedExpectation(content::WebContents * contents,bool is_capturing_display)48 void SetOnIsCapturingDisplayChangedExpectation(content::WebContents* contents,
49 bool is_capturing_display) {
50 EXPECT_CALL(*this,
51 OnIsCapturingDisplayChanged(contents, is_capturing_display));
52 }
53
54 private:
55 MOCK_METHOD2(OnIsCapturingVideoChanged,
56 void(content::WebContents* contents, bool is_capturing_video));
57 MOCK_METHOD2(OnIsCapturingAudioChanged,
58 void(content::WebContents* contents, bool is_capturing_audio));
59 MOCK_METHOD2(OnIsBeingMirroredChanged,
60 void(content::WebContents* contents, bool is_being_mirrored));
61 MOCK_METHOD2(OnIsCapturingWindowChanged,
62 void(content::WebContents* contents, bool is_capturing_window));
63 MOCK_METHOD2(OnIsCapturingDisplayChanged,
64 void(content::WebContents* contents, bool is_capturing_display));
65
66 DISALLOW_COPY_AND_ASSIGN(LenientMockObserver);
67 };
68 using MockObserver = testing::StrictMock<LenientMockObserver>;
69
70 typedef void (MockObserver::*MockObserverSetExpectationsMethod)(
71 content::WebContents* web_contents,
72 bool value);
73 typedef bool (MediaStreamCaptureIndicator::*AccessorMethod)(
74 content::WebContents* web_contents) const;
75
76 class MediaStreamCaptureIndicatorTest : public ChromeRenderViewHostTestHarness {
77 public:
MediaStreamCaptureIndicatorTest()78 MediaStreamCaptureIndicatorTest() {}
~MediaStreamCaptureIndicatorTest()79 ~MediaStreamCaptureIndicatorTest() override {}
80 MediaStreamCaptureIndicatorTest(const MediaStreamCaptureIndicatorTest&) =
81 delete;
82 MediaStreamCaptureIndicatorTest& operator=(
83 const MediaStreamCaptureIndicatorTest&) = delete;
84
SetUp()85 void SetUp() override {
86 ChromeRenderViewHostTestHarness::SetUp();
87 content::WebContentsTester::For(web_contents())
88 ->NavigateAndCommit(GURL("https://www.example.com/"));
89 indicator_ = MediaCaptureDevicesDispatcher::GetInstance()
90 ->GetMediaStreamCaptureIndicator();
91 observer_ = std::make_unique<MockObserver>();
92 indicator_->AddObserver(observer());
93 portal_token_ = content::WebContentsTester::For(web_contents())
94 ->CreatePortal(CreateTestWebContents());
95 }
96
TearDown()97 void TearDown() override {
98 indicator_->RemoveObserver(observer());
99 observer_.reset();
100 indicator_.reset();
101 ChromeRenderViewHostTestHarness::TearDown();
102 }
103
indicator()104 MediaStreamCaptureIndicator* indicator() { return indicator_.get(); }
portal_contents()105 content::WebContents* portal_contents() {
106 return content::WebContentsTester::For(web_contents())
107 ->GetPortalContents(portal_token_);
108 }
observer()109 MockObserver* observer() { return observer_.get(); }
110
111 private:
112 std::unique_ptr<MockObserver> observer_;
113 scoped_refptr<MediaStreamCaptureIndicator> indicator_;
114 blink::PortalToken portal_token_;
115 };
116
117 struct ObserverMethodTestParam {
118 blink::mojom::MediaStreamType stream_type;
119 base::Optional<media::mojom::DisplayMediaInformation> display_media_info;
120 MockObserverSetExpectationsMethod observer_method;
121 AccessorMethod accessor_method;
122 };
123
124 ObserverMethodTestParam kObserverMethodTestParams[] = {
125 {blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
126 /*display_media_info=*/base::nullopt,
127 &MockObserver::SetOnIsCapturingVideoChangedExpectation,
128 &MediaStreamCaptureIndicator::IsCapturingVideo},
129 {blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
130 /*display_media_info=*/base::nullopt,
131 &MockObserver::SetOnIsCapturingAudioChangedExpectation,
132 &MediaStreamCaptureIndicator::IsCapturingAudio},
133 {blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
134 /*display_media_info=*/base::nullopt,
135 &MockObserver::SetOnIsBeingMirroredChangedExpectation,
136 &MediaStreamCaptureIndicator::IsBeingMirrored},
137 {blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
138 /*display_media_info=*/base::nullopt,
139 &MockObserver::SetOnIsCapturingWindowChangedExpectation,
140 &MediaStreamCaptureIndicator::IsCapturingWindow},
141 {blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
142 media::mojom::DisplayMediaInformation(
143 media::mojom::DisplayCaptureSurfaceType::MONITOR,
144 /*logical_surface=*/true,
145 media::mojom::CursorCaptureType::NEVER),
146 &MockObserver::SetOnIsCapturingDisplayChangedExpectation,
147 &MediaStreamCaptureIndicator::IsCapturingDisplay},
148 };
149
150 class MediaStreamCaptureIndicatorObserverMethodTest
151 : public MediaStreamCaptureIndicatorTest,
152 public testing::WithParamInterface<
153 std::tuple<ObserverMethodTestParam, bool>> {};
154
CreateFakeDevice(const ObserverMethodTestParam & param)155 blink::MediaStreamDevice CreateFakeDevice(
156 const ObserverMethodTestParam& param) {
157 blink::MediaStreamDevice device(param.stream_type, "fake_device",
158 "fake_device");
159 if (param.display_media_info)
160 device.display_media_info = param.display_media_info->Clone();
161
162 return device;
163 }
164
165 } // namespace
166
TEST_P(MediaStreamCaptureIndicatorObserverMethodTest,AddAndRemoveDevice)167 TEST_P(MediaStreamCaptureIndicatorObserverMethodTest, AddAndRemoveDevice) {
168 const ObserverMethodTestParam& param = std::get<0>(GetParam());
169 bool is_portal = std::get<1>(GetParam());
170 content::WebContents* source = is_portal ? portal_contents() : web_contents();
171
172 // By default all accessors should return false as there's no stream device.
173 EXPECT_FALSE((indicator()->*(param.accessor_method))(web_contents()));
174 std::unique_ptr<content::MediaStreamUI> ui =
175 indicator()->RegisterMediaStream(source, {CreateFakeDevice(param)});
176
177 // Make sure that the observer gets called and that the corresponding accessor
178 // gets called when |OnStarted| is called.
179 (observer()->*(param.observer_method))(source, true);
180 ui->OnStarted(base::OnceClosure(), content::MediaStreamUI::SourceCallback(),
181 /*label=*/std::string(), /*screen_capture_ids=*/{},
182 content::MediaStreamUI::StateChangeCallback());
183 EXPECT_TRUE((indicator()->*(param.accessor_method))(web_contents()));
184 ::testing::Mock::VerifyAndClear(observer());
185
186 // Removing the stream device should cause the observer to be notified that
187 // the observed property is now set to false.
188 (observer()->*(param.observer_method))(source, false);
189 ui.reset();
190 EXPECT_FALSE((indicator()->*(param.accessor_method))(web_contents()));
191 ::testing::Mock::VerifyAndClear(observer());
192 }
193
TEST_P(MediaStreamCaptureIndicatorObserverMethodTest,CloseActiveWebContents)194 TEST_P(MediaStreamCaptureIndicatorObserverMethodTest, CloseActiveWebContents) {
195 const ObserverMethodTestParam& param = std::get<0>(GetParam());
196 bool is_portal = std::get<1>(GetParam());
197 content::WebContents* source = is_portal ? portal_contents() : web_contents();
198
199 // Create and start the fake stream device.
200 std::unique_ptr<content::MediaStreamUI> ui =
201 indicator()->RegisterMediaStream(source, {CreateFakeDevice(param)});
202 (observer()->*(param.observer_method))(source, true);
203 ui->OnStarted(base::OnceClosure(), content::MediaStreamUI::SourceCallback(),
204 /*label=*/std::string(), /*screen_capture_ids=*/{},
205 content::MediaStreamUI::StateChangeCallback());
206 ::testing::Mock::VerifyAndClear(observer());
207
208 // Deleting the WebContents should cause the observer to be notified that the
209 // observed property is now set to false.
210 (observer()->*(param.observer_method))(source, false);
211 DeleteContents();
212 ::testing::Mock::VerifyAndClear(observer());
213 }
214
215 INSTANTIATE_TEST_SUITE_P(
216 All,
217 MediaStreamCaptureIndicatorObserverMethodTest,
218 testing::Combine(testing::ValuesIn(kObserverMethodTestParams),
219 testing::Bool()));
220