• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/widget/root_view.h"
6 
7 #include "ui/views/context_menu_controller.h"
8 #include "ui/views/test/views_test_base.h"
9 #include "ui/views/view_targeter.h"
10 #include "ui/views/widget/root_view.h"
11 
12 namespace views {
13 namespace test {
14 
15 typedef ViewsTestBase RootViewTest;
16 
17 class DeleteOnKeyEventView : public View {
18  public:
DeleteOnKeyEventView(bool * set_on_key)19   explicit DeleteOnKeyEventView(bool* set_on_key) : set_on_key_(set_on_key) {}
~DeleteOnKeyEventView()20   virtual ~DeleteOnKeyEventView() {}
21 
OnKeyPressed(const ui::KeyEvent & event)22   virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE {
23     *set_on_key_ = true;
24     delete this;
25     return true;
26   }
27 
28  private:
29   // Set to true in OnKeyPressed().
30   bool* set_on_key_;
31 
32   DISALLOW_COPY_AND_ASSIGN(DeleteOnKeyEventView);
33 };
34 
35 // Verifies deleting a View in OnKeyPressed() doesn't crash and that the
36 // target is marked as destroyed in the returned EventDispatchDetails.
TEST_F(RootViewTest,DeleteViewDuringKeyEventDispatch)37 TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) {
38   Widget widget;
39   Widget::InitParams init_params =
40       CreateParams(Widget::InitParams::TYPE_POPUP);
41   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
42   widget.Init(init_params);
43 
44   bool got_key_event = false;
45 
46   View* content = new View;
47   widget.SetContentsView(content);
48 
49   View* child = new DeleteOnKeyEventView(&got_key_event);
50   content->AddChildView(child);
51 
52   // Give focus to |child| so that it will be the target of the key event.
53   child->SetFocusable(true);
54   child->RequestFocus();
55 
56   internal::RootView* root_view =
57       static_cast<internal::RootView*>(widget.GetRootView());
58   ViewTargeter* view_targeter = new ViewTargeter(root_view);
59   root_view->SetEventTargeter(make_scoped_ptr(view_targeter));
60 
61   ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, ui::EF_NONE);
62   ui::EventDispatchDetails details = root_view->OnEventFromSource(&key_event);
63   EXPECT_TRUE(details.target_destroyed);
64   EXPECT_FALSE(details.dispatcher_destroyed);
65   EXPECT_TRUE(got_key_event);
66 }
67 
68 // Tracks whether a context menu is shown.
69 class TestContextMenuController : public ContextMenuController {
70  public:
TestContextMenuController()71   TestContextMenuController()
72       : show_context_menu_calls_(0),
73         menu_source_view_(NULL),
74         menu_source_type_(ui::MENU_SOURCE_NONE) {
75   }
~TestContextMenuController()76   virtual ~TestContextMenuController() {}
77 
show_context_menu_calls() const78   int show_context_menu_calls() const { return show_context_menu_calls_; }
menu_source_view() const79   View* menu_source_view() const { return menu_source_view_; }
menu_source_type() const80   ui::MenuSourceType menu_source_type() const { return menu_source_type_; }
81 
Reset()82   void Reset() {
83     show_context_menu_calls_ = 0;
84     menu_source_view_ = NULL;
85     menu_source_type_ = ui::MENU_SOURCE_NONE;
86   }
87 
88   // ContextMenuController:
ShowContextMenuForView(View * source,const gfx::Point & point,ui::MenuSourceType source_type)89   virtual void ShowContextMenuForView(
90       View* source,
91       const gfx::Point& point,
92       ui::MenuSourceType source_type) OVERRIDE {
93     show_context_menu_calls_++;
94     menu_source_view_ = source;
95     menu_source_type_ = source_type;
96   }
97 
98  private:
99   int show_context_menu_calls_;
100   View* menu_source_view_;
101   ui::MenuSourceType menu_source_type_;
102 
103   DISALLOW_COPY_AND_ASSIGN(TestContextMenuController);
104 };
105 
106 // Tests that context menus are shown for certain key events (Shift+F10
107 // and VKEY_APPS) by the pre-target handler installed on RootView.
TEST_F(RootViewTest,ContextMenuFromKeyEvent)108 TEST_F(RootViewTest, ContextMenuFromKeyEvent) {
109   Widget widget;
110   Widget::InitParams init_params =
111       CreateParams(Widget::InitParams::TYPE_POPUP);
112   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
113   widget.Init(init_params);
114   internal::RootView* root_view =
115       static_cast<internal::RootView*>(widget.GetRootView());
116 
117   TestContextMenuController controller;
118   View* focused_view = new View;
119   focused_view->set_context_menu_controller(&controller);
120   widget.SetContentsView(focused_view);
121   focused_view->SetFocusable(true);
122   focused_view->RequestFocus();
123 
124   // No context menu should be shown for a keypress of 'A'.
125   ui::KeyEvent nomenu_key_event('a', ui::VKEY_A, ui::EF_NONE);
126   ui::EventDispatchDetails details =
127       root_view->OnEventFromSource(&nomenu_key_event);
128   EXPECT_FALSE(details.target_destroyed);
129   EXPECT_FALSE(details.dispatcher_destroyed);
130   EXPECT_EQ(0, controller.show_context_menu_calls());
131   EXPECT_EQ(NULL, controller.menu_source_view());
132   EXPECT_EQ(ui::MENU_SOURCE_NONE, controller.menu_source_type());
133   controller.Reset();
134 
135   // A context menu should be shown for a keypress of Shift+F10.
136   ui::KeyEvent menu_key_event(
137       ui::ET_KEY_PRESSED, ui::VKEY_F10, ui::EF_SHIFT_DOWN);
138   details = root_view->OnEventFromSource(&menu_key_event);
139   EXPECT_FALSE(details.target_destroyed);
140   EXPECT_FALSE(details.dispatcher_destroyed);
141   EXPECT_EQ(1, controller.show_context_menu_calls());
142   EXPECT_EQ(focused_view, controller.menu_source_view());
143   EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type());
144   controller.Reset();
145 
146   // A context menu should be shown for a keypress of VKEY_APPS.
147   ui::KeyEvent menu_key_event2(ui::ET_KEY_PRESSED, ui::VKEY_APPS, ui::EF_NONE);
148   details = root_view->OnEventFromSource(&menu_key_event2);
149   EXPECT_FALSE(details.target_destroyed);
150   EXPECT_FALSE(details.dispatcher_destroyed);
151   EXPECT_EQ(1, controller.show_context_menu_calls());
152   EXPECT_EQ(focused_view, controller.menu_source_view());
153   EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type());
154   controller.Reset();
155 }
156 
157 // View which handles all gesture events.
158 class GestureHandlingView : public View {
159  public:
GestureHandlingView()160   GestureHandlingView() {
161   }
162 
~GestureHandlingView()163   virtual ~GestureHandlingView() {
164   }
165 
OnGestureEvent(ui::GestureEvent * event)166   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
167     event->SetHandled();
168   }
169 
170  private:
171   DISALLOW_COPY_AND_ASSIGN(GestureHandlingView);
172 };
173 
174 // Tests that context menus are shown for long press by the post-target handler
175 // installed on the RootView only if the event is targetted at a view which can
176 // show a context menu.
TEST_F(RootViewTest,ContextMenuFromLongPress)177 TEST_F(RootViewTest, ContextMenuFromLongPress) {
178   Widget widget;
179   Widget::InitParams init_params =
180       CreateParams(Widget::InitParams::TYPE_POPUP);
181   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
182   init_params.bounds = gfx::Rect(100, 100);
183   widget.Init(init_params);
184   internal::RootView* root_view =
185       static_cast<internal::RootView*>(widget.GetRootView());
186 
187   // Create a view capable of showing the context menu with two children one of
188   // which handles all gesture events (e.g. a button).
189   TestContextMenuController controller;
190   View* parent_view = new View;
191   parent_view->set_context_menu_controller(&controller);
192   widget.SetContentsView(parent_view);
193 
194   View* gesture_handling_child_view = new GestureHandlingView;
195   gesture_handling_child_view->SetBoundsRect(gfx::Rect(10, 10));
196   parent_view->AddChildView(gesture_handling_child_view);
197 
198   View* other_child_view = new View;
199   other_child_view->SetBoundsRect(gfx::Rect(20, 0, 10, 10));
200   parent_view->AddChildView(other_child_view);
201 
202   // |parent_view| should not show a context menu as a result of a long press on
203   // |gesture_handling_child_view|.
204   ui::GestureEvent long_press1(
205       5,
206       5,
207       0,
208       base::TimeDelta(),
209       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
210   ui::EventDispatchDetails details = root_view->OnEventFromSource(&long_press1);
211 
212   ui::GestureEvent end1(
213       5, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
214   details = root_view->OnEventFromSource(&end1);
215 
216   EXPECT_FALSE(details.target_destroyed);
217   EXPECT_FALSE(details.dispatcher_destroyed);
218   EXPECT_EQ(0, controller.show_context_menu_calls());
219   controller.Reset();
220 
221   // |parent_view| should show a context menu as a result of a long press on
222   // |other_child_view|.
223   ui::GestureEvent long_press2(
224       25,
225       5,
226       0,
227       base::TimeDelta(),
228       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
229   details = root_view->OnEventFromSource(&long_press2);
230 
231   ui::GestureEvent end2(
232       25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
233   details = root_view->OnEventFromSource(&end2);
234 
235   EXPECT_FALSE(details.target_destroyed);
236   EXPECT_FALSE(details.dispatcher_destroyed);
237   EXPECT_EQ(1, controller.show_context_menu_calls());
238   controller.Reset();
239 
240   // |parent_view| should show a context menu as a result of a long press on
241   // itself.
242   ui::GestureEvent long_press3(
243       50,
244       50,
245       0,
246       base::TimeDelta(),
247       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
248   details = root_view->OnEventFromSource(&long_press3);
249 
250   ui::GestureEvent end3(
251       25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
252   details = root_view->OnEventFromSource(&end3);
253 
254   EXPECT_FALSE(details.target_destroyed);
255   EXPECT_FALSE(details.dispatcher_destroyed);
256   EXPECT_EQ(1, controller.show_context_menu_calls());
257 }
258 
259 // Tests that context menus are not shown for disabled views on a long press.
TEST_F(RootViewTest,ContextMenuFromLongPressOnDisabledView)260 TEST_F(RootViewTest, ContextMenuFromLongPressOnDisabledView) {
261   Widget widget;
262   Widget::InitParams init_params =
263       CreateParams(Widget::InitParams::TYPE_POPUP);
264   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
265   init_params.bounds = gfx::Rect(100, 100);
266   widget.Init(init_params);
267   internal::RootView* root_view =
268       static_cast<internal::RootView*>(widget.GetRootView());
269 
270   // Create a view capable of showing the context menu with two children one of
271   // which handles all gesture events (e.g. a button). Also mark this view
272   // as disabled.
273   TestContextMenuController controller;
274   View* parent_view = new View;
275   parent_view->set_context_menu_controller(&controller);
276   parent_view->SetEnabled(false);
277   widget.SetContentsView(parent_view);
278 
279   View* gesture_handling_child_view = new GestureHandlingView;
280   gesture_handling_child_view->SetBoundsRect(gfx::Rect(10, 10));
281   parent_view->AddChildView(gesture_handling_child_view);
282 
283   View* other_child_view = new View;
284   other_child_view->SetBoundsRect(gfx::Rect(20, 0, 10, 10));
285   parent_view->AddChildView(other_child_view);
286 
287   // |parent_view| should not show a context menu as a result of a long press on
288   // |gesture_handling_child_view|.
289   ui::GestureEvent long_press1(
290       5,
291       5,
292       0,
293       base::TimeDelta(),
294       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
295   ui::EventDispatchDetails details = root_view->OnEventFromSource(&long_press1);
296 
297   ui::GestureEvent end1(
298       5, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
299   details = root_view->OnEventFromSource(&end1);
300 
301   EXPECT_FALSE(details.target_destroyed);
302   EXPECT_FALSE(details.dispatcher_destroyed);
303   EXPECT_EQ(0, controller.show_context_menu_calls());
304   controller.Reset();
305 
306   // |parent_view| should not show a context menu as a result of a long press on
307   // |other_child_view|.
308   ui::GestureEvent long_press2(
309       25,
310       5,
311       0,
312       base::TimeDelta(),
313       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
314   details = root_view->OnEventFromSource(&long_press2);
315 
316   ui::GestureEvent end2(
317       25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
318   details = root_view->OnEventFromSource(&end2);
319 
320   EXPECT_FALSE(details.target_destroyed);
321   EXPECT_FALSE(details.dispatcher_destroyed);
322   EXPECT_EQ(0, controller.show_context_menu_calls());
323   controller.Reset();
324 
325   // |parent_view| should not show a context menu as a result of a long press on
326   // itself.
327   ui::GestureEvent long_press3(
328       50,
329       50,
330       0,
331       base::TimeDelta(),
332       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
333   details = root_view->OnEventFromSource(&long_press3);
334 
335   ui::GestureEvent end3(
336       25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END));
337   details = root_view->OnEventFromSource(&end3);
338 
339   EXPECT_FALSE(details.target_destroyed);
340   EXPECT_FALSE(details.dispatcher_destroyed);
341   EXPECT_EQ(0, controller.show_context_menu_calls());
342 }
343 
344 }  // namespace test
345 }  // namespace views
346