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.
5 #include "ui/views/widget/root_view.h"
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"
15 typedef ViewsTestBase RootViewTest
;
17 class DeleteOnKeyEventView
: public View
{
19 explicit DeleteOnKeyEventView(bool* set_on_key
) : set_on_key_(set_on_key
) {}
20 virtual ~DeleteOnKeyEventView() {}
22 virtual bool OnKeyPressed(const ui::KeyEvent
& event
) OVERRIDE
{
29 // Set to true in OnKeyPressed().
32 DISALLOW_COPY_AND_ASSIGN(DeleteOnKeyEventView
);
35 // Verifies deleting a View in OnKeyPressed() doesn't crash and that the
36 // target is marked as destroyed in the returned EventDispatchDetails.
37 TEST_F(RootViewTest
, DeleteViewDuringKeyEventDispatch
) {
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
);
44 bool got_key_event
= false;
46 View
* content
= new View
;
47 widget
.SetContentsView(content
);
49 View
* child
= new DeleteOnKeyEventView(&got_key_event
);
50 content
->AddChildView(child
);
52 // Give focus to |child| so that it will be the target of the key event.
53 child
->SetFocusable(true);
54 child
->RequestFocus();
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
));
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
);
68 // Tracks whether a context menu is shown.
69 class TestContextMenuController
: public ContextMenuController
{
71 TestContextMenuController()
72 : show_context_menu_calls_(0),
73 menu_source_view_(NULL
),
74 menu_source_type_(ui::MENU_SOURCE_NONE
) {
76 virtual ~TestContextMenuController() {}
78 int show_context_menu_calls() const { return show_context_menu_calls_
; }
79 View
* menu_source_view() const { return menu_source_view_
; }
80 ui::MenuSourceType
menu_source_type() const { return menu_source_type_
; }
83 show_context_menu_calls_
= 0;
84 menu_source_view_
= NULL
;
85 menu_source_type_
= ui::MENU_SOURCE_NONE
;
88 // ContextMenuController:
89 virtual void ShowContextMenuForView(
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
;
99 int show_context_menu_calls_
;
100 View
* menu_source_view_
;
101 ui::MenuSourceType menu_source_type_
;
103 DISALLOW_COPY_AND_ASSIGN(TestContextMenuController
);
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.
108 TEST_F(RootViewTest
, ContextMenuFromKeyEvent
) {
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());
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();
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());
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());
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());
157 // View which handles all gesture events.
158 class GestureHandlingView
: public View
{
160 GestureHandlingView() {
163 virtual ~GestureHandlingView() {
166 virtual void OnGestureEvent(ui::GestureEvent
* event
) OVERRIDE
{
171 DISALLOW_COPY_AND_ASSIGN(GestureHandlingView
);
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.
177 TEST_F(RootViewTest
, ContextMenuFromLongPress
) {
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());
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
);
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
);
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
);
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(
209 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
210 ui::EventDispatchDetails details
= root_view
->OnEventFromSource(&long_press1
);
212 ui::GestureEvent
end1(5,
216 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
217 details
= root_view
->OnEventFromSource(&end1
);
219 EXPECT_FALSE(details
.target_destroyed
);
220 EXPECT_FALSE(details
.dispatcher_destroyed
);
221 EXPECT_EQ(0, controller
.show_context_menu_calls());
224 // |parent_view| should show a context menu as a result of a long press on
225 // |other_child_view|.
226 ui::GestureEvent
long_press2(
231 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
232 details
= root_view
->OnEventFromSource(&long_press2
);
234 ui::GestureEvent
end2(25,
238 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
239 details
= root_view
->OnEventFromSource(&end2
);
241 EXPECT_FALSE(details
.target_destroyed
);
242 EXPECT_FALSE(details
.dispatcher_destroyed
);
243 EXPECT_EQ(1, controller
.show_context_menu_calls());
246 // |parent_view| should show a context menu as a result of a long press on
248 ui::GestureEvent
long_press3(
253 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
254 details
= root_view
->OnEventFromSource(&long_press3
);
256 ui::GestureEvent
end3(25,
260 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
261 details
= root_view
->OnEventFromSource(&end3
);
263 EXPECT_FALSE(details
.target_destroyed
);
264 EXPECT_FALSE(details
.dispatcher_destroyed
);
265 EXPECT_EQ(1, controller
.show_context_menu_calls());
268 // Tests that context menus are not shown for disabled views on a long press.
269 TEST_F(RootViewTest
, ContextMenuFromLongPressOnDisabledView
) {
271 Widget::InitParams init_params
=
272 CreateParams(Widget::InitParams::TYPE_POPUP
);
273 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
274 init_params
.bounds
= gfx::Rect(100, 100);
275 widget
.Init(init_params
);
276 internal::RootView
* root_view
=
277 static_cast<internal::RootView
*>(widget
.GetRootView());
279 // Create a view capable of showing the context menu with two children one of
280 // which handles all gesture events (e.g. a button). Also mark this view
282 TestContextMenuController controller
;
283 View
* parent_view
= new View
;
284 parent_view
->set_context_menu_controller(&controller
);
285 parent_view
->SetEnabled(false);
286 widget
.SetContentsView(parent_view
);
288 View
* gesture_handling_child_view
= new GestureHandlingView
;
289 gesture_handling_child_view
->SetBoundsRect(gfx::Rect(10, 10));
290 parent_view
->AddChildView(gesture_handling_child_view
);
292 View
* other_child_view
= new View
;
293 other_child_view
->SetBoundsRect(gfx::Rect(20, 0, 10, 10));
294 parent_view
->AddChildView(other_child_view
);
296 // |parent_view| should not show a context menu as a result of a long press on
297 // |gesture_handling_child_view|.
298 ui::GestureEvent
long_press1(
303 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
304 ui::EventDispatchDetails details
= root_view
->OnEventFromSource(&long_press1
);
306 ui::GestureEvent
end1(5,
310 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
311 details
= root_view
->OnEventFromSource(&end1
);
313 EXPECT_FALSE(details
.target_destroyed
);
314 EXPECT_FALSE(details
.dispatcher_destroyed
);
315 EXPECT_EQ(0, controller
.show_context_menu_calls());
318 // |parent_view| should not show a context menu as a result of a long press on
319 // |other_child_view|.
320 ui::GestureEvent
long_press2(
325 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
326 details
= root_view
->OnEventFromSource(&long_press2
);
328 ui::GestureEvent
end2(25,
332 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
333 details
= root_view
->OnEventFromSource(&end2
);
335 EXPECT_FALSE(details
.target_destroyed
);
336 EXPECT_FALSE(details
.dispatcher_destroyed
);
337 EXPECT_EQ(0, controller
.show_context_menu_calls());
340 // |parent_view| should not show a context menu as a result of a long press on
342 ui::GestureEvent
long_press3(
347 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
, 0, 0));
348 details
= root_view
->OnEventFromSource(&long_press3
);
350 ui::GestureEvent
end3(25,
354 ui::GestureEventDetails(ui::ET_GESTURE_END
, 0, 0));
355 details
= root_view
->OnEventFromSource(&end3
);
357 EXPECT_FALSE(details
.target_destroyed
);
358 EXPECT_FALSE(details
.dispatcher_destroyed
);
359 EXPECT_EQ(0, controller
.show_context_menu_calls());