summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-18 19:35:23 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-18 19:35:23 +0000
commitb1f68a0b0754032311a3ed79164a3e54082660bf (patch)
tree47cd9fe5e4ca2b0ca22c0cdb9203bf9e9c3fea04
parent381cde1f9843cf2f0a69776b35e469ea846f7f6c (diff)
downloadchromium_src-b1f68a0b0754032311a3ed79164a3e54082660bf.zip
chromium_src-b1f68a0b0754032311a3ed79164a3e54082660bf.tar.gz
chromium_src-b1f68a0b0754032311a3ed79164a3e54082660bf.tar.bz2
A workaround to close currently opened menu. Send escape key if screen locker fails to grab input.
* added new xtst target in build/linux/system.gyp BUG=chromium-os:5902 TEST=goto youtube, open flash menu then kick off the screen locker. It used to show spinner and eventually crash. It should close the menu and lock, and will not crash. Review URL: http://codereview.chromium.org/5043002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66662 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--build/linux/system.gyp5
-rw-r--r--chrome/browser/chromeos/login/screen_locker.cc78
2 files changed, 77 insertions, 6 deletions
diff --git a/build/linux/system.gyp b/build/linux/system.gyp
index 59cf486..85f8063 100644
--- a/build/linux/system.gyp
+++ b/build/linux/system.gyp
@@ -46,6 +46,11 @@
'<!@(<(pkg-config) --libs-only-l gtk+-2.0 gthread-2.0)',
],
},
+ }],
+ [ 'chromeos==1', {
+ 'link_settings': {
+ 'libraries': [ '-lXtst' ]
+ }
}]]
},
{
diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc
index 2d8ccd0..666bf8a 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 hopefully close menus or windows
+ // 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
+ // 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,