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 23b4a76fc2143a2afbf67811f0581f831c656828..507f0fe3ffcdbe6d0b513da9bb77649e9d91dc12 100644 |
--- a/chrome/browser/chromeos/login/screen_locker.cc |
+++ b/chrome/browser/chromeos/login/screen_locker.cc |
@@ -18,6 +18,11 @@ |
#include "views/widget/widget_gtk.h" |
namespace { |
+// The maxium times that the screen locker should try to grab input, |
+// and its interval. It has to be able to grab all inputs in 30 seconds, |
+// otherwise chromium process fails and the session is terminated. |
+const int64 kRetryGrabIntervalMs = 1000; |
+const int kGrabFailureLimit = 30; |
// Observer to start ScreenLocker when the screen lock |
class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, |
@@ -69,7 +74,12 @@ ScreenLocker* ScreenLocker::screen_locker_ = NULL; |
// TODO(oshima): catch grab-broke event and quit if it ever happenes. |
class GrabWidget : public views::WidgetGtk { |
public: |
- GrabWidget() : views::WidgetGtk(views::WidgetGtk::TYPE_CHILD) { |
+ GrabWidget() |
+ : views::WidgetGtk(views::WidgetGtk::TYPE_CHILD), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), |
+ grab_failure_count_(0), |
+ kbd_grab_status_(GDK_GRAB_INVALID_TIME), |
+ mouse_grab_status_(GDK_GRAB_INVALID_TIME) { |
} |
virtual void Show() { |
@@ -79,11 +89,34 @@ class GrabWidget : public views::WidgetGtk { |
gtk_grab_remove(current_grab_window); |
DoGrab(); |
- GdkGrabStatus kbd_status = |
- gdk_keyboard_grab(window_contents()->window, FALSE, |
- GDK_CURRENT_TIME); |
- CHECK_EQ(GDK_GRAB_SUCCESS, kbd_status) << "Failed to grab keyboard input"; |
- GdkGrabStatus ptr_status = |
+ |
+ // Now steal all inputs. |
+ TryGrabAllInputs(); |
+ } |
+ |
+ // Try to grab all inputs. It initiates another try if it fails to |
+ // grab and the retry count is within a limit, or fails with CHECK. |
+ void TryGrabAllInputs(); |
+ |
+ private: |
+ ScopedRunnableMethodFactory<GrabWidget> task_factory_; |
+ |
+ // The number times the widget tried to grab all focus. |
+ int grab_failure_count_; |
+ // Status of keyboard and mouse grab. |
+ GdkGrabStatus kbd_grab_status_; |
+ GdkGrabStatus mouse_grab_status_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GrabWidget); |
+}; |
+ |
+ |
+void GrabWidget::TryGrabAllInputs() { |
+ if (kbd_grab_status_ != GDK_GRAB_SUCCESS) |
+ kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, |
+ GDK_CURRENT_TIME); |
+ if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { |
+ mouse_grab_status_ = |
gdk_pointer_grab(window_contents()->window, |
FALSE, |
static_cast<GdkEventMask>( |
@@ -92,12 +125,23 @@ class GrabWidget : public views::WidgetGtk { |
NULL, |
NULL, |
GDK_CURRENT_TIME); |
- CHECK_EQ(GDK_GRAB_SUCCESS, ptr_status) << "Failed to grab pointer input"; |
} |
- |
- private: |
- DISALLOW_COPY_AND_ASSIGN(GrabWidget); |
-}; |
+ if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || |
+ kbd_grab_status_ != GDK_GRAB_SUCCESS) && |
+ grab_failure_count_++ < kGrabFailureLimit) { |
+ DLOG(WARNING) << "Failed to grab inputs. Trying again in 1 second: kbd=" |
+ << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; |
+ MessageLoop::current()->PostDelayedTask( |
+ FROM_HERE, |
+ task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), |
+ kRetryGrabIntervalMs); |
+ } else { |
+ CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_) |
+ << "Failed to grab keyboard input:" << kbd_grab_status_; |
+ CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) |
+ << "Failed to grab pointer input:" << mouse_grab_status_; |
+ } |
+} |
} // namespace |
@@ -182,11 +226,16 @@ void ScreenLocker::EnableInput() { |
// static |
void ScreenLocker::Show() { |
DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); |
- DCHECK(!screen_locker_); |
- gfx::Rect bounds(views::Screen::GetMonitorWorkAreaNearestWindow(NULL)); |
- ScreenLocker* locker = |
- new ScreenLocker(UserManager::Get()->logged_in_user()); |
- locker->Init(bounds); |
+ // TODO(oshima): Currently, PowerManager may send a lock screen event |
+ // even if a screen is locked. Investigate & solve the issue and |
+ // enable this again if it's possible. |
+ // DCHECK(!screen_locker_); |
+ if (!screen_locker_) { |
+ gfx::Rect bounds(views::Screen::GetMonitorWorkAreaNearestWindow(NULL)); |
+ ScreenLocker* locker = |
+ new ScreenLocker(UserManager::Get()->logged_in_user()); |
+ locker->Init(bounds); |
+ } |
// TODO(oshima): Wait for a message from WM to complete the process. |
if (CrosLibrary::Get()->EnsureLoaded()) |
CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted(); |