• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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"
13 
14 #if defined(USE_AURA)
15 #include "ui/aura/env.h"
16 #endif
17 
18 namespace views {
19 
20 namespace {
21 
22 class TestBubbleDelegateView : public BubbleDelegateView {
23  public:
TestBubbleDelegateView(View * anchor_view)24   TestBubbleDelegateView(View* anchor_view)
25       : BubbleDelegateView(anchor_view, BubbleBorder::TOP_LEFT),
26         view_(new View()) {
27     view_->SetFocusable(true);
28     AddChildView(view_);
29   }
~TestBubbleDelegateView()30   virtual ~TestBubbleDelegateView() {}
31 
SetAnchorRectForTest(gfx::Rect rect)32   void SetAnchorRectForTest(gfx::Rect rect) {
33     SetAnchorRect(rect);
34   }
35 
SetAnchorViewForTest(View * view)36   void SetAnchorViewForTest(View* view) {
37     SetAnchorView(view);
38   }
39 
40   // BubbleDelegateView overrides:
GetInitiallyFocusedView()41   virtual View* GetInitiallyFocusedView() OVERRIDE { return view_; }
GetPreferredSize()42   virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); }
GetFadeDuration()43   virtual int GetFadeDuration() OVERRIDE { return 1; }
44 
45  private:
46   View* view_;
47 
48   DISALLOW_COPY_AND_ASSIGN(TestBubbleDelegateView);
49 };
50 
51 class BubbleDelegateTest : public ViewsTestBase {
52  public:
BubbleDelegateTest()53   BubbleDelegateTest() {}
~BubbleDelegateTest()54   virtual ~BubbleDelegateTest() {}
55 
56   // Creates a test widget that owns its native widget.
CreateTestWidget()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;
61     widget->Init(params);
62     return widget;
63   }
64 
65  private:
66   DISALLOW_COPY_AND_ASSIGN(BubbleDelegateTest);
67 };
68 
69 }  // namespace
70 
TEST_F(BubbleDelegateTest,CreateDelegate)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();
81 
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());
85 
86   EXPECT_FALSE(bubble_observer.widget_closed());
87   bubble_widget->CloseNow();
88   EXPECT_TRUE(bubble_observer.widget_closed());
89 }
90 
TEST_F(BubbleDelegateTest,CloseAnchorWidget)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());
103 
104   bubble_widget->Show();
105   EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
106   EXPECT_FALSE(bubble_observer.widget_closed());
107 
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());
113 #endif
114 
115   // Ensure that closing the anchor widget also closes the bubble itself.
116   anchor_widget->CloseNow();
117   EXPECT_TRUE(bubble_observer.widget_closed());
118 }
119 
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).
TEST_F(BubbleDelegateTest,CloseAnchorViewTest)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(
129       anchor_view.get());
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);
133 
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
136   // attached).
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());
142 
143   // Create the bubble.
144   bubble_widget->Show();
145   EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget());
146 
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());
150   anchor_view.reset();
151   EXPECT_EQ(NULL, bubble_delegate->GetAnchorView());
152   EXPECT_EQ(view_rect.ToString(), bubble_delegate->GetAnchorRect().ToString());
153 }
154 
155 // Testing that a move of the anchor view will lead to new bubble locations.
TEST_F(BubbleDelegateTest,TestAnchorRectMovesWithViewTest)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);
162 
163   anchor_widget->GetContentsView()->SetBounds(10, 10, 100, 100);
164   const gfx::Rect view_rect = bubble_delegate->GetAnchorRect();
165 
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());
169 }
170 
TEST_F(BubbleDelegateTest,ResetAnchorWidget)171 TEST_F(BubbleDelegateTest, ResetAnchorWidget) {
172   scoped_ptr<Widget> anchor_widget(CreateTestWidget());
173   BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
174       anchor_widget->GetContentsView(), BubbleBorder::NONE);
175 
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());
188 
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());
194 
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());
200 
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());
206 #endif
207 
208   // Ensure that closing the parent widget also closes the bubble itself.
209   parent_widget->CloseNow();
210   EXPECT_TRUE(bubble_observer.widget_closed());
211 }
212 
TEST_F(BubbleDelegateTest,InitiallyFocusedView)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();
221 }
222 
TEST_F(BubbleDelegateTest,NonClientHitTest)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();
230 
231   struct {
232     const int point;
233     const int hit;
234   } cases[] = {
235     { border,      HTNOWHERE },
236     { border + 50, HTCLIENT  },
237     { 1000,        HTNOWHERE },
238   };
239 
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;
244   }
245 }
246 
TEST_F(BubbleDelegateTest,CloseWhenAnchorWidgetBoundsChanged)247 TEST_F(BubbleDelegateTest, CloseWhenAnchorWidgetBoundsChanged) {
248   scoped_ptr<Widget> anchor_widget(CreateTestWidget());
249   BubbleDelegateView* bubble_delegate = new BubbleDelegateView(
250       anchor_widget->GetContentsView(), BubbleBorder::NONE);
251   Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
252   test::TestWidgetObserver bubble_observer(bubble_widget);
253   EXPECT_FALSE(bubble_observer.widget_closed());
254 
255   bubble_widget->Show();
256   EXPECT_TRUE(bubble_widget->IsVisible());
257   anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100));
258   EXPECT_FALSE(bubble_widget->IsVisible());
259 }
260 
261 // This class provides functionality to verify that the BubbleView shows up
262 // when we call BubbleDelegateView::StartFade(true) and is destroyed when we
263 // call BubbleDelegateView::StartFade(false).
264 class BubbleWidgetClosingTest : public BubbleDelegateTest,
265                                 public views::WidgetObserver {
266  public:
BubbleWidgetClosingTest()267   BubbleWidgetClosingTest() : bubble_destroyed_(false) {
268 #if defined(USE_AURA)
269     aura::Env::CreateInstance();
270     loop_.set_dispatcher(aura::Env::GetInstance()->GetDispatcher());
271 #endif
272   }
273 
~BubbleWidgetClosingTest()274   virtual ~BubbleWidgetClosingTest() {}
275 
Observe(views::Widget * widget)276   void Observe(views::Widget* widget) {
277     widget->AddObserver(this);
278   }
279 
280   // views::WidgetObserver overrides.
OnWidgetDestroyed(Widget * widget)281   virtual void OnWidgetDestroyed(Widget* widget) OVERRIDE {
282     bubble_destroyed_ = true;
283     widget->RemoveObserver(this);
284     loop_.Quit();
285   }
286 
bubble_destroyed() const287   bool bubble_destroyed() const { return bubble_destroyed_; }
288 
RunNestedLoop()289   void RunNestedLoop() {
290     loop_.Run();
291   }
292 
293  private:
294   bool bubble_destroyed_;
295   base::RunLoop loop_;
296 
297   DISALLOW_COPY_AND_ASSIGN(BubbleWidgetClosingTest);
298 };
299 
TEST_F(BubbleWidgetClosingTest,TestBubbleVisibilityAndClose)300 TEST_F(BubbleWidgetClosingTest, TestBubbleVisibilityAndClose) {
301   scoped_ptr<Widget> anchor_widget(CreateTestWidget());
302   TestBubbleDelegateView* bubble_delegate =
303       new TestBubbleDelegateView(anchor_widget->GetContentsView());
304   Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate);
305   EXPECT_FALSE(bubble_widget->IsVisible());
306 
307   bubble_delegate->StartFade(true);
308   EXPECT_TRUE(bubble_widget->IsVisible());
309 
310   EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(),
311             bubble_widget->GetFocusManager()->GetFocusedView());
312 
313   Observe(bubble_widget);
314 
315   bubble_delegate->StartFade(false);
316   RunNestedLoop();
317   EXPECT_TRUE(bubble_destroyed());
318 }
319 
320 }  // namespace views
321