| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/status_bubble_views.h" |
| |
| #include <utility> |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/views/status_bubble_views_browsertest_mac.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "content/public/test/browser_test.h" |
| #include "ui/gfx/animation/animation.h" |
| #include "ui/views/widget/widget.h" |
| |
| class StatusBubbleViewsTest : public InProcessBrowserTest { |
| public: |
| StatusBubbleViews* GetBubble() { |
| std::vector<StatusBubble*> status_bubbles = |
| browser()->window()->GetStatusBubbles(); |
| if (status_bubbles.size() > 0) { |
| return static_cast<StatusBubbleViews*>(status_bubbles.front()); |
| } |
| return nullptr; |
| } |
| views::Widget* GetWidget() { return GetBubble()->popup(); } |
| bool IsDestroyPopupTimerRunning() { |
| return GetBubble()->IsDestroyPopupTimerRunningForTest(); |
| } |
| gfx::Animation* GetShowHideAnimationForTesting() { |
| return GetBubble()->GetShowHideAnimationForTest(); |
| } |
| void SetTaskRunners( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| scoped_refptr<base::SequencedTaskRunner> best_effort_task_runner) { |
| ASSERT_FALSE(orig_task_runner_); |
| ASSERT_FALSE(orig_best_effort_task_runner_); |
| orig_task_runner_ = std::exchange(GetBubble()->task_runner_, task_runner); |
| orig_best_effort_task_runner_ = std::exchange( |
| GetBubble()->best_effort_task_runner_, best_effort_task_runner); |
| } |
| void ResetTaskRunners() { |
| ASSERT_TRUE(orig_task_runner_); |
| ASSERT_TRUE(orig_best_effort_task_runner_); |
| GetBubble()->task_runner_ = std::exchange(orig_task_runner_, nullptr); |
| GetBubble()->best_effort_task_runner_ = |
| std::exchange(orig_best_effort_task_runner_, nullptr); |
| } |
| |
| private: |
| scoped_refptr<base::SequencedTaskRunner> orig_task_runner_; |
| scoped_refptr<base::SequencedTaskRunner> orig_best_effort_task_runner_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(StatusBubbleViewsTest, WidgetLifetime) { |
| auto task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| auto best_effort_task_runner = |
| base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| SetTaskRunners(task_runner, best_effort_task_runner); |
| |
| // The widget does not exist until it needs to be shown. |
| StatusBubble* bubble = GetBubble(); |
| ASSERT_TRUE(bubble); |
| EXPECT_FALSE(GetWidget()); |
| |
| // Setting status text shows the widget. |
| bubble->SetStatus(u"test"); |
| views::Widget* widget = GetWidget(); |
| ASSERT_TRUE(widget); |
| EXPECT_TRUE(widget->IsVisible()); |
| |
| // Changing status text keeps the widget visible. |
| bubble->SetStatus(u"foo"); |
| EXPECT_TRUE(widget->IsVisible()); |
| |
| // Setting the URL keeps the widget visible. |
| bubble->SetURL(GURL("http://www.foo.com")); |
| EXPECT_TRUE(widget->IsVisible()); |
| |
| #if !BUILDFLAG(IS_MAC) |
| // Clearing the URL and status closes the widget on platforms other than Mac. |
| EXPECT_FALSE(IsDestroyPopupTimerRunning()); |
| bubble->SetStatus(std::u16string()); |
| bubble->SetURL(GURL()); |
| // The widget is not hidden immediately, instead a task is scheduled. Run that |
| // now. |
| task_runner->RunPendingTasks(); |
| // After the task, a timer is created that animates hidden. Advance that. |
| ASSERT_TRUE(GetShowHideAnimationForTesting()); |
| // Advance well past the time for the animation to ensure it completes. |
| static_cast<gfx::AnimationContainerElement*>(GetShowHideAnimationForTesting()) |
| ->Step(base::TimeTicks::Now() + base::Minutes(1)); |
| // Widget should still exist. |
| ASSERT_TRUE(GetWidget()); |
| EXPECT_FALSE(widget->IsVisible()); |
| EXPECT_TRUE(IsDestroyPopupTimerRunning()); |
| |
| // Run until idle, which should trigger deleting the widget. |
| while (task_runner->HasPendingTask() || |
| best_effort_task_runner->HasPendingTask()) { |
| task_runner->RunPendingTasks(); |
| best_effort_task_runner->RunPendingTasks(); |
| } |
| EXPECT_FALSE(IsDestroyPopupTimerRunning()); |
| EXPECT_FALSE(GetWidget()); |
| #endif |
| ResetTaskRunners(); |
| } |
| |
| // Mac does not delete the widget after a delay, so this test only runs on |
| // non-mac platforms. |
| #if !BUILDFLAG(IS_MAC) |
| IN_PROC_BROWSER_TEST_F(StatusBubbleViewsTest, ShowHideDestroyShow) { |
| auto task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| auto best_effort_task_runner = |
| base::MakeRefCounted<base::TestSimpleTaskRunner>(); |
| SetTaskRunners(task_runner, best_effort_task_runner); |
| |
| // The widget does not exist until it needs to be shown. |
| StatusBubble* bubble = GetBubble(); |
| ASSERT_TRUE(bubble); |
| |
| // Setting status text shows the widget. |
| bubble->SetStatus(u"test"); |
| views::Widget* widget = GetWidget(); |
| ASSERT_TRUE(widget); |
| EXPECT_TRUE(widget->IsVisible()); |
| |
| bubble->SetStatus(std::u16string()); |
| // The widget is not hidden immediately, instead a task is scheduled. Run that |
| // now. |
| task_runner->RunPendingTasks(); |
| // After the task, a timer is created that animates hidden. Advance that. |
| ASSERT_TRUE(GetShowHideAnimationForTesting()); |
| // Advance well past the time for the animation to ensure it completes. |
| static_cast<gfx::AnimationContainerElement*>(GetShowHideAnimationForTesting()) |
| ->Step(base::TimeTicks::Now() + base::Minutes(1)); |
| // Widget should still exist. |
| ASSERT_TRUE(GetWidget()); |
| EXPECT_FALSE(widget->IsVisible()); |
| EXPECT_TRUE(IsDestroyPopupTimerRunning()); |
| |
| // Run until idle, which should trigger deleting the widget. |
| while (task_runner->HasPendingTask() || |
| best_effort_task_runner->HasPendingTask()) { |
| task_runner->RunPendingTasks(); |
| best_effort_task_runner->RunPendingTasks(); |
| } |
| EXPECT_FALSE(IsDestroyPopupTimerRunning()); |
| EXPECT_FALSE(GetWidget()); |
| |
| // Setting status text shows the widget. |
| bubble->SetStatus(u"test"); |
| widget = GetWidget(); |
| ASSERT_TRUE(widget); |
| EXPECT_TRUE(widget->IsVisible()); |
| |
| ResetTaskRunners(); |
| } |
| #endif |