1 // Copyright (c) 2012 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 "ui/views/corewm/tooltip_controller.h"
6
7 #include <memory>
8
9 #include "ash/public/cpp/shell_window_ids.h"
10 #include "ash/shell.h"
11 #include "ash/test/ash_test_base.h"
12 #include "ash/wm/desks/desks_util.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_event_dispatcher.h"
18 #include "ui/events/test/event_generator.h"
19 #include "ui/gfx/font.h"
20 #include "ui/gfx/geometry/point.h"
21 #include "ui/views/corewm/tooltip_controller_test_helper.h"
22 #include "ui/views/view.h"
23 #include "ui/views/widget/widget.h"
24 #include "ui/wm/public/tooltip_client.h"
25
26 using views::corewm::TooltipController;
27 using views::corewm::test::TooltipTestView;
28 using views::corewm::test::TooltipControllerTestHelper;
29
30 // The tests in this file exercise bits of TooltipController that are hard to
31 // test outside of ash. Meaning these tests require the shell and related things
32 // to be installed.
33
34 namespace ash {
35
36 namespace {
37
CreateNewWidgetWithBoundsOn(int display,const gfx::Rect & bounds)38 views::Widget* CreateNewWidgetWithBoundsOn(int display,
39 const gfx::Rect& bounds) {
40 views::Widget* widget = new views::Widget;
41 views::Widget::InitParams params;
42 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
43 params.accept_events = true;
44 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
45 params.parent =
46 Shell::Get()->GetContainer(Shell::GetAllRootWindows().at(display),
47 desks_util::GetActiveDeskContainerId());
48 params.bounds = bounds;
49 widget->Init(std::move(params));
50 widget->Show();
51 return widget;
52 }
53
CreateNewWidgetOn(int display)54 views::Widget* CreateNewWidgetOn(int display) {
55 return CreateNewWidgetWithBoundsOn(display, gfx::Rect());
56 }
57
AddViewToWidgetAndResize(views::Widget * widget,views::View * view)58 void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
59 if (!widget->GetContentsView())
60 widget->SetContentsView(std::make_unique<views::View>());
61
62 views::View* contents_view = widget->GetContentsView();
63 contents_view->AddChildView(view);
64 view->SetBounds(contents_view->width(), 0, 100, 100);
65 gfx::Rect contents_view_bounds = contents_view->bounds();
66 contents_view_bounds.Union(view->bounds());
67 contents_view->SetBoundsRect(contents_view_bounds);
68 widget->SetBounds(gfx::Rect(widget->GetWindowBoundsInScreen().origin(),
69 contents_view_bounds.size()));
70 }
71
GetController()72 TooltipController* GetController() {
73 return static_cast<TooltipController*>(
74 ::wm::GetTooltipClient(Shell::GetPrimaryRootWindow()));
75 }
76
77 } // namespace
78
79 class TooltipControllerTest : public AshTestBase {
80 public:
81 TooltipControllerTest() = default;
82 ~TooltipControllerTest() override = default;
83
SetUp()84 void SetUp() override {
85 AshTestBase::SetUp();
86 helper_.reset(new TooltipControllerTestHelper(GetController()));
87 }
88
89 protected:
90 std::unique_ptr<TooltipControllerTestHelper> helper_;
91
92 private:
93 DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest);
94 };
95
TEST_F(TooltipControllerTest,NonNullTooltipClient)96 TEST_F(TooltipControllerTest, NonNullTooltipClient) {
97 EXPECT_TRUE(::wm::GetTooltipClient(Shell::GetPrimaryRootWindow()) != NULL);
98 EXPECT_EQ(base::string16(), helper_->GetTooltipText());
99 EXPECT_EQ(NULL, helper_->GetTooltipWindow());
100 EXPECT_FALSE(helper_->IsTooltipVisible());
101 }
102
TEST_F(TooltipControllerTest,HideTooltipWhenCursorHidden)103 TEST_F(TooltipControllerTest, HideTooltipWhenCursorHidden) {
104 std::unique_ptr<views::Widget> widget(CreateNewWidgetOn(0));
105 TooltipTestView* view = new TooltipTestView;
106 AddViewToWidgetAndResize(widget.get(), view);
107 view->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text"));
108 EXPECT_EQ(base::string16(), helper_->GetTooltipText());
109 EXPECT_EQ(NULL, helper_->GetTooltipWindow());
110
111 ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
112 generator.MoveMouseRelativeTo(widget->GetNativeView(),
113 view->bounds().CenterPoint());
114 base::string16 expected_tooltip = base::ASCIIToUTF16("Tooltip Text");
115
116 // Mouse event triggers tooltip update so it becomes visible.
117 EXPECT_TRUE(helper_->IsTooltipVisible());
118
119 // Disable mouse event which hides the cursor and check again.
120 Shell::Get()->cursor_manager()->DisableMouseEvents();
121 base::RunLoop().RunUntilIdle();
122 EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());
123 helper_->UpdateIfRequired();
124 EXPECT_FALSE(helper_->IsTooltipVisible());
125
126 // Enable mouse event which shows the cursor and re-check.
127 Shell::Get()->cursor_manager()->EnableMouseEvents();
128 base::RunLoop().RunUntilIdle();
129 EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
130 helper_->UpdateIfRequired();
131 EXPECT_TRUE(helper_->IsTooltipVisible());
132 }
133
TEST_F(TooltipControllerTest,TooltipsOnMultiDisplayShouldNotCrash)134 TEST_F(TooltipControllerTest, TooltipsOnMultiDisplayShouldNotCrash) {
135 UpdateDisplay("1000x600,600x400");
136 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
137 std::unique_ptr<views::Widget> widget1(
138 CreateNewWidgetWithBoundsOn(0, gfx::Rect(10, 10, 100, 100)));
139 TooltipTestView* view1 = new TooltipTestView;
140 AddViewToWidgetAndResize(widget1.get(), view1);
141 view1->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text for view 1"));
142 EXPECT_EQ(widget1->GetNativeView()->GetRootWindow(), root_windows[0]);
143
144 std::unique_ptr<views::Widget> widget2(
145 CreateNewWidgetWithBoundsOn(1, gfx::Rect(1200, 10, 100, 100)));
146 TooltipTestView* view2 = new TooltipTestView;
147 AddViewToWidgetAndResize(widget2.get(), view2);
148 view2->set_tooltip_text(base::ASCIIToUTF16("Tooltip Text for view 2"));
149 EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[1]);
150
151 // Show tooltip on second display.
152 ui::test::EventGenerator generator(root_windows[1]);
153 generator.MoveMouseRelativeTo(widget2->GetNativeView(),
154 view2->bounds().CenterPoint());
155 EXPECT_TRUE(helper_->IsTooltipVisible());
156
157 // Get rid of secondary display. This destroy's the tooltip's aura window. If
158 // we have handled this case, we will not crash in the following statement.
159 UpdateDisplay("1000x600");
160 EXPECT_FALSE(helper_->IsTooltipVisible());
161 EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[0]);
162
163 // The tooltip should create a new aura window for itself, so we should still
164 // be able to show tooltips on the primary display.
165 ui::test::EventGenerator generator1(root_windows[0]);
166 generator1.MoveMouseRelativeTo(widget1->GetNativeView(),
167 view1->bounds().CenterPoint());
168 EXPECT_TRUE(helper_->IsTooltipVisible());
169 }
170
171 } // namespace ash
172