Commit 1fcbdf70 authored by flackr@chromium.org's avatar flackr@chromium.org

Only dispatch menu events if they have a valid target.

BUG=370162
TEST=In touchview, open wrench menu and press a key (i.e. 'T'), nothing should happen.

Review URL: https://codereview.chromium.org/284903002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271283 0039d316-1c4b-4281-b951-d872f2087c98
parent 46226fb0
...@@ -16,6 +16,53 @@ namespace { ...@@ -16,6 +16,53 @@ namespace {
int g_custom_event_types = ET_LAST; int g_custom_event_types = ET_LAST;
} // namespace } // namespace
scoped_ptr<Event> EventFromNative(const base::NativeEvent& native_event) {
scoped_ptr<Event> event;
EventType type = EventTypeFromNative(native_event);
switch(type) {
case ET_KEY_PRESSED:
case ET_KEY_RELEASED:
event.reset(new KeyEvent(native_event, false));
break;
case ET_TRANSLATED_KEY_PRESS:
case ET_TRANSLATED_KEY_RELEASE:
// These should not be generated by native events.
NOTREACHED();
break;
case ET_MOUSE_PRESSED:
case ET_MOUSE_DRAGGED:
case ET_MOUSE_RELEASED:
case ET_MOUSE_MOVED:
case ET_MOUSE_ENTERED:
case ET_MOUSE_EXITED:
event.reset(new MouseEvent(native_event));
break;
case ET_MOUSEWHEEL:
event.reset(new MouseWheelEvent(native_event));
break;
case ET_SCROLL_FLING_START:
case ET_SCROLL_FLING_CANCEL:
case ET_SCROLL:
event.reset(new ScrollEvent(native_event));
break;
case ET_TOUCH_RELEASED:
case ET_TOUCH_PRESSED:
case ET_TOUCH_MOVED:
case ET_TOUCH_CANCELLED:
event.reset(new TouchEvent(native_event));
break;
default:
break;
}
return event.Pass();
}
int RegisterCustomEventType() { int RegisterCustomEventType() {
return ++g_custom_event_types; return ++g_custom_event_types;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define UI_EVENTS_EVENT_UTILS_H_ #define UI_EVENTS_EVENT_UTILS_H_
#include "base/event_types.h" #include "base/event_types.h"
#include "base/memory/scoped_ptr.h"
#include "ui/events/event_constants.h" #include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/display.h" #include "ui/gfx/display.h"
...@@ -32,6 +33,11 @@ class Event; ...@@ -32,6 +33,11 @@ class Event;
// Updates the list of devices for cached properties. // Updates the list of devices for cached properties.
EVENTS_EXPORT void UpdateDeviceList(); EVENTS_EXPORT void UpdateDeviceList();
// Returns a ui::Event wrapping a native event. Ownership of the returned value
// is transferred to the caller.
EVENTS_EXPORT scoped_ptr<Event> EventFromNative(
const base::NativeEvent& native_event);
// Get the EventType from a native event. // Get the EventType from a native event.
EVENTS_EXPORT EventType EventTypeFromNative( EVENTS_EXPORT EventType EventTypeFromNative(
const base::NativeEvent& native_event); const base::NativeEvent& native_event);
......
...@@ -92,6 +92,10 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { ...@@ -92,6 +92,10 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
// Whether or not drag operation is in progress. // Whether or not drag operation is in progress.
bool drag_in_progress() const { return drag_in_progress_; } bool drag_in_progress() const { return drag_in_progress_; }
// Returns the owner of child windows.
// WARNING: this may be NULL.
Widget* owner() { return owner_; }
// Get the anchor position wich is used to show this menu. // Get the anchor position wich is used to show this menu.
MenuAnchorPosition GetAnchorPosition() { return state_.anchor; } MenuAnchorPosition GetAnchorPosition() { return state_.anchor; }
......
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
#include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_controller.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/events/event_targeter.h"
#include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/platform_event_source.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/test/views_test_base.h" #include "ui/views/test/views_test_base.h"
#include "ui/wm/public/dispatcher_client.h" #include "ui/wm/public/dispatcher_client.h"
...@@ -16,12 +19,22 @@ ...@@ -16,12 +19,22 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#undef Bool #undef Bool
#undef None #undef None
#include "ui/events/test/events_test_utils_x11.h"
#endif #endif
namespace views { namespace views {
namespace { namespace {
class TestMenuItemView : public MenuItemView {
public:
TestMenuItemView() : MenuItemView(NULL) {}
virtual ~TestMenuItemView() {}
private:
DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
};
class TestPlatformEventSource : public ui::PlatformEventSource { class TestPlatformEventSource : public ui::PlatformEventSource {
public: public:
TestPlatformEventSource() {} TestPlatformEventSource() {}
...@@ -35,6 +48,20 @@ class TestPlatformEventSource : public ui::PlatformEventSource { ...@@ -35,6 +48,20 @@ class TestPlatformEventSource : public ui::PlatformEventSource {
DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource); DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
}; };
class TestNullTargeter : public ui::EventTargeter {
public:
TestNullTargeter() {}
virtual ~TestNullTargeter() {}
virtual ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
ui::Event* event) OVERRIDE {
return NULL;
}
private:
DISALLOW_COPY_AND_ASSIGN(TestNullTargeter);
};
class TestDispatcherClient : public aura::client::DispatcherClient { class TestDispatcherClient : public aura::client::DispatcherClient {
public: public:
TestDispatcherClient() : dispatcher_(NULL) {} TestDispatcherClient() : dispatcher_(NULL) {}
...@@ -79,16 +106,7 @@ class MenuControllerTest : public ViewsTestBase { ...@@ -79,16 +106,7 @@ class MenuControllerTest : public ViewsTestBase {
base::MessageLoop::ScopedNestableTaskAllower allow(loop); base::MessageLoop::ScopedNestableTaskAllower allow(loop);
controller_->exit_type_ = MenuController::EXIT_ALL; controller_->exit_type_ = MenuController::EXIT_ALL;
#if defined(USE_X11) DispatchEvent();
XEvent xevent;
memset(&xevent, 0, sizeof(xevent));
event_source_.Dispatch(&xevent);
#else
MSG msg;
memset(&msg, 0, sizeof(MSG));
dispatcher_client_.dispatcher()->Dispatch(msg);
#endif
if (count) { if (count) {
base::MessageLoop::current()->PostTask( base::MessageLoop::current()->PostTask(
FROM_HERE, FROM_HERE,
...@@ -115,24 +133,59 @@ class MenuControllerTest : public ViewsTestBase { ...@@ -115,24 +133,59 @@ class MenuControllerTest : public ViewsTestBase {
} }
void Step1_RunMenu() { void Step1_RunMenu() {
Widget widget; base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
base::Unretained(this)));
scoped_ptr<Widget> owner(CreateOwnerWidget());
RunMenu(owner.get());
}
scoped_ptr<Widget> CreateOwnerWidget() {
scoped_ptr<Widget> widget(new Widget);
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(params); widget->Init(params);
widget.Show(); widget->Show();
aura::client::SetDispatcherClient(widget.GetNativeWindow()->GetRootWindow(), aura::client::SetDispatcherClient(
&dispatcher_client_); widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_);
return widget.Pass();
}
void RunMenu(views::Widget* owner) {
scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
controller_ = new MenuController(NULL, true, NULL); controller_ = new MenuController(NULL, true, NULL);
controller_->owner_ = &widget; controller_->owner_ = owner;
base::MessageLoop::current()->PostTask( controller_->showing_ = true;
FROM_HERE, controller_->SetSelection(menu_item.get(),
base::Bind(&MenuControllerTest::Step2_RunNestedLoop, MenuController::SELECTION_UPDATE_IMMEDIATELY);
base::Unretained(this)));
controller_->RunMessageLoop(false); controller_->RunMessageLoop(false);
} }
#if defined(USE_X11)
void DispatchEscapeAndExpect(MenuController::ExitType exit_type) {
ui::ScopedXI2Event key_event;
key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
event_source_.Dispatch(key_event);
EXPECT_EQ(exit_type, controller_->exit_type());
controller_->exit_type_ = MenuController::EXIT_ALL;
DispatchEvent();
}
#endif
void DispatchEvent() {
#if defined(USE_X11)
XEvent xevent;
memset(&xevent, 0, sizeof(xevent));
event_source_.Dispatch(&xevent);
#else
MSG msg;
memset(&msg, 0, sizeof(MSG));
dispatcher_client_.dispatcher()->Dispatch(msg);
#endif
}
private: private:
MenuController* controller_; MenuController* controller_;
scoped_ptr<base::RunLoop> run_loop_; scoped_ptr<base::RunLoop> run_loop_;
...@@ -150,4 +203,36 @@ TEST_F(MenuControllerTest, Basic) { ...@@ -150,4 +203,36 @@ TEST_F(MenuControllerTest, Basic) {
base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this))); base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
} }
#if defined(OS_LINUX) && defined(USE_X11)
// Tests that an event targeter which blocks events will be honored by the menu
// event dispatcher.
TEST_F(MenuControllerTest, EventTargeter) {
{
// Verify that the menu handles the escape key under normal circumstances.
scoped_ptr<Widget> owner(CreateOwnerWidget());
message_loop()->PostTask(
FROM_HERE,
base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
base::Unretained(this),
MenuController::EXIT_OUTERMOST));
RunMenu(owner.get());
}
{
// With the NULL targeter instantiated and assigned we expect the menu to
// not handle the key event.
scoped_ptr<Widget> owner(CreateOwnerWidget());
aura::ScopedWindowTargeter scoped_targeter(
owner->GetNativeWindow()->GetRootWindow(),
scoped_ptr<ui::EventTargeter>(new TestNullTargeter));
message_loop()->PostTask(
FROM_HERE,
base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
base::Unretained(this),
MenuController::EXIT_NONE));
RunMenu(owner.get());
}
}
#endif
} // namespace views } // namespace views
...@@ -4,10 +4,13 @@ ...@@ -4,10 +4,13 @@
#include "ui/views/controls/menu/menu_event_dispatcher_linux.h" #include "ui/views/controls/menu/menu_event_dispatcher_linux.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/window.h"
#include "ui/events/event_utils.h" #include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/widget/widget.h"
namespace views { namespace views {
namespace internal { namespace internal {
...@@ -24,10 +27,27 @@ bool MenuEventDispatcher::CanDispatchEvent(const ui::PlatformEvent& event) { ...@@ -24,10 +27,27 @@ bool MenuEventDispatcher::CanDispatchEvent(const ui::PlatformEvent& event) {
uint32_t MenuEventDispatcher::DispatchEvent(const ui::PlatformEvent& event) { uint32_t MenuEventDispatcher::DispatchEvent(const ui::PlatformEvent& event) {
bool should_quit = false; bool should_quit = false;
bool should_perform_default = true; bool should_perform_default = true;
bool should_process_event = true;
// Check if the event should be handled.
scoped_ptr<ui::Event> ui_event(ui::EventFromNative(event));
if (ui_event && menu_controller_->owner()) {
aura::Window* menu_window = menu_controller_->owner()->GetNativeWindow();
aura::Window* target_window = static_cast<aura::Window*>(
static_cast<ui::EventTarget*>(menu_window->GetRootWindow())->
GetEventTargeter()->FindTargetForEvent(menu_window,
ui_event.get()));
// TODO(flackr): The event shouldn't be handled if target_window is not
// menu_window, however the event targeter does not properly target the
// open menu. For now, we allow targeters to prevent handling by the menu.
if (!target_window)
should_process_event = false;
}
if (menu_controller_->exit_type() == MenuController::EXIT_ALL || if (menu_controller_->exit_type() == MenuController::EXIT_ALL ||
menu_controller_->exit_type() == MenuController::EXIT_DESTROYED) { menu_controller_->exit_type() == MenuController::EXIT_DESTROYED) {
should_quit = true; should_quit = true;
} else { } else if (should_process_event) {
switch (ui::EventTypeFromNative(event)) { switch (ui::EventTypeFromNative(event)) {
case ui::ET_KEY_PRESSED: { case ui::ET_KEY_PRESSED: {
if (!menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(event))) { if (!menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(event))) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment