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/events/event_targeter.h"
      8 #include "ui/views/context_menu_controller.h"
      9 #include "ui/views/test/views_test_base.h"
     10 #include "ui/views/view_targeter.h"
     11 #include "ui/views/widget/root_view.h"
     12 
     13 namespace views {
     14 namespace test {
     15 
     16 typedef ViewsTestBase RootViewTest;
     17 
     18 class DeleteOnKeyEventView : public View {
     19  public:
     20   explicit DeleteOnKeyEventView(bool* set_on_key) : set_on_key_(set_on_key) {}
     21   virtual ~DeleteOnKeyEventView() {}
     22 
     23   virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE {
     24     *set_on_key_ = true;
     25     delete this;
     26     return true;
     27   }
     28 
     29  private:
     30   // Set to true in OnKeyPressed().
     31   bool* set_on_key_;
     32 
     33   DISALLOW_COPY_AND_ASSIGN(DeleteOnKeyEventView);
     34 };
     35 
     36 // Verifies deleting a View in OnKeyPressed() doesn't crash and that the
     37 // target is marked as destroyed in the returned EventDispatchDetails.
     38 TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) {
     39   Widget widget;
     40   Widget::InitParams init_params =
     41       CreateParams(Widget::InitParams::TYPE_POPUP);
     42   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     43   widget.Init(init_params);
     44 
     45   bool got_key_event = false;
     46 
     47   View* content = new View;
     48   widget.SetContentsView(content);
     49 
     50   View* child = new DeleteOnKeyEventView(&got_key_event);
     51   content->AddChildView(child);
     52 
     53   // Give focus to |child| so that it will be the target of the key event.
     54   child->SetFocusable(true);
     55   child->RequestFocus();
     56 
     57   ui::EventTargeter* targeter = new ViewTargeter();
     58   internal::RootView* root_view =
     59       static_cast<internal::RootView*>(widget.GetRootView());
     60   root_view->SetEventTargeter(make_scoped_ptr(targeter));
     61 
     62   ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0, false);
     63   ui::EventDispatchDetails details = root_view->OnEventFromSource(&key_event);
     64   EXPECT_TRUE(details.target_destroyed);
     65   EXPECT_FALSE(details.dispatcher_destroyed);
     66   EXPECT_TRUE(got_key_event);
     67 }
     68 
     69 // Tracks whether a context menu is shown.
     70 class TestContextMenuController : public ContextMenuController {
     71  public:
     72   TestContextMenuController()
     73       : show_context_menu_calls_(0),
     74         menu_source_view_(NULL),
     75         menu_source_type_(ui::MENU_SOURCE_NONE) {
     76   }
     77   virtual ~TestContextMenuController() {}
     78 
     79   int show_context_menu_calls() const { return show_context_menu_calls_; }
     80   View* menu_source_view() const { return menu_source_view_; }
     81   ui::MenuSourceType menu_source_type() const { return menu_source_type_; }
     82 
     83   void Reset() {
     84     show_context_menu_calls_ = 0;
     85     menu_source_view_ = NULL;
     86     menu_source_type_ = ui::MENU_SOURCE_NONE;
     87   }
     88 
     89   // ContextMenuController:
     90   virtual void ShowContextMenuForView(
     91       View* source,
     92       const gfx::Point& point,
     93       ui::MenuSourceType source_type) OVERRIDE {
     94     show_context_menu_calls_++;
     95     menu_source_view_ = source;
     96     menu_source_type_ = source_type;
     97   }
     98 
     99  private:
    100   int show_context_menu_calls_;
    101   View* menu_source_view_;
    102   ui::MenuSourceType menu_source_type_;
    103 
    104   DISALLOW_COPY_AND_ASSIGN(TestContextMenuController);
    105 };
    106 
    107 // Tests that context menus are shown for certain key events (Shift+F10
    108 // and VKEY_APPS) by the pre-target handler installed on RootView.
    109 TEST_F(RootViewTest, ContextMenuFromKeyEvent) {
    110   Widget widget;
    111   Widget::InitParams init_params =
    112       CreateParams(Widget::InitParams::TYPE_POPUP);
    113   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    114   widget.Init(init_params);
    115   internal::RootView* root_view =
    116       static_cast<internal::RootView*>(widget.GetRootView());
    117 
    118   TestContextMenuController controller;
    119   View* focused_view = new View;
    120   focused_view->set_context_menu_controller(&controller);
    121   widget.SetContentsView(focused_view);
    122   focused_view->SetFocusable(true);
    123   focused_view->RequestFocus();
    124 
    125   // No context menu should be shown for a keypress of 'A'.
    126   ui::KeyEvent nomenu_key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
    127   ui::EventDispatchDetails details =
    128       root_view->OnEventFromSource(&nomenu_key_event);
    129   EXPECT_FALSE(details.target_destroyed);
    130   EXPECT_FALSE(details.dispatcher_destroyed);
    131   EXPECT_EQ(0, controller.show_context_menu_calls());
    132   EXPECT_EQ(NULL, controller.menu_source_view());
    133   EXPECT_EQ(ui::MENU_SOURCE_NONE, controller.menu_source_type());
    134   controller.Reset();
    135 
    136   // A context menu should be shown for a keypress of Shift+F10.
    137   ui::KeyEvent menu_key_event(
    138       ui::ET_KEY_PRESSED, ui::VKEY_F10, ui::EF_SHIFT_DOWN, false);
    139   details = root_view->OnEventFromSource(&menu_key_event);
    140   EXPECT_FALSE(details.target_destroyed);
    141   EXPECT_FALSE(details.dispatcher_destroyed);
    142   EXPECT_EQ(1, controller.show_context_menu_calls());
    143   EXPECT_EQ(focused_view, controller.menu_source_view());
    144   EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type());
    145   controller.Reset();
    146 
    147   // A context menu should be shown for a keypress of VKEY_APPS.
    148   ui::KeyEvent menu_key_event2(ui::ET_KEY_PRESSED, ui::VKEY_APPS, 0, false);
    149   details = root_view->OnEventFromSource(&menu_key_event2);
    150   EXPECT_FALSE(details.target_destroyed);
    151   EXPECT_FALSE(details.dispatcher_destroyed);
    152   EXPECT_EQ(1, controller.show_context_menu_calls());
    153   EXPECT_EQ(focused_view, controller.menu_source_view());
    154   EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type());
    155   controller.Reset();
    156 }
    157 
    158 // View which handles all gesture events.
    159 class GestureHandlingView : public View {
    160  public:
    161   GestureHandlingView() {
    162   }
    163 
    164   virtual ~GestureHandlingView() {
    165   }
    166 
    167   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
    168     event->SetHandled();
    169   }
    170 
    171  private:
    172   DISALLOW_COPY_AND_ASSIGN(GestureHandlingView);
    173 };
    174 
    175 // Tests that context menus are shown for long press by the post-target handler
    176 // installed on the RootView only if the event is targetted at a view which can
    177 // show a context menu.
    178 TEST_F(RootViewTest, ContextMenuFromLongPress) {
    179   Widget widget;
    180   Widget::InitParams init_params =
    181       CreateParams(Widget::InitParams::TYPE_POPUP);
    182   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    183   init_params.bounds = gfx::Rect(100,100);
    184   widget.Init(init_params);
    185   internal::RootView* root_view =
    186       static_cast<internal::RootView*>(widget.GetRootView());
    187 
    188   // Create a view capable of showing the context menu with two children one of
    189   // which handles all gesture events (e.g. a button).
    190   TestContextMenuController controller;
    191   View* parent_view = new View;
    192   parent_view->set_context_menu_controller(&controller);
    193   widget.SetContentsView(parent_view);
    194 
    195   View* gesture_handling_child_view = new GestureHandlingView;
    196   gesture_handling_child_view->SetBoundsRect(gfx::Rect(10,10));
    197   parent_view->AddChildView(gesture_handling_child_view);
    198 
    199   View* other_child_view = new View;
    200   other_child_view->SetBoundsRect(gfx::Rect(20, 0, 10,10));
    201   parent_view->AddChildView(other_child_view);
    202 
    203   // |parent_view| should not show a context menu as a result of a long press on
    204   // |gesture_handling_child_view|.
    205   ui::GestureEvent begin1(ui::ET_GESTURE_BEGIN, 5, 5, 0, base::TimeDelta(),
    206       ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
    207   ui::EventDispatchDetails details = root_view->OnEventFromSource(&begin1);
    208 
    209   ui::GestureEvent long_press1(ui::ET_GESTURE_LONG_PRESS, 5, 5, 0,
    210       base::TimeDelta(),
    211       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0), 1);
    212   details = root_view->OnEventFromSource(&long_press1);
    213 
    214   ui::GestureEvent end1(ui::ET_GESTURE_END, 5, 5, 0, base::TimeDelta(),
    215       ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
    216   details = root_view->OnEventFromSource(&end1);
    217 
    218   EXPECT_FALSE(details.target_destroyed);
    219   EXPECT_FALSE(details.dispatcher_destroyed);
    220   EXPECT_EQ(0, controller.show_context_menu_calls());
    221 
    222   // |parent_view| should show a context menu as a result of a long press on
    223   // |other_child_view|.
    224   ui::GestureEvent begin2(ui::ET_GESTURE_BEGIN, 25, 5, 0, base::TimeDelta(),
    225       ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
    226   details = root_view->OnEventFromSource(&begin2);
    227 
    228   ui::GestureEvent long_press2(ui::ET_GESTURE_LONG_PRESS, 25, 5, 0,
    229       base::TimeDelta(),
    230       ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0), 1);
    231   details = root_view->OnEventFromSource(&long_press2);
    232 
    233   ui::GestureEvent end2(ui::ET_GESTURE_END, 25, 5, 0, base::TimeDelta(),
    234       ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
    235   details = root_view->OnEventFromSource(&end2);
    236 
    237   EXPECT_FALSE(details.target_destroyed);
    238   EXPECT_FALSE(details.dispatcher_destroyed);
    239   EXPECT_EQ(1, controller.show_context_menu_calls());
    240 }
    241 
    242 }  // namespace test
    243 }  // namespace views
    244