Chromium Code Reviews| Index: chrome/browser/chromeos/login/screen_locker.cc |
| diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc |
| index 2d8ccd08ea5d0ed9f4e6899a2e923814298f6266..bfc68c93568947d3f2bee3e4e1c0e63cbe3a7055 100644 |
| --- a/chrome/browser/chromeos/login/screen_locker.cc |
| +++ b/chrome/browser/chromeos/login/screen_locker.cc |
| @@ -4,11 +4,15 @@ |
| #include "chrome/browser/chromeos/login/screen_locker.h" |
| +#include <gdk/gdkx.h> |
| #include <string> |
| #include <vector> |
| +#include <X11/extensions/XTest.h> |
| +#include <X11/keysym.h> |
| #include "app/l10n_util.h" |
| #include "app/resource_bundle.h" |
| +#include "app/x11_util.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram.h" |
| #include "base/message_loop.h" |
| @@ -274,12 +278,13 @@ class GrabWidget : public views::WidgetGtk { |
| views::WidgetGtk::Show(); |
| } |
| - void ClearGrab() { |
| + void ClearGtkGrab() { |
| GtkWidget* current_grab_window; |
| - // Grab gtk input first so that the menu holding grab will close itself. |
| + // Grab gtk input first so that the menu holding gtk grab will |
| + // close itself. |
| gtk_grab_add(window_contents()); |
| - // Make sure there is no grab widget so that gtk simply propagates |
| + // Make sure there is no gtk grab widget so that gtk simply propagates |
| // an event. This is necessary to allow message bubble and password |
| // field, button to process events simultaneously. GTK |
| // maintains grab widgets in a linked-list, so we need to remove |
| @@ -298,6 +303,11 @@ class GrabWidget : public views::WidgetGtk { |
| // grab and the retry count is within a limit, or fails with CHECK. |
| void TryGrabAllInputs(); |
| + // This method tries to steal pointer/keyboard grab from other |
| + // client by sending events that will hopefullly close menus or windows |
|
Daniel Erat
2010/11/17 19:00:55
nit: s/hopefullly/hopefully/
oshima
2010/11/17 19:09:45
Done.
|
| + // that have the grab. |
| + void TryUngrabOtherClients(); |
| + |
| private: |
| virtual void HandleGrabBroke() { |
| // Input should never be stolen from ScreenLocker once it's |
| @@ -322,8 +332,9 @@ class GrabWidget : public views::WidgetGtk { |
| }; |
| void GrabWidget::TryGrabAllInputs() { |
| - ClearGrab(); |
| - |
| + // Grab x server so that we can atomically grab and take |
| + // action when grab fails. |
| + gdk_x11_grab_server(); |
| if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { |
| kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, |
| GDK_CURRENT_TIME); |
| @@ -345,11 +356,14 @@ void GrabWidget::TryGrabAllInputs() { |
| LOG(WARNING) << "Failed to grab inputs. Trying again in " |
| << kRetryGrabIntervalMs << " ms: kbd=" |
| << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; |
| + TryUngrabOtherClients(); |
| + gdk_x11_ungrab_server(); |
| MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), |
| kRetryGrabIntervalMs); |
| } else { |
| + gdk_x11_ungrab_server(); |
| CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_) |
| << "Failed to grab keyboard input:" << kbd_grab_status_; |
| CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) |
| @@ -359,6 +373,54 @@ void GrabWidget::TryGrabAllInputs() { |
| } |
| } |
| +void GrabWidget::TryUngrabOtherClients() { |
| +#if !defined(NDEBUG) |
| + { |
| + int event_base, error_base; |
| + int major, minor; |
| + // Make sure we have XTest extension. |
| + DCHECK(XTestQueryExtension(x11_util::GetXDisplay(), |
| + &event_base, &error_base, |
| + &major, &minor)); |
| + } |
| +#endif |
| + |
| + // The following code is an attempt to grab inputs by closing |
| + // supposedly opened menu. This happens when a plugin has a menu |
| + // opened. |
| + if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED || |
| + mouse_grab_status_ == GDK_GRAB_FROZEN) { |
| + // Successfully grabbed the keyboard, but pointer is still |
| + // grabbed by other client. Another attempt to close supposedly |
| + // opened menu by emulating keypress at the left top corner. |
| + Display* display = x11_util::GetXDisplay(); |
| + Window root, child; |
| + int root_x, root_y, win_x, winy; |
| + unsigned int mask; |
| + XQueryPointer(display, |
| + x11_util::GetX11WindowFromGtkWidget(window_contents()), |
| + &root, &child, &root_x, &root_y, |
| + &win_x, &winy, &mask); |
| + XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime); |
| + XTestFakeButtonEvent(display, 1, True, CurrentTime); |
| + XTestFakeButtonEvent(display, 1, False, CurrentTime); |
| + // Move the pointer back. |
| + XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime); |
| + XFlush(display); |
| + } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED || |
| + kbd_grab_status_ == GDK_GRAB_FROZEN) { |
| + // Successfully grabbed the pointer, but keyboard is still grabbed |
| + // by other client. Another attempt to close supposedly opened |
| + // menu by emulating escape key. Such situation must be very |
| + // rare, but handling this just in case |
| + Display* display = x11_util::GetXDisplay(); |
| + KeyCode escape = XKeysymToKeycode(display, XK_Escape); |
| + XTestFakeKeyEvent(display, escape, True, CurrentTime); |
| + XTestFakeKeyEvent(display, escape, False, CurrentTime); |
| + XFlush(display); |
| + } |
| +} |
| + |
| // BackgroundView for ScreenLocker, which layouts a lock widget in |
| // addition to other background components. |
| class ScreenLockerBackgroundView : public chromeos::BackgroundView { |
| @@ -612,12 +674,16 @@ void ScreenLocker::Init() { |
| lock_window_->SetContentsView(background_view_); |
| lock_window_->Show(); |
| + cast_lock_widget->ClearGtkGrab(); |
| + |
| + // Call this after lock_window_->Show() otherwise the 1st invocation |
|
Daniel Erat
2010/11/17 19:00:55
nit: add a semicolon after "Show()", e.g. "Call th
oshima
2010/11/17 19:09:45
Done.
|
| + // of gdk_xxx_grab() will always fail. |
| cast_lock_widget->TryGrabAllInputs(); |
| // Add the window to its own group so that its grab won't be stolen if |
| // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. |
| // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this |
| - // after calling TryGrabAllInputs(), as want to be in the default window group |
| + // after calling ClearGtkGrab(), as want to be in the default window group |
| // then so we can break any existing GTK grabs. |
| GtkWindowGroup* window_group = gtk_window_group_new(); |
| gtk_window_group_add_window(window_group, |