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