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