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.
5 #include "base/run_loop.h"
6 #include "ui/base/hit_test.h"
7 #include "ui/views/bubble/bubble_delegate.h"
8 #include "ui/views/bubble/bubble_frame_view.h"
9 #include "ui/views/test/test_widget_observer.h"
10 #include "ui/views/test/views_test_base.h"
11 #include "ui/views/widget/widget.h"
12 #include "ui/views/widget/widget_observer.h"
15 #include "ui/aura/env.h"
22 class TestBubbleDelegateView : public BubbleDelegateView {
24 TestBubbleDelegateView(View* anchor_view)
25 : BubbleDelegateView(anchor_view, BubbleBorder::TOP_LEFT),
27 view_->set_focusable(true);
30 virtual ~TestBubbleDelegateView() {}
32 void SetAnchorRectForTest(gfx::Rect rect) {
33 set_anchor_rect(rect);
36 void SetAnchorViewForTest(View* view) {
40 // BubbleDelegateView overrides:
41 virtual View* GetInitiallyFocusedView() OVERRIDE { return view_; }
42 virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); }
43 virtual int GetFadeDuration() OVERRIDE { return 1; }
48 DISALLOW_COPY_AND_ASSIGN(TestBubbleDelegateView);
51 class BubbleDelegateTest : public ViewsTestBase {
53 BubbleDelegateTest() {}
54 virtual ~BubbleDelegateTest() {}
56 // Creates a test widget that owns its native widget.
57 Widget* CreateTestWidget() {
58 Widget* widget = new Widget();
59 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
60 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
66 DISALLOW_COPY_AND_ASSIGN(BubbleDelegateTest);
71 TEST_F(BubbleDelegateTest, CreateDelegate) {
72 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
73 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
74 anchor_widget->GetContentsView(), BubbleBorder::NONE);
75 bubble_delegate->set_color(SK_ColorGREEN);
76 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
77 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
78 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
79 test::TestWidgetObserver bubble_observer(bubble_widget);
80 bubble_widget->Show();
82 BubbleBorder* border = bubble_delegate->GetBubbleFrameView()->bubble_border();
83 EXPECT_EQ(bubble_delegate->arrow(), border->arrow());
84 EXPECT_EQ(bubble_delegate->color(), border->background_color());
86 EXPECT_FALSE(bubble_observer.widget_closed());
87 bubble_widget->CloseNow();
88 EXPECT_TRUE(bubble_observer.widget_closed());
91 TEST_F(BubbleDelegateTest, CloseAnchorWidget) {
92 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
93 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
94 anchor_widget->GetContentsView(), BubbleBorder::NONE);
95 // Preventing close on deactivate should not prevent closing with the anchor.
96 bubble_delegate->set_close_on_deactivate(false);
97 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
98 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
99 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
100 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
101 test::TestWidgetObserver bubble_observer(bubble_widget);
102 EXPECT_FALSE(bubble_observer.widget_closed());
104 bubble_widget->Show();
105 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
106 EXPECT_FALSE(bubble_observer.widget_closed());
108 #if defined(USE_AURA)
109 // TODO(msw): Remove activation hack to prevent bookkeeping errors in:
110 // aura::test::TestActivationClient::OnWindowDestroyed().
111 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget());
112 EXPECT_FALSE(bubble_observer.widget_closed());
115 // Ensure that closing the anchor widget also closes the bubble itself.
116 anchor_widget->CloseNow();
117 EXPECT_TRUE(bubble_observer.widget_closed());
120 // This test checks that the bubble delegate is capable to handle an early
121 // destruction of the used anchor view. (Animations and delayed closure of the
122 // bubble will call upon the anchor view to get its location).
123 TEST_F(BubbleDelegateTest, CloseAnchorViewTest) {
124 // Create an anchor widget and add a view to be used as an anchor view.
125 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
126 scoped_ptr<View> anchor_view(new View());
127 anchor_widget->GetContentsView()->AddChildView(anchor_view.get());
128 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView(
130 // Prevent flakes by avoiding closing on activation changes.
131 bubble_delegate->set_close_on_deactivate(false);
132 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
134 // Check that the anchor view is correct and set up an anchor view rect.
135 // Make sure that this rect will get ignored (as long as the anchor view is
137 EXPECT_EQ(anchor_view, bubble_delegate->GetAnchorView());
138 const gfx::Rect set_anchor_rect = gfx::Rect(10, 10, 100, 100);
139 bubble_delegate->SetAnchorRectForTest(set_anchor_rect);
140 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect();
141 EXPECT_NE(view_rect.ToString(), set_anchor_rect.ToString());
143 // Create the bubble.
144 bubble_widget->Show();
145 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
147 // Remove now the anchor view and make sure that the original found rect
148 // is still kept, so that the bubble does not jump when the view gets deleted.
149 anchor_widget->GetContentsView()->RemoveChildView(anchor_view.get());
151 EXPECT_EQ(NULL, bubble_delegate->GetAnchorView());
152 EXPECT_EQ(view_rect.ToString(), bubble_delegate->GetAnchorRect().ToString());
155 // Testing that a move of the anchor view will lead to new bubble locations.
156 TEST_F(BubbleDelegateTest, TestAnchorRectMovesWithViewTest) {
157 // Create an anchor widget and add a view to be used as anchor view.
158 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
159 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView(
160 anchor_widget->GetContentsView());
161 BubbleDelegateView::CreateBubble(bubble_delegate);
163 anchor_widget->GetContentsView()->SetBounds(10, 10, 100, 100);
164 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect();
166 anchor_widget->GetContentsView()->SetBounds(20, 10, 100, 100);
167 const gfx::Rect view_rect_2 = bubble_delegate->GetAnchorRect();
168 EXPECT_NE(view_rect.ToString(), view_rect_2.ToString());
171 TEST_F(BubbleDelegateTest, ResetAnchorWidget) {
172 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
173 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
174 anchor_widget->GetContentsView(), BubbleBorder::NONE);
176 // Make sure the bubble widget is parented to a widget other than the anchor
177 // widget so that closing the anchor widget does not close the bubble widget.
178 scoped_ptr<Widget> parent_widget(CreateTestWidget());
179 bubble_delegate->set_parent_window(parent_widget->GetNativeView());
180 // Preventing close on deactivate should not prevent closing with the parent.
181 bubble_delegate->set_close_on_deactivate(false);
182 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
183 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
184 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
185 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
186 test::TestWidgetObserver bubble_observer(bubble_widget);
187 EXPECT_FALSE(bubble_observer.widget_closed());
189 // Showing and hiding the bubble widget should have no effect on its anchor.
190 bubble_widget->Show();
191 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
192 bubble_widget->Hide();
193 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
195 // Ensure that closing the anchor widget clears the bubble's reference to that
196 // anchor widget, but the bubble itself does not close.
197 anchor_widget->CloseNow();
198 EXPECT_NE(anchor_widget, bubble_delegate->anchor_widget());
199 EXPECT_FALSE(bubble_observer.widget_closed());
201 #if defined(USE_AURA)
202 // TODO(msw): Remove activation hack to prevent bookkeeping errors in:
203 // aura::test::TestActivationClient::OnWindowDestroyed().
204 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget());
205 EXPECT_FALSE(bubble_observer.widget_closed());
208 // Ensure that closing the parent widget also closes the bubble itself.
209 parent_widget->CloseNow();
210 EXPECT_TRUE(bubble_observer.widget_closed());
213 TEST_F(BubbleDelegateTest, InitiallyFocusedView) {
214 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
215 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
216 anchor_widget->GetContentsView(), BubbleBorder::NONE);
217 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
218 EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(),
219 bubble_widget->GetFocusManager()->GetFocusedView());
220 bubble_widget->CloseNow();
223 TEST_F(BubbleDelegateTest, NonClientHitTest) {
224 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
225 TestBubbleDelegateView* bubble_delegate =
226 new TestBubbleDelegateView(anchor_widget->GetContentsView());
227 BubbleDelegateView::CreateBubble(bubble_delegate);
228 BubbleFrameView* frame = bubble_delegate->GetBubbleFrameView();
229 const int border = frame->bubble_border()->GetBorderThickness();
235 { border, HTNOWHERE },
236 { border + 50, HTCLIENT },
240 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
241 gfx::Point point(cases[i].point, cases[i].point);
242 EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
243 << " with border: " << border << ", at point " << cases[i].point;
247 // This class provides functionality to verify that the BubbleView shows up
248 // when we call BubbleDelegateView::StartFade(true) and is destroyed when we
249 // call BubbleDelegateView::StartFade(false).
250 class BubbleWidgetClosingTest : public BubbleDelegateTest,
251 public views::WidgetObserver {
253 BubbleWidgetClosingTest() : bubble_destroyed_(false) {
254 #if defined(USE_AURA)
255 aura::Env::CreateInstance();
256 loop_.set_dispatcher(aura::Env::GetInstance()->GetDispatcher());
260 virtual ~BubbleWidgetClosingTest() {}
262 void Observe(views::Widget* widget) {
263 widget->AddObserver(this);
266 // views::WidgetObserver overrides.
267 virtual void OnWidgetDestroyed(Widget* widget) OVERRIDE {
268 bubble_destroyed_ = true;
269 widget->RemoveObserver(this);
273 bool bubble_destroyed() const { return bubble_destroyed_; }
275 void RunNestedLoop() {
280 bool bubble_destroyed_;
283 DISALLOW_COPY_AND_ASSIGN(BubbleWidgetClosingTest);
286 TEST_F(BubbleWidgetClosingTest, TestBubbleVisibilityAndClose) {
287 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
288 TestBubbleDelegateView* bubble_delegate =
289 new TestBubbleDelegateView(anchor_widget->GetContentsView());
290 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
291 EXPECT_FALSE(bubble_widget->IsVisible());
293 bubble_delegate->StartFade(true);
294 EXPECT_TRUE(bubble_widget->IsVisible());
296 EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(),
297 bubble_widget->GetFocusManager()->GetFocusedView());
299 Observe(bubble_widget);
301 bubble_delegate->StartFade(false);
303 EXPECT_TRUE(bubble_destroyed());