| OLD | NEW |
| (Empty) |
| 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: | |
| 19 explicit DeleteOnKeyEventView(bool* set_on_key) : set_on_key_(set_on_key) {} | |
| 20 virtual ~DeleteOnKeyEventView() {} | |
| 21 | |
| 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. | |
| 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: | |
| 71 TestContextMenuController() | |
| 72 : show_context_menu_calls_(0), | |
| 73 menu_source_view_(NULL), | |
| 74 menu_source_type_(ui::MENU_SOURCE_NONE) { | |
| 75 } | |
| 76 virtual ~TestContextMenuController() {} | |
| 77 | |
| 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_; } | |
| 81 | |
| 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: | |
| 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. | |
| 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: | |
| 160 GestureHandlingView() { | |
| 161 } | |
| 162 | |
| 163 virtual ~GestureHandlingView() { | |
| 164 } | |
| 165 | |
| 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. | |
| 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. | |
| 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 | |
| OLD | NEW |