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/ui/views/global_media_controls/media_dialog_view.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/run_loop.h"
9 #include "base/test/scoped_feature_list.h"
10 #include "build/build_config.h"
11 #include "chrome/browser/media/router/chrome_media_router_factory.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h"
14 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/global_media_controls/media_dialog_view_observer.h"
16 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
17 #include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
18 #include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
19 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
20 #include "chrome/browser/ui/views/user_education/new_badge_label.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/test/base/in_process_browser_test.h"
23 #include "chrome/test/base/interactive_test_utils.h"
24 #include "chrome/test/base/ui_test_utils.h"
25 #include "components/media_message_center/media_notification_view_impl.h"
26 #include "components/media_router/browser/presentation/web_contents_presentation_manager.h"
27 #include "components/media_router/browser/test/mock_media_router.h"
28 #include "content/public/browser/presentation_request.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/test/browser_test.h"
31 #include "content/public/test/media_start_stop_observer.h"
32 #include "media/base/media_switches.h"
33 #include "services/media_session/public/mojom/media_session.mojom.h"
34 #include "ui/views/controls/button/image_button.h"
35 #include "ui/views/controls/button/toggle_button.h"
36 #include "ui/views/view_utils.h"
37
38 using media_session::mojom::MediaSessionAction;
39
40 namespace {
41
42 class MediaToolbarButtonWatcher : public MediaToolbarButtonObserver,
43 public MediaDialogViewObserver {
44 public:
MediaToolbarButtonWatcher(MediaToolbarButtonView * button)45 explicit MediaToolbarButtonWatcher(MediaToolbarButtonView* button)
46 : button_(button) {
47 button_->AddObserver(this);
48 }
49
~MediaToolbarButtonWatcher()50 ~MediaToolbarButtonWatcher() override {
51 button_->RemoveObserver(this);
52 if (observed_dialog_ &&
53 observed_dialog_ == MediaDialogView::GetDialogViewForTesting()) {
54 observed_dialog_->RemoveObserver(this);
55 }
56 }
57
58 // MediaDialogViewObserver implementation.
OnMediaSessionShown()59 void OnMediaSessionShown() override { CheckDialogForText(); }
60
OnMediaSessionHidden()61 void OnMediaSessionHidden() override {}
62
OnMediaSessionMetadataUpdated()63 void OnMediaSessionMetadataUpdated() override { CheckDialogForText(); }
64
OnMediaSessionActionsChanged()65 void OnMediaSessionActionsChanged() override {
66 CheckPictureInPictureButton();
67 }
68
69 // MediaToolbarButtonObserver implementation.
OnMediaDialogOpened()70 void OnMediaDialogOpened() override {
71 waiting_for_dialog_opened_ = false;
72 MaybeStopWaiting();
73 }
74
OnMediaButtonShown()75 void OnMediaButtonShown() override {
76 waiting_for_button_shown_ = false;
77 MaybeStopWaiting();
78 }
79
OnMediaButtonHidden()80 void OnMediaButtonHidden() override {}
OnMediaButtonEnabled()81 void OnMediaButtonEnabled() override {}
OnMediaButtonDisabled()82 void OnMediaButtonDisabled() override {}
83
WaitForDialogOpened()84 void WaitForDialogOpened() {
85 if (MediaDialogView::IsShowing())
86 return;
87 waiting_for_dialog_opened_ = true;
88 Wait();
89 }
90
WaitForButtonShown()91 void WaitForButtonShown() {
92 if (button_->GetVisible())
93 return;
94 waiting_for_button_shown_ = true;
95 Wait();
96 }
97
WaitForDialogToContainText(const base::string16 & text)98 void WaitForDialogToContainText(const base::string16& text) {
99 if (DialogContainsText(text))
100 return;
101
102 waiting_for_dialog_to_contain_text_ = true;
103 expected_text_ = text;
104 observed_dialog_ = MediaDialogView::GetDialogViewForTesting();
105 observed_dialog_->AddObserver(this);
106 Wait();
107 }
108
WaitForNotificationCount(int count)109 void WaitForNotificationCount(int count) {
110 if (GetNotificationCount() == count)
111 return;
112
113 waiting_for_notification_count_ = true;
114 expected_notification_count_ = count;
115 observed_dialog_ = MediaDialogView::GetDialogViewForTesting();
116 observed_dialog_->AddObserver(this);
117 Wait();
118 }
119
WaitForPictureInPictureButtonVisibility(bool visible)120 void WaitForPictureInPictureButtonVisibility(bool visible) {
121 if (CheckPictureInPictureButtonVisibility(visible))
122 return;
123
124 waiting_for_pip_visibility_changed_ = true;
125 expected_pip_visibility_ = visible;
126 observed_dialog_ = MediaDialogView::GetDialogViewForTesting();
127 observed_dialog_->AddObserver(this);
128 Wait();
129 }
130
131 private:
CheckDialogForText()132 void CheckDialogForText() {
133 if (!waiting_for_dialog_to_contain_text_)
134 return;
135
136 if (!DialogContainsText(expected_text_))
137 return;
138
139 waiting_for_dialog_to_contain_text_ = false;
140 MaybeStopWaiting();
141 }
142
CheckNotificationCount()143 void CheckNotificationCount() {
144 if (!waiting_for_notification_count_)
145 return;
146
147 if (GetNotificationCount() != expected_notification_count_)
148 return;
149
150 waiting_for_notification_count_ = false;
151 MaybeStopWaiting();
152 }
153
CheckPictureInPictureButton()154 void CheckPictureInPictureButton() {
155 if (!waiting_for_pip_visibility_changed_)
156 return;
157
158 if (!CheckPictureInPictureButtonVisibility(expected_pip_visibility_))
159 return;
160
161 waiting_for_pip_visibility_changed_ = false;
162 MaybeStopWaiting();
163 }
164
MaybeStopWaiting()165 void MaybeStopWaiting() {
166 if (!run_loop_)
167 return;
168
169 if (!waiting_for_dialog_opened_ && !waiting_for_button_shown_ &&
170 !waiting_for_dialog_to_contain_text_ &&
171 !waiting_for_notification_count_ &&
172 !waiting_for_pip_visibility_changed_) {
173 run_loop_->Quit();
174 }
175 }
176
Wait()177 void Wait() {
178 ASSERT_EQ(nullptr, run_loop_.get());
179 run_loop_ = std::make_unique<base::RunLoop>();
180 run_loop_->Run();
181 }
182
183 // Checks the title and artist of each notification in the dialog to see if
184 // |text| is contained anywhere in the dialog.
DialogContainsText(const base::string16 & text)185 bool DialogContainsText(const base::string16& text) {
186 for (const auto& notification_pair :
187 MediaDialogView::GetDialogViewForTesting()
188 ->GetNotificationsForTesting()) {
189 const media_message_center::MediaNotificationViewImpl* view =
190 notification_pair.second->view_for_testing();
191 if (view->title_label_for_testing()->GetText().find(text) !=
192 std::string::npos ||
193 view->artist_label_for_testing()->GetText().find(text) !=
194 std::string::npos ||
195 view->GetSourceTitleForTesting().find(text) != std::string::npos) {
196 return true;
197 }
198 }
199 return false;
200 }
201
CheckPictureInPictureButtonVisibility(bool visible)202 bool CheckPictureInPictureButtonVisibility(bool visible) {
203 const auto notification_pair = MediaDialogView::GetDialogViewForTesting()
204 ->GetNotificationsForTesting()
205 .begin();
206 const media_message_center::MediaNotificationViewImpl* view =
207 notification_pair->second->view_for_testing();
208
209 return view->picture_in_picture_button_for_testing()->GetVisible() ==
210 visible;
211 }
212
GetNotificationCount()213 int GetNotificationCount() {
214 return MediaDialogView::GetDialogViewForTesting()
215 ->GetNotificationsForTesting()
216 .size();
217 }
218
219 MediaToolbarButtonView* const button_;
220 std::unique_ptr<base::RunLoop> run_loop_;
221
222 bool waiting_for_dialog_opened_ = false;
223 bool waiting_for_button_shown_ = false;
224 bool waiting_for_notification_count_ = false;
225 bool waiting_for_pip_visibility_changed_ = false;
226
227 MediaDialogView* observed_dialog_ = nullptr;
228 bool waiting_for_dialog_to_contain_text_ = false;
229 base::string16 expected_text_;
230 int expected_notification_count_ = 0;
231 bool expected_pip_visibility_ = false;
232
233 DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonWatcher);
234 };
235
236 class TestWebContentsPresentationManager
237 : public media_router::WebContentsPresentationManager {
238 public:
NotifyMediaRoutesChanged(const std::vector<media_router::MediaRoute> & routes)239 void NotifyMediaRoutesChanged(
240 const std::vector<media_router::MediaRoute>& routes) {
241 for (auto& observer : observers_) {
242 observer.OnMediaRoutesChanged(routes);
243 }
244 }
245
AddObserver(Observer * observer)246 void AddObserver(Observer* observer) override {
247 observers_.AddObserver(observer);
248 }
249
RemoveObserver(Observer * observer)250 void RemoveObserver(Observer* observer) override {
251 observers_.RemoveObserver(observer);
252 }
253
254 MOCK_CONST_METHOD0(HasDefaultPresentationRequest, bool());
255 MOCK_CONST_METHOD0(GetDefaultPresentationRequest,
256 const content::PresentationRequest&());
257
OnPresentationResponse(const content::PresentationRequest & presentation_request,media_router::mojom::RoutePresentationConnectionPtr connection,const media_router::RouteRequestResult & result)258 void OnPresentationResponse(
259 const content::PresentationRequest& presentation_request,
260 media_router::mojom::RoutePresentationConnectionPtr connection,
261 const media_router::RouteRequestResult& result) override {}
262
GetWeakPtr()263 base::WeakPtr<WebContentsPresentationManager> GetWeakPtr() override {
264 return weak_factory_.GetWeakPtr();
265 }
266
267 private:
268 base::ObserverList<Observer> observers_;
269 base::WeakPtrFactory<TestWebContentsPresentationManager> weak_factory_{this};
270 };
271
272 class TestMediaRouter : public media_router::MockMediaRouter {
273 public:
Create(content::BrowserContext * context)274 static std::unique_ptr<KeyedService> Create(
275 content::BrowserContext* context) {
276 return std::make_unique<TestMediaRouter>();
277 }
278
GetLogger()279 media_router::LoggerImpl* GetLogger() override {
280 if (!logger_)
281 logger_ = std::make_unique<media_router::LoggerImpl>();
282 return logger_.get();
283 }
284
RegisterMediaRoutesObserver(media_router::MediaRoutesObserver * observer)285 void RegisterMediaRoutesObserver(
286 media_router::MediaRoutesObserver* observer) override {
287 routes_observers_.push_back(observer);
288 }
289
UnregisterMediaRoutesObserver(media_router::MediaRoutesObserver * observer)290 void UnregisterMediaRoutesObserver(
291 media_router::MediaRoutesObserver* observer) override {
292 base::Erase(routes_observers_, observer);
293 }
294
NotifyMediaRoutesChanged(const std::vector<media_router::MediaRoute> & routes)295 void NotifyMediaRoutesChanged(
296 const std::vector<media_router::MediaRoute>& routes) {
297 for (auto* observer : routes_observers_)
298 observer->OnRoutesUpdated(routes, {});
299 }
300
301 private:
302 std::vector<media_router::MediaRoutesObserver*> routes_observers_;
303 std::unique_ptr<media_router::LoggerImpl> logger_;
304 };
305
306 } // anonymous namespace
307
308 class MediaDialogViewBrowserTest : public InProcessBrowserTest {
309 public:
310 MediaDialogViewBrowserTest() = default;
311 ~MediaDialogViewBrowserTest() override = default;
312
SetUpCommandLine(base::CommandLine * command_line)313 void SetUpCommandLine(base::CommandLine* command_line) override {
314 command_line->AppendSwitchASCII(
315 switches::kAutoplayPolicy,
316 switches::autoplay::kNoUserGestureRequiredPolicy);
317 }
318
SetUp()319 void SetUp() override {
320 feature_list_.InitWithFeatures(
321 {media::kGlobalMediaControls, media::kGlobalMediaControlsForCast,
322 media::kLiveCaption},
323 {});
324
325 presentation_manager_ =
326 std::make_unique<TestWebContentsPresentationManager>();
327 media_router::WebContentsPresentationManager::SetTestInstance(
328 presentation_manager_.get());
329
330 InProcessBrowserTest::SetUp();
331 }
332
TearDown()333 void TearDown() override {
334 InProcessBrowserTest::TearDown();
335 media_router::WebContentsPresentationManager::SetTestInstance(nullptr);
336 }
337
SetUpInProcessBrowserTestFixture()338 void SetUpInProcessBrowserTestFixture() override {
339 subscription_ =
340 BrowserContextDependencyManager::GetInstance()
341 ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
342 &MediaDialogViewBrowserTest::OnWillCreateBrowserContextServices,
343 base::Unretained(this)));
344 }
345
OnWillCreateBrowserContextServices(content::BrowserContext * context)346 void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
347 media_router_ = static_cast<TestMediaRouter*>(
348 media_router::ChromeMediaRouterFactory::GetInstance()
349 ->SetTestingFactoryAndUse(
350 context, base::BindRepeating(&TestMediaRouter::Create)));
351 }
352
LayoutBrowser()353 void LayoutBrowser() {
354 BrowserView::GetBrowserViewForBrowser(browser())
355 ->GetWidget()
356 ->LayoutRootViewIfNecessary();
357 }
358
GetToolbarIcon()359 MediaToolbarButtonView* GetToolbarIcon() {
360 LayoutBrowser();
361 return BrowserView::GetBrowserViewForBrowser(browser())
362 ->toolbar()
363 ->media_button();
364 }
365
ClickToolbarIcon()366 void ClickToolbarIcon() { ClickButton(GetToolbarIcon()); }
367
IsToolbarIconVisible()368 bool IsToolbarIconVisible() { return GetToolbarIcon()->GetVisible(); }
369
WaitForVisibleToolbarIcon()370 void WaitForVisibleToolbarIcon() {
371 MediaToolbarButtonWatcher(GetToolbarIcon()).WaitForButtonShown();
372 }
373
OpenTestURL()374 void OpenTestURL() {
375 GURL url = ui_test_utils::GetTestUrl(
376 base::FilePath(FILE_PATH_LITERAL("media/session")),
377 base::FilePath(FILE_PATH_LITERAL("video-with-metadata.html")));
378 ui_test_utils::NavigateToURL(browser(), url);
379 }
380
OpenDifferentMetadataURLInNewTab()381 void OpenDifferentMetadataURLInNewTab() {
382 GURL url = ui_test_utils::GetTestUrl(
383 base::FilePath(FILE_PATH_LITERAL("media/session")),
384 base::FilePath(
385 FILE_PATH_LITERAL("video-with-different-metadata.html")));
386 ui_test_utils::NavigateToURLWithDisposition(
387 browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
388 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
389 }
390
StartPlayback()391 void StartPlayback() {
392 // The test HTML files used in these tests contain "play()" functions that
393 // play the video.
394 GetActiveWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
395 base::ASCIIToUTF16("play()"), base::NullCallback());
396 }
397
WaitForStart()398 void WaitForStart() {
399 content::MediaStartStopObserver observer(
400 GetActiveWebContents(), content::MediaStartStopObserver::Type::kStart);
401 observer.Wait();
402 }
403
WaitForStop()404 void WaitForStop() { WaitForStop(GetActiveWebContents()); }
405
WaitForStop(content::WebContents * web_contents)406 void WaitForStop(content::WebContents* web_contents) {
407 content::MediaStartStopObserver observer(
408 web_contents, content::MediaStartStopObserver::Type::kStop);
409 observer.Wait();
410 }
411
DisablePictureInPicture()412 void DisablePictureInPicture() {
413 GetActiveWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
414 base::ASCIIToUTF16("disablePictureInPicture()"), base::NullCallback());
415 }
416
EnablePictureInPicture()417 void EnablePictureInPicture() {
418 GetActiveWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
419 base::ASCIIToUTF16("enablePictureInPicture()"), base::NullCallback());
420 }
421
WaitForEnterPictureInPicture()422 void WaitForEnterPictureInPicture() {
423 content::MediaStartStopObserver observer(
424 GetActiveWebContents(),
425 content::MediaStartStopObserver::Type::kEnterPictureInPicture);
426 observer.Wait();
427 }
428
WaitForExitPictureInPicture()429 void WaitForExitPictureInPicture() {
430 content::MediaStartStopObserver observer(
431 GetActiveWebContents(),
432 content::MediaStartStopObserver::Type::kExitPictureInPicture);
433 observer.Wait();
434 }
435
WaitForDialogOpened()436 void WaitForDialogOpened() {
437 MediaToolbarButtonWatcher(GetToolbarIcon()).WaitForDialogOpened();
438 }
439
IsDialogVisible()440 bool IsDialogVisible() { return MediaDialogView::IsShowing(); }
441
WaitForDialogToContainText(const base::string16 & text)442 void WaitForDialogToContainText(const base::string16& text) {
443 MediaToolbarButtonWatcher(GetToolbarIcon())
444 .WaitForDialogToContainText(text);
445 }
446
WaitForNotificationCount(int count)447 void WaitForNotificationCount(int count) {
448 MediaToolbarButtonWatcher(GetToolbarIcon()).WaitForNotificationCount(count);
449 }
450
WaitForPictureInPictureButtonVisibility(bool visible)451 void WaitForPictureInPictureButtonVisibility(bool visible) {
452 MediaToolbarButtonWatcher(GetToolbarIcon())
453 .WaitForPictureInPictureButtonVisibility(visible);
454 }
455
ClickPauseButtonOnDialog()456 void ClickPauseButtonOnDialog() {
457 base::RunLoop().RunUntilIdle();
458 ASSERT_TRUE(MediaDialogView::IsShowing());
459 ClickButton(GetButtonForAction(MediaSessionAction::kPause));
460 }
461
ClickPlayButtonOnDialog()462 void ClickPlayButtonOnDialog() {
463 base::RunLoop().RunUntilIdle();
464 ASSERT_TRUE(MediaDialogView::IsShowing());
465 ClickButton(GetButtonForAction(MediaSessionAction::kPlay));
466 }
467
ClickEnterPictureInPictureButtonOnDialog()468 void ClickEnterPictureInPictureButtonOnDialog() {
469 base::RunLoop().RunUntilIdle();
470 ASSERT_TRUE(MediaDialogView::IsShowing());
471 ClickButton(GetButtonForAction(MediaSessionAction::kEnterPictureInPicture));
472 }
473
ClickExitPictureInPictureButtonOnDialog()474 void ClickExitPictureInPictureButtonOnDialog() {
475 base::RunLoop().RunUntilIdle();
476 ASSERT_TRUE(MediaDialogView::IsShowing());
477 ClickButton(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
478 }
479
ClickEnableLiveCaptionOnDialog()480 void ClickEnableLiveCaptionOnDialog() {
481 base::RunLoop().RunUntilIdle();
482 ASSERT_TRUE(MediaDialogView::IsShowing());
483 views::Button* live_caption_button = static_cast<views::Button*>(
484 MediaDialogView::GetDialogViewForTesting()->live_caption_button_);
485 ClickButton(live_caption_button);
486 }
487
ClickNotificationByTitle(const base::string16 & title)488 void ClickNotificationByTitle(const base::string16& title) {
489 ASSERT_TRUE(MediaDialogView::IsShowing());
490 MediaNotificationContainerImplView* notification =
491 GetNotificationByTitle(title);
492 ASSERT_NE(nullptr, notification);
493 ClickButton(notification);
494 }
495
GetActiveWebContents()496 content::WebContents* GetActiveWebContents() {
497 return browser()->tab_strip_model()->GetActiveWebContents();
498 }
499
IsPlayingSessionDisplayedFirst()500 bool IsPlayingSessionDisplayedFirst() {
501 bool seen_paused = false;
502 for (views::View* view : MediaDialogView::GetDialogViewForTesting()
503 ->GetListViewForTesting()
504 ->contents()
505 ->children()) {
506 MediaNotificationContainerImplView* notification =
507 static_cast<MediaNotificationContainerImplView*>(view);
508
509 if (seen_paused && notification->is_playing_for_testing())
510 return false;
511
512 if (!seen_paused && !notification->is_playing_for_testing())
513 seen_paused = true;
514 }
515
516 return true;
517 }
518
GetButtonForAction(MediaSessionAction action)519 views::ImageButton* GetButtonForAction(MediaSessionAction action) {
520 return GetButtonForAction(
521 MediaDialogView::GetDialogViewForTesting()->children().front(),
522 static_cast<int>(action));
523 }
524
525 // Returns true if |target| exists in |base|'s forward focus chain
ViewFollowsInFocusChain(views::View * base,views::View * target)526 bool ViewFollowsInFocusChain(views::View* base, views::View* target) {
527 for (views::View* cur = base; cur; cur = cur->GetNextFocusableView()) {
528 if (cur == target)
529 return true;
530 }
531 return false;
532 }
533
GetLiveCaptionTitleLabel()534 views::Label* GetLiveCaptionTitleLabel() {
535 return MediaDialogView::GetDialogViewForTesting()->live_caption_title_;
536 }
537
GetLiveCaptionTitleNewBadgeLabel()538 views::Label* GetLiveCaptionTitleNewBadgeLabel() {
539 return MediaDialogView::GetDialogViewForTesting()
540 ->live_caption_title_new_badge_;
541 }
542
OnSODAProgress(int progress)543 void OnSODAProgress(int progress) {
544 MediaDialogView::GetDialogViewForTesting()->OnSODAProgress(progress);
545 }
546
OnSODAInstalled()547 void OnSODAInstalled() {
548 MediaDialogView::GetDialogViewForTesting()->OnSODAInstalled();
549 }
550
551 protected:
552 std::unique_ptr<TestWebContentsPresentationManager> presentation_manager_;
553 TestMediaRouter* media_router_ = nullptr;
554
555 private:
ClickButton(views::Button * button)556 void ClickButton(views::Button* button) {
557 base::RunLoop closure_loop;
558 ui_test_utils::MoveMouseToCenterAndPress(
559 button, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
560 closure_loop.QuitClosure());
561 closure_loop.Run();
562 }
563
564 // Recursively tries to find a views::ImageButton for the given
565 // MediaSessionAction. This operates under the assumption that
566 // media_message_center::MediaNotificationViewImpl sets the tags of its action
567 // buttons to the MediaSessionAction value.
GetButtonForAction(views::View * view,int action)568 views::ImageButton* GetButtonForAction(views::View* view, int action) {
569 if (views::IsViewClass<views::ImageButton>(view)) {
570 views::ImageButton* image_button = static_cast<views::ImageButton*>(view);
571 if (image_button->tag() == action)
572 return image_button;
573 }
574
575 for (views::View* child : view->children()) {
576 views::ImageButton* result = GetButtonForAction(child, action);
577 if (result)
578 return result;
579 }
580
581 return nullptr;
582 }
583
584 // Finds a MediaNotificationContainerImplView by title.
GetNotificationByTitle(const base::string16 & title)585 MediaNotificationContainerImplView* GetNotificationByTitle(
586 const base::string16& title) {
587 for (const auto& notification_pair :
588 MediaDialogView::GetDialogViewForTesting()
589 ->GetNotificationsForTesting()) {
590 const media_message_center::MediaNotificationViewImpl* view =
591 notification_pair.second->view_for_testing();
592 if (view->title_label_for_testing()->GetText() == title)
593 return notification_pair.second;
594 }
595 return nullptr;
596 }
597
598 base::test::ScopedFeatureList feature_list_;
599 std::unique_ptr<
600 BrowserContextDependencyManager::CreateServicesCallbackList::Subscription>
601 subscription_;
602
603 DISALLOW_COPY_AND_ASSIGN(MediaDialogViewBrowserTest);
604 };
605
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,ShowsMetadataAndControlsMedia)606 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
607 ShowsMetadataAndControlsMedia) {
608 // The toolbar icon should not start visible.
609 EXPECT_FALSE(IsToolbarIconVisible());
610
611 // Opening a page with media that hasn't played yet should not make the
612 // toolbar icon visible.
613 OpenTestURL();
614 LayoutBrowser();
615 EXPECT_FALSE(IsToolbarIconVisible());
616
617 // Once playback starts, the icon should be visible, but the dialog should not
618 // appear if it hasn't been clicked.
619 StartPlayback();
620 WaitForStart();
621 WaitForVisibleToolbarIcon();
622 EXPECT_TRUE(IsToolbarIconVisible());
623 EXPECT_FALSE(IsDialogVisible());
624
625 // At this point, the toolbar icon has been set visible. Layout the
626 // browser to ensure it can be clicked.
627 LayoutBrowser();
628
629 // Clicking on the toolbar icon should open the dialog.
630 ClickToolbarIcon();
631 WaitForDialogOpened();
632 EXPECT_TRUE(IsDialogVisible());
633
634 // The dialog should contain the title and artist. These are taken from
635 // video-with-metadata.html.
636 WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
637 WaitForDialogToContainText(base::ASCIIToUTF16("Blender Foundation"));
638
639 // Clicking on the pause button in the dialog should pause the media on the
640 // page.
641 ClickPauseButtonOnDialog();
642 WaitForStop();
643
644 // Clicking on the play button in the dialog should play the media on the
645 // page.
646 ClickPlayButtonOnDialog();
647 WaitForStart();
648
649 // Clicking on the toolbar icon again should hide the dialog.
650 EXPECT_TRUE(IsDialogVisible());
651 ClickToolbarIcon();
652 EXPECT_FALSE(IsDialogVisible());
653 }
654
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,ShowsMetadataAndControlsMediaInRTL)655 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
656 ShowsMetadataAndControlsMediaInRTL) {
657 base::i18n::SetICUDefaultLocale("ar");
658 ASSERT_TRUE(base::i18n::IsRTL());
659
660 // The toolbar icon should not start visible.
661 EXPECT_FALSE(IsToolbarIconVisible());
662
663 // Opening a page with media that hasn't played yet should not make the
664 // toolbar icon visible.
665 OpenTestURL();
666 LayoutBrowser();
667 EXPECT_FALSE(IsToolbarIconVisible());
668
669 // Once playback starts, the icon should be visible, but the dialog should not
670 // appear if it hasn't been clicked.
671 StartPlayback();
672 WaitForStart();
673 WaitForVisibleToolbarIcon();
674 EXPECT_TRUE(IsToolbarIconVisible());
675 EXPECT_FALSE(IsDialogVisible());
676
677 // At this point, the toolbar icon has been set visible. Layout the
678 // browser to ensure it can be clicked.
679 LayoutBrowser();
680
681 // Clicking on the toolbar icon should open the dialog.
682 ClickToolbarIcon();
683 WaitForDialogOpened();
684 EXPECT_TRUE(IsDialogVisible());
685
686 // The view containing playback controls should not be mirrored.
687 EXPECT_FALSE(MediaDialogView::GetDialogViewForTesting()
688 ->GetNotificationsForTesting()
689 .begin()
690 ->second->view_for_testing()
691 ->playback_button_container_for_testing()
692 ->GetMirrored());
693
694 // The dialog should contain the title and artist. These are taken from
695 // video-with-metadata.html.
696 WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
697 WaitForDialogToContainText(base::ASCIIToUTF16("Blender Foundation"));
698
699 // Clicking on the pause button in the dialog should pause the media on the
700 // page.
701 ClickPauseButtonOnDialog();
702 WaitForStop();
703
704 // Clicking on the play button in the dialog should play the media on the
705 // page.
706 ClickPlayButtonOnDialog();
707 WaitForStart();
708
709 // In the RTL UI the picture in picture button should be to the left of the
710 // playback control buttons.
711 EXPECT_LT(
712 GetButtonForAction(MediaSessionAction::kEnterPictureInPicture)
713 ->GetMirroredX(),
714 GetButtonForAction(MediaSessionAction::kPlay)->parent()->GetMirroredX());
715
716 // In the RTL UI the focus order should be the same as it is in the LTR UI.
717 // That is the play/pause button logically proceeds the picture in picture
718 // button.
719 EXPECT_TRUE(ViewFollowsInFocusChain(
720 GetButtonForAction(MediaSessionAction::kPlay)->parent(),
721 GetButtonForAction(MediaSessionAction::kEnterPictureInPicture)));
722
723 // Clicking on the toolbar icon again should hide the dialog.
724 EXPECT_TRUE(IsDialogVisible());
725 ClickToolbarIcon();
726 EXPECT_FALSE(IsDialogVisible());
727 }
728
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,ShowsMultipleMediaSessions)729 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, ShowsMultipleMediaSessions) {
730 // Open a tab and play media.
731 OpenTestURL();
732 StartPlayback();
733 WaitForStart();
734
735 // Open another tab and play different media.
736 OpenDifferentMetadataURLInNewTab();
737 StartPlayback();
738 WaitForStart();
739
740 // Open the media dialog.
741 ClickToolbarIcon();
742 WaitForDialogOpened();
743 EXPECT_TRUE(IsDialogVisible());
744
745 // The dialog should show both media sessions.
746 WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
747 WaitForDialogToContainText(base::ASCIIToUTF16("Blender Foundation"));
748 WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
749 WaitForDialogToContainText(base::ASCIIToUTF16("Another Artist"));
750 }
751
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,ClickingOnNotificationGoesBackToTab)752 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
753 ClickingOnNotificationGoesBackToTab) {
754 // Open a tab and play media.
755 OpenTestURL();
756 StartPlayback();
757 WaitForStart();
758
759 // Pointer to the first tab.
760 content::WebContents* first_web_contents = GetActiveWebContents();
761
762 // Open another tab and play different media.
763 OpenDifferentMetadataURLInNewTab();
764 StartPlayback();
765 WaitForStart();
766
767 // Now the active web contents is the second tab.
768 content::WebContents* second_web_contents = GetActiveWebContents();
769 ASSERT_NE(first_web_contents, second_web_contents);
770
771 // Open the media dialog.
772 ClickToolbarIcon();
773 WaitForDialogOpened();
774 EXPECT_TRUE(IsDialogVisible());
775
776 // Wait for the dialog to be populated.
777 WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
778 WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
779
780 // The second tab should be the active tab.
781 EXPECT_EQ(second_web_contents, GetActiveWebContents());
782
783 // Clicking the first notification should make the first tab active.
784 ClickNotificationByTitle(base::ASCIIToUTF16("Big Buck Bunny"));
785 EXPECT_EQ(first_web_contents, GetActiveWebContents());
786 }
787
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,ShowsCastSession)788 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, ShowsCastSession) {
789 OpenTestURL();
790 StartPlayback();
791 WaitForStart();
792
793 const std::string route_description = "Casting: Big Buck Bunny";
794 const std::string sink_name = "My Sink";
795 media_router::MediaRoute route("id", media_router::MediaSource("source_id"),
796 "sink_id", route_description, true, true);
797 route.set_media_sink_name(sink_name);
798 route.set_controller_type(media_router::RouteControllerType::kGeneric);
799 media_router_->NotifyMediaRoutesChanged({route});
800 base::RunLoop().RunUntilIdle();
801 presentation_manager_->NotifyMediaRoutesChanged({route});
802
803 WaitForVisibleToolbarIcon();
804 ClickToolbarIcon();
805 WaitForDialogOpened();
806 WaitForDialogToContainText(
807 base::UTF8ToUTF16(route_description + " \xC2\xB7 " + sink_name));
808 WaitForNotificationCount(1);
809 }
810
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,PictureInPicture)811 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, PictureInPicture) {
812 // Open a tab and play media.
813 OpenTestURL();
814 StartPlayback();
815 WaitForStart();
816
817 // Open the media dialog.
818 WaitForVisibleToolbarIcon();
819 ClickToolbarIcon();
820 WaitForDialogOpened();
821 EXPECT_TRUE(IsDialogVisible());
822
823 ClickEnterPictureInPictureButtonOnDialog();
824 WaitForEnterPictureInPicture();
825
826 ClickExitPictureInPictureButtonOnDialog();
827 WaitForExitPictureInPicture();
828 }
829
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,PictureInPictureButtonVisibility)830 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
831 PictureInPictureButtonVisibility) {
832 // Open a tab and play media.
833 OpenTestURL();
834 StartPlayback();
835 WaitForStart();
836
837 // Open the media dialog.
838 WaitForVisibleToolbarIcon();
839 ClickToolbarIcon();
840 WaitForDialogOpened();
841 EXPECT_TRUE(IsDialogVisible());
842
843 DisablePictureInPicture();
844 WaitForPictureInPictureButtonVisibility(false);
845
846 EnablePictureInPicture();
847 WaitForPictureInPictureButtonVisibility(true);
848 }
849
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,PlayingSessionAlwaysDisplayFirst)850 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
851 PlayingSessionAlwaysDisplayFirst) {
852 OpenTestURL();
853 StartPlayback();
854 WaitForStart();
855
856 content::WebContents* first_web_contents = GetActiveWebContents();
857
858 OpenDifferentMetadataURLInNewTab();
859 StartPlayback();
860 WaitForStart();
861
862 WaitForVisibleToolbarIcon();
863 EXPECT_TRUE(IsToolbarIconVisible());
864
865 ClickToolbarIcon();
866 WaitForDialogOpened();
867 EXPECT_TRUE(IsDialogVisible());
868
869 // Pause the first session.
870 ClickPauseButtonOnDialog();
871 WaitForStop(first_web_contents);
872
873 // Reopen dialog.
874 ClickToolbarIcon();
875 EXPECT_FALSE(IsDialogVisible());
876 ClickToolbarIcon();
877 WaitForDialogOpened();
878 EXPECT_TRUE(IsDialogVisible());
879
880 EXPECT_TRUE(IsPlayingSessionDisplayedFirst());
881 }
882
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,LiveCaption)883 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, LiveCaption) {
884 // Open a tab and play media.
885 OpenTestURL();
886 StartPlayback();
887 WaitForStart();
888
889 // Open the media dialog.
890 WaitForVisibleToolbarIcon();
891 ClickToolbarIcon();
892 WaitForDialogOpened();
893 EXPECT_TRUE(IsDialogVisible());
894
895 // When media dialog opens and Live Caption is disabled, the New badge is
896 // visible and the regular title is not visible.
897 EXPECT_NE(GetLiveCaptionTitleNewBadgeLabel(), nullptr);
898 EXPECT_TRUE(GetLiveCaptionTitleNewBadgeLabel()->GetVisible());
899 EXPECT_FALSE(GetLiveCaptionTitleLabel()->GetVisible());
900
901 ClickEnableLiveCaptionOnDialog();
902 EXPECT_TRUE(
903 browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
904 // The New Badge disappears when Live Caption is enabled. The regular title
905 // appears.
906 EXPECT_FALSE(GetLiveCaptionTitleNewBadgeLabel()->GetVisible());
907 EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
908
909 ClickEnableLiveCaptionOnDialog();
910 EXPECT_FALSE(
911 browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
912 // The New Badge doesn't reappear after Live Caption is disabled again.
913 EXPECT_FALSE(GetLiveCaptionTitleNewBadgeLabel()->GetVisible());
914 EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
915
916 // Close dialog and enable live caption preference. Reopen dialog.
917 ClickToolbarIcon();
918 EXPECT_FALSE(IsDialogVisible());
919 browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled,
920 true);
921 ClickToolbarIcon();
922 WaitForDialogOpened();
923 EXPECT_TRUE(IsDialogVisible());
924 // When media dialog opens and Live Caption is enabled, the New badge is not
925 // created. The regular title is visible.
926 EXPECT_EQ(GetLiveCaptionTitleNewBadgeLabel(), nullptr);
927 EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
928
929 ClickEnableLiveCaptionOnDialog();
930 EXPECT_FALSE(
931 browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
932 // The New badge is still not created. The regular title is still visible.
933 EXPECT_EQ(GetLiveCaptionTitleNewBadgeLabel(), nullptr);
934 EXPECT_TRUE(GetLiveCaptionTitleLabel()->GetVisible());
935 }
936
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,LiveCaptionProgressUpdate)937 IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, LiveCaptionProgressUpdate) {
938 // Open a tab and play media.
939 OpenTestURL();
940 StartPlayback();
941 WaitForStart();
942
943 // Open the media dialog.
944 WaitForVisibleToolbarIcon();
945 ClickToolbarIcon();
946 WaitForDialogOpened();
947 EXPECT_TRUE(IsDialogVisible());
948
949 EXPECT_EQ("Live Caption (English only)",
950 base::UTF16ToUTF8(GetLiveCaptionTitleNewBadgeLabel()->GetText()));
951
952 ClickEnableLiveCaptionOnDialog();
953 OnSODAProgress(0);
954 EXPECT_EQ("Downloading… 0%",
955 base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
956
957 OnSODAProgress(12);
958 EXPECT_EQ("Downloading… 12%",
959 base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
960
961 OnSODAProgress(100);
962 EXPECT_EQ("Downloading… 100%",
963 base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
964
965 OnSODAInstalled();
966 EXPECT_EQ("Live Caption (English only)",
967 base::UTF16ToUTF8(GetLiveCaptionTitleLabel()->GetText()));
968 }
969