Home | History | Annotate | Download | only in widget
      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
    346