summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/keyboard_code_conversion_gtk.cc4
-rw-r--r--chrome/browser/browser_keyevents_browsertest.cc62
-rw-r--r--chrome/browser/renderer_host/gtk_im_context_wrapper.cc11
-rw-r--r--chrome/browser/views/frame/browser_view.cc7
-rw-r--r--views/focus/accelerator_handler.h11
-rw-r--r--views/focus/accelerator_handler_gtk.cc107
-rw-r--r--views/focus/accelerator_handler_gtk_unittest.cc8
-rw-r--r--views/focus/focus_manager.cc4
-rw-r--r--views/focus/focus_manager_unittest.cc9
-rw-r--r--views/widget/gtk_views_window.cc13
-rw-r--r--views/widget/widget_gtk.cc84
-rw-r--r--views/widget/widget_gtk.h10
12 files changed, 176 insertions, 154 deletions
diff --git a/base/keyboard_code_conversion_gtk.cc b/base/keyboard_code_conversion_gtk.cc
index 9e6bfd1..83f4197 100644
--- a/base/keyboard_code_conversion_gtk.cc
+++ b/base/keyboard_code_conversion_gtk.cc
@@ -93,6 +93,7 @@ base::KeyboardCode WindowsKeyCodeForGdkKeyCode(int keycode) {
case GDK_Control_R:
return VKEY_CONTROL; // (11) CTRL key
case GDK_Menu:
+ return VKEY_APPS; // (5D) Applications key (Natural keyboard)
case GDK_Alt_L:
case GDK_Alt_R:
return VKEY_MENU; // (12) ALT key
@@ -265,7 +266,6 @@ base::KeyboardCode WindowsKeyCodeForGdkKeyCode(int keycode) {
case GDK_Meta_R:
case GDK_Super_R:
return VKEY_RWIN; // (5C) Right Windows key (Natural keyboard)
- // VKEY_APPS (5D) Applications key (Natural keyboard)
// VKEY_SLEEP (5F) Computer Sleep key
// VKEY_SEPARATOR (6C) Separator key
// VKEY_SUBTRACT (6D) Subtract key
@@ -456,6 +456,8 @@ int GdkKeyCodeForWindowsKeyCode(base::KeyboardCode keycode, bool shift) {
case VKEY_CONTROL:
return GDK_Control_L;
case VKEY_MENU:
+ return GDK_Alt_L;
+ case VKEY_APPS:
return GDK_Menu;
case VKEY_PAUSE:
diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc
index e943bef..0c4bd37db 100644
--- a/chrome/browser/browser_keyevents_browsertest.cc
+++ b/chrome/browser/browser_keyevents_browsertest.cc
@@ -532,14 +532,7 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, CommandKeyEvents) {
}
#endif
-#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX)
-// See http://crbug.com/40037 for details.
-#define MAYBE_AccessKeys DISABLED_AccessKeys
-#else
-#define MAYBE_AccessKeys AccessKeys
-#endif
-
-IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_AccessKeys) {
+IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, AccessKeys) {
#if defined(OS_MACOSX)
// On Mac, access keys use ctrl+alt modifiers.
static const KeyEventTestData kTestAccessA = {
@@ -891,4 +884,57 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, PageUpDownKeys) {
EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"A", L""));
}
+#if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
+IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, FocusMenuBarByAltKey) {
+ static const KeyEventTestData kTestAltKey = {
+ base::VKEY_MENU, false, false, false, false,
+ false, false, false, false, 2,
+ { "D 18 0 false false true false",
+ "U 18 0 false false true false" }
+ };
+
+ static const KeyEventTestData kTestAltKeySuppress = {
+ base::VKEY_MENU, false, false, false, false,
+ true, false, false, false, 2,
+ { "D 18 0 false false true false",
+ "U 18 0 false false true false" }
+ };
+
+ static const KeyEventTestData kTestCtrlAltKey = {
+ base::VKEY_MENU, true, false, false, false,
+ false, false, false, false, 4,
+ { "D 17 0 true false false false",
+ "D 18 0 true false true false",
+ "U 18 0 true false true false",
+ "U 17 0 true false false false" }
+ };
+
+ net::HTTPTestServer* server = StartHTTPServer();
+ ASSERT_TRUE(server);
+
+ BringBrowserWindowToFront();
+ GURL url = server->TestServerPage(kTestingPage);
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
+ ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
+
+ int tab_index = browser()->selected_index();
+ // Press and release Alt key to focus wrench menu button.
+ EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKey));
+ EXPECT_TRUE(IsViewFocused(VIEW_ID_APP_MENU));
+
+ ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
+ ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
+
+ // Alt key can be suppressed.
+ EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKeySuppress));
+ ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
+
+ // Ctrl+Alt should have no effect.
+ EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlAltKey));
+ ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
+}
+#endif
+
} // namespace
diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
index 8aa7f8c..4ac138b 100644
--- a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
+++ b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
@@ -132,6 +132,13 @@ void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
NativeWebKeyboardEvent wke(event);
+ // If the key event was handled by the input method, then we need to prevent
+ // RenderView::UnhandledKeyboardEvent() from processing it.
+ // Otherwise unexpected result may occur. For example if it's a
+ // Backspace key event, the browser may go back to previous page.
+ if (filtered)
+ wke.skip_in_browser = true;
+
// Send filtered keydown event before sending IME result.
if (event->type == GDK_KEY_PRESS && filtered)
ProcessFilteredKeyPressEvent(&wke);
@@ -321,10 +328,6 @@ void GtkIMContextWrapper::ProcessFilteredKeyPressEvent(
// keyidentifier must be updated accordingly, otherwise this key event may
// still be processed by webkit.
wke->setKeyIdentifierFromWindowsKeyCode();
- // Prevent RenderView::UnhandledKeyboardEvent() from processing it.
- // Otherwise unexpected result may occur. For example if it's a
- // Backspace key event, the browser may go back to previous page.
- wke->skip_in_browser = true;
}
host_view_->ForwardKeyboardEvent(*wke);
}
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 0e11eaa..d8ab622 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -73,6 +73,7 @@
#elif defined(OS_LINUX)
#include "chrome/browser/views/accelerator_table_gtk.h"
#include "views/window/hit_test.h"
+#include "views/window/window_gtk.h"
#endif
using base::TimeDelta;
@@ -1238,8 +1239,14 @@ bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
}
void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
+#if defined(OS_LINUX)
+ views::Window* window = GetWidget()->GetWindow();
+ if (window && event.os_event && !event.skip_in_browser)
+ static_cast<views::WindowGtk*>(window)->HandleKeyboardEvent(event.os_event);
+#else
unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
GetFocusManager());
+#endif
}
// TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always
diff --git a/views/focus/accelerator_handler.h b/views/focus/accelerator_handler.h
index 6140d364..3726d96 100644
--- a/views/focus/accelerator_handler.h
+++ b/views/focus/accelerator_handler.h
@@ -35,17 +35,6 @@ class AcceleratorHandler : public MessageLoopForUI::Dispatcher {
#if defined(OS_WIN)
// The keys currently pressed and consumed by the FocusManager.
std::set<WPARAM> pressed_keys_;
-#else // OS_LINUX
- // The set of hardware keycodes that are currently pressed. We use this
- // to keep track of whether or not a key is being pressed in isolation
- // or not. We must use hardware keycodes because higher-level keycodes
- // may change between press and release depending on what modifier keys
- // are pressed in the interim.
- std::set<int> pressed_hardware_keys_;
- // The set of vkey codes that were consumed by the FocusManager.
- std::set<int> consumed_vkeys_;
- // True if only the menu key (Alt key) was pressed, and no other keys.
- bool only_menu_pressed_;
#endif
DISALLOW_COPY_AND_ASSIGN(AcceleratorHandler);
diff --git a/views/focus/accelerator_handler_gtk.cc b/views/focus/accelerator_handler_gtk.cc
index ee91906..e0af89b 100644
--- a/views/focus/accelerator_handler_gtk.cc
+++ b/views/focus/accelerator_handler_gtk.cc
@@ -13,112 +13,11 @@
namespace views {
-AcceleratorHandler::AcceleratorHandler() : only_menu_pressed_(false) {}
+AcceleratorHandler::AcceleratorHandler() {}
bool AcceleratorHandler::Dispatch(GdkEvent* event) {
- // When focus changes, we may not see a full key-press / key-release
- // pair, so clear the set of keys we think were pressed.
- if (event->type == GDK_FOCUS_CHANGE) {
- pressed_hardware_keys_.clear();
- consumed_vkeys_.clear();
- only_menu_pressed_ = false;
- }
-
- // Skip accelerator handling for non key events.
- bool skip = event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE;
-
- // Skip if there is a grabbed widget and it is not a window. This is because
- // of the following two reasons:
- // 1. Widget such as pop up from GtkComboBox is a grabbed widget and use ESC
- // as its accelerator key to dismiss itself. We will break Gtk's code if
- // we eat this key. See http://crosbug.com/2355
- // 2. Modal dialogs in Gtk are grabbed windows and we want to have our
- // accelerator key processing in this case. See http://crogbug.com/3701
- if (!skip) {
- GtkWidget* grabbed_widget = gtk_grab_get_current();
- skip = grabbed_widget && !GTK_IS_WINDOW(grabbed_widget);
- }
-
- if (skip) {
- // Let Gtk processes the event.
- gtk_main_do_event(event);
- return true;
- }
-
- GdkEventKey* key_event = reinterpret_cast<GdkEventKey*>(event);
- // Let's retrieve the focus manager for the GdkWindow.
- GdkWindow* window = gdk_window_get_toplevel(key_event->window);
- gpointer ptr;
- gdk_window_get_user_data(window, &ptr);
- if (!ptr && !gdk_window_is_visible(window)) {
- // The window is destroyed while we're handling key events.
- gtk_main_do_event(event);
- return true;
- }
-
- DCHECK(ptr);
-
- // The top-level window or window widget is expected to always be associated
- // with the top-level gtk widget.
- WidgetGtk* widget =
- WidgetGtk::GetViewForNative(reinterpret_cast<GtkWidget*>(ptr));
- if (!widget) {
- // During dnd we get events for windows we don't control (such as the
- // window being dragged).
- gtk_main_do_event(event);
- return true;
- }
- FocusManager* focus_manager = widget->GetFocusManager();
- if (!focus_manager) {
- NOTREACHED();
- return true;
- }
-
- KeyEvent view_key_event(key_event);
- int key_code = view_key_event.GetKeyCode();
- int hardware_keycode = key_event->hardware_keycode;
- if (event->type == GDK_KEY_PRESS) {
- pressed_hardware_keys_.insert(hardware_keycode);
-
- // If it's the Alt key, don't send it to the focus manager until release
- // (to handle focusing the menu bar).
- if (key_code == base::VKEY_MENU && pressed_hardware_keys_.size() == 1) {
- only_menu_pressed_ = true;
- return true;
- }
-
- if (pressed_hardware_keys_.size() != 1)
- only_menu_pressed_ = false;
-
- // FocusManager::OnKeyEvent will return false if this message has been
- // consumed and should not be propagated further.
- if (!focus_manager->OnKeyEvent(view_key_event)) {
- consumed_vkeys_.insert(key_code);
- return true;
- }
- }
-
- if (event->type == GDK_KEY_RELEASE) {
- pressed_hardware_keys_.erase(hardware_keycode);
-
- // Make sure to filter out the key release for a key press consumed
- // as an accelerator, to avoid calling gtk_main_do_event with an
- // unpaired key release.
- if (consumed_vkeys_.find(key_code) != consumed_vkeys_.end()) {
- consumed_vkeys_.erase(key_code);
- return true;
- }
-
- // Special case: the Alt key can trigger an accelerator on release
- // rather than on press, but only if no other keys were pressed.
- if (key_code == base::VKEY_MENU && only_menu_pressed_) {
- Accelerator accelerator(base::VKEY_MENU, false, false, false);
- focus_manager->ProcessAccelerator(accelerator);
- return true;
- }
- }
-
- // Pass the event to gtk if we didn't consume it above.
+ // The logic for handling keyboard accelerators has been moved into
+ // WidgetGtk::OnKeyEvent handler (views/widget/widget_gtk.cc).
gtk_main_do_event(event);
return true;
}
diff --git a/views/focus/accelerator_handler_gtk_unittest.cc b/views/focus/accelerator_handler_gtk_unittest.cc
index ef05312..78e9110 100644
--- a/views/focus/accelerator_handler_gtk_unittest.cc
+++ b/views/focus/accelerator_handler_gtk_unittest.cc
@@ -99,7 +99,7 @@ TEST_F(AcceleratorHandlerGtkTest, TestHomepageAccelerator) {
ASSERT_FALSE(menu_pressed_);
ASSERT_FALSE(home_pressed_);
- evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0);
+ evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
ASSERT_FALSE(menu_pressed_);
@@ -113,7 +113,7 @@ TEST_F(AcceleratorHandlerGtkTest, TestHomepageAccelerator) {
evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Home, GDK_MOD1_MASK);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
- evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0);
+ evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
ASSERT_FALSE(menu_pressed_);
@@ -127,12 +127,12 @@ TEST_F(AcceleratorHandlerGtkTest, TestMenuAccelerator) {
ASSERT_FALSE(menu_pressed_);
- evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0);
+ evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
ASSERT_FALSE(menu_pressed_);
- evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0);
+ evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
ASSERT_TRUE(menu_pressed_);
diff --git a/views/focus/focus_manager.cc b/views/focus/focus_manager.cc
index 5a94c95..f0f5f99 100644
--- a/views/focus/focus_manager.cc
+++ b/views/focus/focus_manager.cc
@@ -81,9 +81,13 @@ FocusManager::~FocusManager() {
}
bool FocusManager::OnKeyEvent(const KeyEvent& event) {
+#if defined(OS_WIN)
// If the focused view wants to process the key event as is, let it be.
+ // On Linux we always dispatch key events to the focused view first, so
+ // we should not do this check here. See also WidgetGtk::OnKeyEvent().
if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event))
return true;
+#endif
// Intercept Tab related messages for focus traversal.
// Note that we don't do focus traversal if the root window is not part of the
diff --git a/views/focus/focus_manager_unittest.cc b/views/focus/focus_manager_unittest.cc
index 955a83a..f786b1d 100644
--- a/views/focus/focus_manager_unittest.cc
+++ b/views/focus/focus_manager_unittest.cc
@@ -1485,6 +1485,10 @@ class MessageTrackingView : public View {
DISALLOW_COPY_AND_ASSIGN(MessageTrackingView);
};
+#if defined(OS_WIN)
+// This test is now Windows only. Linux Views port does not handle accelerator
+// keys in AcceleratorHandler anymore. The logic has been moved into
+// WidgetGtk::OnKeyEvent().
// Tests that the keyup messages are eaten for accelerators.
TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) {
FocusManager* focus_manager = GetFocusManager();
@@ -1553,15 +1557,12 @@ TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) {
PostKeyUp(base::VKEY_0);
MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
MessageLoopForUI::current()->Run(&accelerator_handler);
-#if defined(OS_WIN)
- // Linux eats only last accelerator's release event.
- // See http://crbug.com/23383 for details.
EXPECT_TRUE(mtv->keys_pressed().empty());
EXPECT_TRUE(mtv->keys_released().empty());
-#endif
EXPECT_TRUE(mtv->accelerator_pressed());
mtv->Reset();
}
+#endif
#if defined(OS_WIN)
// Test that the focus manager is created successfully for the first view
diff --git a/views/widget/gtk_views_window.cc b/views/widget/gtk_views_window.cc
index fb3657d..2eff8f1 100644
--- a/views/widget/gtk_views_window.cc
+++ b/views/widget/gtk_views_window.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <gtk/gtk.h>
+#include "views/event.h"
#include "views/widget/gtk_views_window.h"
#include "views/focus/focus_manager.h"
@@ -9,15 +11,16 @@ G_BEGIN_DECLS
G_DEFINE_TYPE(GtkViewsWindow, gtk_views_window, GTK_TYPE_WINDOW)
-static void gtk_views_window_move_focus(GtkWindow *window,
+static void gtk_views_window_move_focus(GtkWindow* window,
GtkDirectionType dir) {
views::FocusManager* focus_manager =
views::FocusManager::GetFocusManagerForNativeWindow(window);
if (focus_manager) {
- // We only support tab traversing by tab keys, so just ignore all other
- // cases silently.
- if (dir == GTK_DIR_TAB_BACKWARD || dir == GTK_DIR_TAB_FORWARD)
- focus_manager->AdvanceFocus(dir == GTK_DIR_TAB_BACKWARD);
+ GdkEvent* key = gtk_get_current_event();
+ if (key && key->type == GDK_KEY_PRESS) {
+ views::KeyEvent key_event(reinterpret_cast<GdkEventKey*>(key));
+ focus_manager->OnKeyEvent(key_event);
+ }
} else if (GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus) {
GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus(window, dir);
}
diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc
index de4ad9a..93fdcdc 100644
--- a/views/widget/widget_gtk.cc
+++ b/views/widget/widget_gtk.cc
@@ -246,7 +246,8 @@ WidgetGtk::WidgetGtk(Type type)
has_focus_(false),
delegate_(NULL),
always_on_top_(false),
- is_double_buffered_(false) {
+ is_double_buffered_(false),
+ should_handle_menu_key_release_(false) {
static bool installed_message_loop_observer = false;
if (!installed_message_loop_observer) {
installed_message_loop_observer = true;
@@ -546,9 +547,9 @@ void WidgetGtk::Init(GtkWidget* parent,
// See views::Views::Focus and views::FocusManager::ClearNativeFocus
// for more details.
g_signal_connect(widget_, "key_press_event",
- G_CALLBACK(&OnKeyPressThunk), this);
+ G_CALLBACK(&OnKeyEventThunk), this);
g_signal_connect(widget_, "key_release_event",
- G_CALLBACK(&OnKeyReleaseThunk), this);
+ G_CALLBACK(&OnKeyEventThunk), this);
// Drag and drop.
gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0),
@@ -861,6 +862,38 @@ void WidgetGtk::ClearNativeFocus() {
gtk_window_set_focus(GTK_WINDOW(GetNativeView()), NULL);
}
+bool WidgetGtk::HandleKeyboardEvent(GdkEventKey* event) {
+ if (!focus_manager_)
+ return false;
+
+ KeyEvent key(event);
+ int key_code = key.GetKeyCode();
+ bool handled = false;
+
+ // Always reset |should_handle_menu_key_release_| unless we are handling a
+ // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only
+ // be activated when handling a VKEY_MENU key release event which is preceded
+ // by an unhandled VKEY_MENU key press event.
+ if (key_code != base::VKEY_MENU || event->type != GDK_KEY_RELEASE)
+ should_handle_menu_key_release_ = false;
+
+ if (event->type == GDK_KEY_PRESS) {
+ // VKEY_MENU is triggered by key release event.
+ if (key_code != base::VKEY_MENU)
+ handled = focus_manager_->OnKeyEvent(key);
+ else
+ should_handle_menu_key_release_ = true;
+ } else if (key_code == base::VKEY_MENU && should_handle_menu_key_release_ &&
+ (key.GetFlags() & ~Event::EF_ALT_DOWN) == 0) {
+ // Trigger VKEY_MENU when only this key is pressed and released, and both
+ // press and release events are not handled by others.
+ Accelerator accelerator(base::VKEY_MENU, false, false, false);
+ handled = focus_manager_->ProcessAccelerator(accelerator);
+ }
+
+ return handled;
+}
+
////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, protected:
@@ -1088,6 +1121,8 @@ gboolean WidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) {
return false; // This is the second focus-in event in a row, ignore it.
has_focus_ = true;
+ should_handle_menu_key_release_ = false;
+
if (type_ == TYPE_CHILD)
return false;
@@ -1114,14 +1149,41 @@ gboolean WidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) {
return false;
}
-gboolean WidgetGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) {
- KeyEvent key_event(event);
- return root_view_->ProcessKeyEvent(key_event);
-}
-
-gboolean WidgetGtk::OnKeyRelease(GtkWidget* widget, GdkEventKey* event) {
- KeyEvent key_event(event);
- return root_view_->ProcessKeyEvent(key_event);
+gboolean WidgetGtk::OnKeyEvent(GtkWidget* widget, GdkEventKey* event) {
+ KeyEvent key(event);
+
+ // Always reset |should_handle_menu_key_release_| unless we are handling a
+ // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only
+ // be activated when handling a VKEY_MENU key release event which is preceded
+ // by an unhandled VKEY_MENU key press event. See also HandleKeyboardEvent().
+ if (key.GetKeyCode() != base::VKEY_MENU || event->type != GDK_KEY_RELEASE)
+ should_handle_menu_key_release_ = false;
+
+ bool handled = false;
+
+ // Dispatch the key event to View hierarchy first.
+ handled = root_view_->ProcessKeyEvent(key);
+
+ // Dispatch the key event to native GtkWidget hierarchy.
+ // To prevent GtkWindow from handling the key event as a keybinding, we need
+ // to bypass GtkWindow's default key event handler and dispatch the event
+ // here.
+ if (!handled && GTK_IS_WINDOW(widget))
+ handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), event);
+
+ // On Linux, in order to handle VKEY_MENU (Alt) accelerator key correctly and
+ // avoid issues like: http://crbug.com/40966 and http://crbug.com/49701, we
+ // should only send the key event to the focus manager if it's not handled by
+ // any View or native GtkWidget.
+ // The flow is different when the focus is in a RenderWidgetHostViewGtk, which
+ // always consumes the key event and send it back to us later by calling
+ // HandleKeyboardEvent() directly, if it's not handled by webkit.
+ if (!handled)
+ handled = HandleKeyboardEvent(event);
+
+ // Always return true for toplevel window to prevents GtkWindow's default key
+ // event handler.
+ return GTK_IS_WINDOW(widget) ? true : handled;
}
gboolean WidgetGtk::OnQueryTooltip(GtkWidget* widget,
diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h
index d010b03..7e1bdd1 100644
--- a/views/widget/widget_gtk.h
+++ b/views/widget/widget_gtk.h
@@ -198,6 +198,10 @@ class WidgetGtk
// Clears the focus on the native widget having the focus.
virtual void ClearNativeFocus();
+ // Handles a keyboard event by sending it to our focus manager.
+ // Returns true if it's handled by the focus manager.
+ bool HandleKeyboardEvent(GdkEventKey* event);
+
protected:
// If widget containes another widget, translates event coordinates to the
// contained widget's coordinates, else returns original event coordinates.
@@ -247,8 +251,7 @@ class WidgetGtk
CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnButtonRelease, GdkEventButton*);
CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusIn, GdkEventFocus*);
CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusOut, GdkEventFocus*);
- CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyPress, GdkEventKey*);
- CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyRelease, GdkEventKey*);
+ CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyEvent, GdkEventKey*);
CHROMEGTK_CALLBACK_4(WidgetGtk, gboolean, OnQueryTooltip,
gint, gint, gboolean, GtkTooltip*);
CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnScroll, GdkEventScroll*);
@@ -436,6 +439,9 @@ class WidgetGtk
// This is false by default.
bool is_double_buffered_;
+ // Indicates if we should handle the upcoming Alt key release event.
+ bool should_handle_menu_key_release_;
+
DISALLOW_COPY_AND_ASSIGN(WidgetGtk);
};