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/aura/env.h"
7 #include "ui/base/hit_test.h"
8 #include "ui/views/bubble/bubble_delegate.h"
9 #include "ui/views/bubble/bubble_frame_view.h"
10 #include "ui/views/test/test_widget_observer.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/widget.h"
13 #include "ui/views/widget/widget_observer.h"
19 class TestBubbleDelegateView : public BubbleDelegateView {
21 TestBubbleDelegateView(View* anchor_view)
22 : BubbleDelegateView(anchor_view, BubbleBorder::TOP_LEFT),
24 view_->SetFocusable(true);
27 virtual ~TestBubbleDelegateView() {}
29 void SetAnchorRectForTest(gfx::Rect rect) {
33 void SetAnchorViewForTest(View* view) {
37 // BubbleDelegateView overrides:
38 virtual View* GetInitiallyFocusedView() OVERRIDE { return view_; }
39 virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); }
40 virtual int GetFadeDuration() OVERRIDE { return 1; }
45 DISALLOW_COPY_AND_ASSIGN(TestBubbleDelegateView);
48 class BubbleDelegateTest : public ViewsTestBase {
50 BubbleDelegateTest() {}
51 virtual ~BubbleDelegateTest() {}
53 // Creates a test widget that owns its native widget.
54 Widget* CreateTestWidget() {
55 Widget* widget = new Widget();
56 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
57 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
63 DISALLOW_COPY_AND_ASSIGN(BubbleDelegateTest);
68 TEST_F(BubbleDelegateTest, CreateDelegate) {
69 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
70 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
71 anchor_widget->GetContentsView(), BubbleBorder::NONE);
72 bubble_delegate->set_color(SK_ColorGREEN);
73 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
74 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
75 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
76 test::TestWidgetObserver bubble_observer(bubble_widget);
77 bubble_widget->Show();
79 BubbleBorder* border = bubble_delegate->GetBubbleFrameView()->bubble_border();
80 EXPECT_EQ(bubble_delegate->arrow(), border->arrow());
81 EXPECT_EQ(bubble_delegate->color(), border->background_color());
83 EXPECT_FALSE(bubble_observer.widget_closed());
84 bubble_widget->CloseNow();
85 EXPECT_TRUE(bubble_observer.widget_closed());
88 TEST_F(BubbleDelegateTest, CloseAnchorWidget) {
89 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
90 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
91 anchor_widget->GetContentsView(), BubbleBorder::NONE);
92 // Preventing close on deactivate should not prevent closing with the anchor.
93 bubble_delegate->set_close_on_deactivate(false);
94 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
95 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
96 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
97 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
98 test::TestWidgetObserver bubble_observer(bubble_widget);
99 EXPECT_FALSE(bubble_observer.widget_closed());
101 bubble_widget->Show();
102 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
103 EXPECT_FALSE(bubble_observer.widget_closed());
105 // TODO(msw): Remove activation hack to prevent bookkeeping errors in:
106 // aura::test::TestActivationClient::OnWindowDestroyed().
107 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget());
108 EXPECT_FALSE(bubble_observer.widget_closed());
110 // Ensure that closing the anchor widget also closes the bubble itself.
111 anchor_widget->CloseNow();
112 EXPECT_TRUE(bubble_observer.widget_closed());
115 // This test checks that the bubble delegate is capable to handle an early
116 // destruction of the used anchor view. (Animations and delayed closure of the
117 // bubble will call upon the anchor view to get its location).
118 TEST_F(BubbleDelegateTest, CloseAnchorViewTest) {
119 // Create an anchor widget and add a view to be used as an anchor view.
120 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
121 scoped_ptr<View> anchor_view(new View());
122 anchor_widget->GetContentsView()->AddChildView(anchor_view.get());
123 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView(
125 // Prevent flakes by avoiding closing on activation changes.
126 bubble_delegate->set_close_on_deactivate(false);
127 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
129 // Check that the anchor view is correct and set up an anchor view rect.
130 // Make sure that this rect will get ignored (as long as the anchor view is
132 EXPECT_EQ(anchor_view, bubble_delegate->GetAnchorView());
133 const gfx::Rect set_anchor_rect = gfx::Rect(10, 10, 100, 100);
134 bubble_delegate->SetAnchorRectForTest(set_anchor_rect);
135 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect();
136 EXPECT_NE(view_rect.ToString(), set_anchor_rect.ToString());
138 // Create the bubble.
139 bubble_widget->Show();
140 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
142 // Remove now the anchor view and make sure that the original found rect
143 // is still kept, so that the bubble does not jump when the view gets deleted.
144 anchor_widget->GetContentsView()->RemoveChildView(anchor_view.get());
146 EXPECT_EQ(NULL, bubble_delegate->GetAnchorView());
147 EXPECT_EQ(view_rect.ToString(), bubble_delegate->GetAnchorRect().ToString());
150 // Testing that a move of the anchor view will lead to new bubble locations.
151 TEST_F(BubbleDelegateTest, TestAnchorRectMovesWithViewTest) {
152 // Create an anchor widget and add a view to be used as anchor view.
153 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
154 TestBubbleDelegateView* bubble_delegate = new TestBubbleDelegateView(
155 anchor_widget->GetContentsView());
156 BubbleDelegateView::CreateBubble(bubble_delegate);
158 anchor_widget->GetContentsView()->SetBounds(10, 10, 100, 100);
159 const gfx::Rect view_rect = bubble_delegate->GetAnchorRect();
161 anchor_widget->GetContentsView()->SetBounds(20, 10, 100, 100);
162 const gfx::Rect view_rect_2 = bubble_delegate->GetAnchorRect();
163 EXPECT_NE(view_rect.ToString(), view_rect_2.ToString());
166 TEST_F(BubbleDelegateTest, ResetAnchorWidget) {
167 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
168 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
169 anchor_widget->GetContentsView(), BubbleBorder::NONE);
171 // Make sure the bubble widget is parented to a widget other than the anchor
172 // widget so that closing the anchor widget does not close the bubble widget.
173 scoped_ptr<Widget> parent_widget(CreateTestWidget());
174 bubble_delegate->set_parent_window(parent_widget->GetNativeView());
175 // Preventing close on deactivate should not prevent closing with the parent.
176 bubble_delegate->set_close_on_deactivate(false);
177 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
178 EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
179 EXPECT_EQ(bubble_widget, bubble_delegate->GetWidget());
180 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
181 test::TestWidgetObserver bubble_observer(bubble_widget);
182 EXPECT_FALSE(bubble_observer.widget_closed());
184 // Showing and hiding the bubble widget should have no effect on its anchor.
185 bubble_widget->Show();
186 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
187 bubble_widget->Hide();
188 EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
190 // Ensure that closing the anchor widget clears the bubble's reference to that
191 // anchor widget, but the bubble itself does not close.
192 anchor_widget->CloseNow();
193 EXPECT_NE(anchor_widget, bubble_delegate->anchor_widget());
194 EXPECT_FALSE(bubble_observer.widget_closed());
196 // TODO(msw): Remove activation hack to prevent bookkeeping errors in:
197 // aura::test::TestActivationClient::OnWindowDestroyed().
198 scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget());
199 EXPECT_FALSE(bubble_observer.widget_closed());
201 // Ensure that closing the parent widget also closes the bubble itself.
202 parent_widget->CloseNow();
203 EXPECT_TRUE(bubble_observer.widget_closed());
206 TEST_F(BubbleDelegateTest, InitiallyFocusedView) {
207 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
208 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
209 anchor_widget->GetContentsView(), BubbleBorder::NONE);
210 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
211 EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(),
212 bubble_widget->GetFocusManager()->GetFocusedView());
213 bubble_widget->CloseNow();
216 TEST_F(BubbleDelegateTest, NonClientHitTest) {
217 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
218 TestBubbleDelegateView* bubble_delegate =
219 new TestBubbleDelegateView(anchor_widget->GetContentsView());
220 BubbleDelegateView::CreateBubble(bubble_delegate);
221 BubbleFrameView* frame = bubble_delegate->GetBubbleFrameView();
222 const int border = frame->bubble_border()->GetBorderThickness();
228 { border, HTNOWHERE },
229 { border + 50, HTCLIENT },
233 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
234 gfx::Point point(cases[i].point, cases[i].point);
235 EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
236 << " with border: " << border << ", at point " << cases[i].point;
240 TEST_F(BubbleDelegateTest, CloseWhenAnchorWidgetBoundsChanged) {
241 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
242 BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
243 anchor_widget->GetContentsView(), BubbleBorder::NONE);
244 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
245 test::TestWidgetObserver bubble_observer(bubble_widget);
246 EXPECT_FALSE(bubble_observer.widget_closed());
248 bubble_widget->Show();
249 EXPECT_TRUE(bubble_widget->IsVisible());
250 anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100));
251 EXPECT_FALSE(bubble_widget->IsVisible());
254 // This class provides functionality to verify that the BubbleView shows up
255 // when we call BubbleDelegateView::StartFade(true) and is destroyed when we
256 // call BubbleDelegateView::StartFade(false).
257 class BubbleWidgetClosingTest : public BubbleDelegateTest,
258 public views::WidgetObserver {
260 BubbleWidgetClosingTest() : bubble_destroyed_(false) {
261 aura::Env::CreateInstance();
264 virtual ~BubbleWidgetClosingTest() {}
266 void Observe(views::Widget* widget) {
267 widget->AddObserver(this);
270 // views::WidgetObserver overrides.
271 virtual void OnWidgetDestroyed(Widget* widget) OVERRIDE {
272 bubble_destroyed_ = true;
273 widget->RemoveObserver(this);
277 bool bubble_destroyed() const { return bubble_destroyed_; }
279 void RunNestedLoop() {
284 bool bubble_destroyed_;
287 DISALLOW_COPY_AND_ASSIGN(BubbleWidgetClosingTest);
290 TEST_F(BubbleWidgetClosingTest, TestBubbleVisibilityAndClose) {
291 scoped_ptr<Widget> anchor_widget(CreateTestWidget());
292 TestBubbleDelegateView* bubble_delegate =
293 new TestBubbleDelegateView(anchor_widget->GetContentsView());
294 Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
295 EXPECT_FALSE(bubble_widget->IsVisible());
297 bubble_delegate->StartFade(true);
298 EXPECT_TRUE(bubble_widget->IsVisible());
300 EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(),
301 bubble_widget->GetFocusManager()->GetFocusedView());
303 Observe(bubble_widget);
305 bubble_delegate->StartFade(false);
307 EXPECT_TRUE(bubble_destroyed());