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 ~DeleteOnKeyEventView() override
{}
22 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 ~TestContextMenuController() override
{}
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 void ShowContextMenuForView(View
* source
,
90 const gfx::Point
& point
,
91 ui::MenuSourceType source_type
) override
{
92 show_context_menu_calls_
++;
93 menu_source_view_
= source
;
94 menu_source_type_
= source_type
;
98 int show_context_menu_calls_
;
99 View
* menu_source_view_
;
100 ui::MenuSourceType menu_source_type_
;
102 DISALLOW_COPY_AND_ASSIGN(TestContextMenuController
);
105 // Tests that context menus are shown for certain key events (Shift+F10
106 // and VKEY_APPS) by the pre-target handler installed on RootView.
107 TEST_F(RootViewTest
, ContextMenuFromKeyEvent
) {
109 Widget::InitParams init_params
=
110 CreateParams(Widget::InitParams::TYPE_POPUP
);
111 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
112 widget
.Init(init_params
);
113 internal::RootView
* root_view
=
114 static_cast<internal::RootView
*>(widget
.GetRootView());
116 TestContextMenuController controller
;
117 View
* focused_view
= new View
;
118 focused_view
->set_context_menu_controller(&controller
);
119 widget
.SetContentsView(focused_view
);
120 focused_view
->SetFocusable(true);
121 focused_view
->RequestFocus();
123 // No context menu should be shown for a keypress of 'A'.
124 ui::KeyEvent
nomenu_key_event('a', ui::VKEY_A
, ui::EF_NONE
);
125 ui::EventDispatchDetails details
=
126 root_view
->OnEventFromSource(&nomenu_key_event
);
127 EXPECT_FALSE(details
.target_destroyed
);
128 EXPECT_FALSE(details
.dispatcher_destroyed
);
129 EXPECT_EQ(0, controller
.show_context_menu_calls());
130 EXPECT_EQ(NULL
, controller
.menu_source_view());
131 EXPECT_EQ(ui::MENU_SOURCE_NONE
, controller
.menu_source_type());
134 // A context menu should be shown for a keypress of Shift+F10.
135 ui::KeyEvent
menu_key_event(
136 ui::ET_KEY_PRESSED
, ui::VKEY_F10
, ui::EF_SHIFT_DOWN
);
137 details
= root_view
->OnEventFromSource(&menu_key_event
);
138 EXPECT_FALSE(details
.target_destroyed
);
139 EXPECT_FALSE(details
.dispatcher_destroyed
);
140 EXPECT_EQ(1, controller
.show_context_menu_calls());
141 EXPECT_EQ(focused_view
, controller
.menu_source_view());
142 EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD
, controller
.menu_source_type());
145 // A context menu should be shown for a keypress of VKEY_APPS.
146 ui::KeyEvent
menu_key_event2(ui::ET_KEY_PRESSED
, ui::VKEY_APPS
, ui::EF_NONE
);
147 details
= root_view
->OnEventFromSource(&menu_key_event2
);
148 EXPECT_FALSE(details
.target_destroyed
);
149 EXPECT_FALSE(details
.dispatcher_destroyed
);
150 EXPECT_EQ(1, controller
.show_context_menu_calls());
151 EXPECT_EQ(focused_view
, controller
.menu_source_view());
152 EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD
, controller
.menu_source_type());
156 // View which handles all gesture events.
157 class GestureHandlingView
: public View
{
159 GestureHandlingView() {
162 ~GestureHandlingView() override
{}
164 void OnGestureEvent(ui::GestureEvent
* event
) override
{ event
->SetHandled(); }
167 DISALLOW_COPY_AND_ASSIGN(GestureHandlingView
);
170 // Tests that context menus are shown for long press by the post-target handler
171 // installed on the RootView only if the event is targetted at a view which can
172 // show a context menu.
173 TEST_F(RootViewTest
, ContextMenuFromLongPress
) {
175 Widget::InitParams init_params
=
176 CreateParams(Widget::InitParams::TYPE_POPUP
);
177 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
178 init_params
.bounds
= gfx::Rect(100, 100);
179 widget
.Init(init_params
);
180 internal::RootView
* root_view
=
181 static_cast<internal::RootView
*>(widget
.GetRootView());
183 // Create a view capable of showing the context menu with two children one of
184 // which handles all gesture events (e.g. a button).
185 TestContextMenuController controller
;
186 View
* parent_view
= new View
;
187 parent_view
->set_context_menu_controller(&controller
);
188 widget
.SetContentsView(parent_view
);
190 View
* gesture_handling_child_view
= new GestureHandlingView
;
191 gesture_handling_child_view
->SetBoundsRect(gfx::Rect(10, 10));
192 parent_view
->AddChildView(gesture_handling_child_view
);
194 View
* other_child_view
= new View
;
195 other_child_view
->SetBoundsRect(gfx::Rect(20, 0, 10, 10));
196 parent_view
->AddChildView(other_child_view
);
198 // |parent_view| should not show a context menu as a result of a long press on
199 // |gesture_handling_child_view|.
200 ui::GestureEvent
long_press1(
205 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
206 ui::EventDispatchDetails details
= root_view
->OnEventFromSource(&long_press1
);
208 ui::GestureEvent
end1(
209 5, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
210 details
= root_view
->OnEventFromSource(&end1
);
212 EXPECT_FALSE(details
.target_destroyed
);
213 EXPECT_FALSE(details
.dispatcher_destroyed
);
214 EXPECT_EQ(0, controller
.show_context_menu_calls());
217 // |parent_view| should show a context menu as a result of a long press on
218 // |other_child_view|.
219 ui::GestureEvent
long_press2(
224 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
225 details
= root_view
->OnEventFromSource(&long_press2
);
227 ui::GestureEvent
end2(
228 25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
229 details
= root_view
->OnEventFromSource(&end2
);
231 EXPECT_FALSE(details
.target_destroyed
);
232 EXPECT_FALSE(details
.dispatcher_destroyed
);
233 EXPECT_EQ(1, controller
.show_context_menu_calls());
236 // |parent_view| should show a context menu as a result of a long press on
238 ui::GestureEvent
long_press3(
243 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
244 details
= root_view
->OnEventFromSource(&long_press3
);
246 ui::GestureEvent
end3(
247 25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
248 details
= root_view
->OnEventFromSource(&end3
);
250 EXPECT_FALSE(details
.target_destroyed
);
251 EXPECT_FALSE(details
.dispatcher_destroyed
);
252 EXPECT_EQ(1, controller
.show_context_menu_calls());
255 // Tests that context menus are not shown for disabled views on a long press.
256 TEST_F(RootViewTest
, ContextMenuFromLongPressOnDisabledView
) {
258 Widget::InitParams init_params
=
259 CreateParams(Widget::InitParams::TYPE_POPUP
);
260 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
261 init_params
.bounds
= gfx::Rect(100, 100);
262 widget
.Init(init_params
);
263 internal::RootView
* root_view
=
264 static_cast<internal::RootView
*>(widget
.GetRootView());
266 // Create a view capable of showing the context menu with two children one of
267 // which handles all gesture events (e.g. a button). Also mark this view
269 TestContextMenuController controller
;
270 View
* parent_view
= new View
;
271 parent_view
->set_context_menu_controller(&controller
);
272 parent_view
->SetEnabled(false);
273 widget
.SetContentsView(parent_view
);
275 View
* gesture_handling_child_view
= new GestureHandlingView
;
276 gesture_handling_child_view
->SetBoundsRect(gfx::Rect(10, 10));
277 parent_view
->AddChildView(gesture_handling_child_view
);
279 View
* other_child_view
= new View
;
280 other_child_view
->SetBoundsRect(gfx::Rect(20, 0, 10, 10));
281 parent_view
->AddChildView(other_child_view
);
283 // |parent_view| should not show a context menu as a result of a long press on
284 // |gesture_handling_child_view|.
285 ui::GestureEvent
long_press1(
290 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
291 ui::EventDispatchDetails details
= root_view
->OnEventFromSource(&long_press1
);
293 ui::GestureEvent
end1(
294 5, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
295 details
= root_view
->OnEventFromSource(&end1
);
297 EXPECT_FALSE(details
.target_destroyed
);
298 EXPECT_FALSE(details
.dispatcher_destroyed
);
299 EXPECT_EQ(0, controller
.show_context_menu_calls());
302 // |parent_view| should not show a context menu as a result of a long press on
303 // |other_child_view|.
304 ui::GestureEvent
long_press2(
309 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
310 details
= root_view
->OnEventFromSource(&long_press2
);
312 ui::GestureEvent
end2(
313 25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
314 details
= root_view
->OnEventFromSource(&end2
);
316 EXPECT_FALSE(details
.target_destroyed
);
317 EXPECT_FALSE(details
.dispatcher_destroyed
);
318 EXPECT_EQ(0, controller
.show_context_menu_calls());
321 // |parent_view| should not show a context menu as a result of a long press on
323 ui::GestureEvent
long_press3(
328 ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS
));
329 details
= root_view
->OnEventFromSource(&long_press3
);
331 ui::GestureEvent
end3(
332 25, 5, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_END
));
333 details
= root_view
->OnEventFromSource(&end3
);
335 EXPECT_FALSE(details
.target_destroyed
);
336 EXPECT_FALSE(details
.dispatcher_destroyed
);
337 EXPECT_EQ(0, controller
.show_context_menu_calls());