OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/login/screen_locker.h" | 5 #include "chrome/browser/chromeos/login/screen_locker.h" |
6 | 6 |
7 #include <gdk/gdkx.h> | |
7 #include <string> | 8 #include <string> |
8 #include <vector> | 9 #include <vector> |
10 #include <X11/extensions/XTest.h> | |
11 #include <X11/keysym.h> | |
9 | 12 |
10 #include "app/l10n_util.h" | 13 #include "app/l10n_util.h" |
11 #include "app/resource_bundle.h" | 14 #include "app/resource_bundle.h" |
15 #include "app/x11_util.h" | |
12 #include "base/command_line.h" | 16 #include "base/command_line.h" |
13 #include "base/metrics/histogram.h" | 17 #include "base/metrics/histogram.h" |
14 #include "base/message_loop.h" | 18 #include "base/message_loop.h" |
15 #include "base/singleton.h" | 19 #include "base/singleton.h" |
16 #include "base/string_util.h" | 20 #include "base/string_util.h" |
17 #include "base/timer.h" | 21 #include "base/timer.h" |
18 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
19 #include "chrome/browser/browser_list.h" | 23 #include "chrome/browser/browser_list.h" |
20 #include "chrome/browser/browser_thread.h" | 24 #include "chrome/browser/browser_thread.h" |
21 #include "chrome/browser/browser_window.h" | 25 #include "chrome/browser/browser_window.h" |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
267 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), | 271 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), |
268 grab_failure_count_(0), | 272 grab_failure_count_(0), |
269 kbd_grab_status_(GDK_GRAB_INVALID_TIME), | 273 kbd_grab_status_(GDK_GRAB_INVALID_TIME), |
270 mouse_grab_status_(GDK_GRAB_INVALID_TIME) { | 274 mouse_grab_status_(GDK_GRAB_INVALID_TIME) { |
271 } | 275 } |
272 | 276 |
273 virtual void Show() { | 277 virtual void Show() { |
274 views::WidgetGtk::Show(); | 278 views::WidgetGtk::Show(); |
275 } | 279 } |
276 | 280 |
277 void ClearGrab() { | 281 void ClearGtkGrab() { |
278 GtkWidget* current_grab_window; | 282 GtkWidget* current_grab_window; |
279 // Grab gtk input first so that the menu holding grab will close itself. | 283 // Grab gtk input first so that the menu holding gtk grab will |
284 // close itself. | |
280 gtk_grab_add(window_contents()); | 285 gtk_grab_add(window_contents()); |
281 | 286 |
282 // Make sure there is no grab widget so that gtk simply propagates | 287 // Make sure there is no gtk grab widget so that gtk simply propagates |
283 // an event. This is necessary to allow message bubble and password | 288 // an event. This is necessary to allow message bubble and password |
284 // field, button to process events simultaneously. GTK | 289 // field, button to process events simultaneously. GTK |
285 // maintains grab widgets in a linked-list, so we need to remove | 290 // maintains grab widgets in a linked-list, so we need to remove |
286 // until it's empty. | 291 // until it's empty. |
287 while ((current_grab_window = gtk_grab_get_current()) != NULL) | 292 while ((current_grab_window = gtk_grab_get_current()) != NULL) |
288 gtk_grab_remove(current_grab_window); | 293 gtk_grab_remove(current_grab_window); |
289 } | 294 } |
290 | 295 |
291 virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) { | 296 virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) { |
292 WidgetGtk::OnButtonPress(widget, event); | 297 WidgetGtk::OnButtonPress(widget, event); |
293 // Never propagate event to parent. | 298 // Never propagate event to parent. |
294 return true; | 299 return true; |
295 } | 300 } |
296 | 301 |
297 // Try to grab all inputs. It initiates another try if it fails to | 302 // Try to grab all inputs. It initiates another try if it fails to |
298 // grab and the retry count is within a limit, or fails with CHECK. | 303 // grab and the retry count is within a limit, or fails with CHECK. |
299 void TryGrabAllInputs(); | 304 void TryGrabAllInputs(); |
300 | 305 |
306 // This method tries to steal pointer/keyboard grab from other | |
307 // client by sending events that will hopefullly close menu or window | |
Daniel Erat
2010/11/17 01:24:23
nit: s/menu or window/menus or windows/
oshima
2010/11/17 18:15:38
Done.
| |
308 // that have the grab. | |
309 void TryUngrabOtherClients(); | |
310 | |
301 private: | 311 private: |
302 virtual void HandleGrabBroke() { | 312 virtual void HandleGrabBroke() { |
303 // Input should never be stolen from ScreenLocker once it's | 313 // Input should never be stolen from ScreenLocker once it's |
304 // grabbed. If this happens, it's a bug and has to be fixed. We | 314 // grabbed. If this happens, it's a bug and has to be fixed. We |
305 // let chrome crash to get a crash report and dump, and | 315 // let chrome crash to get a crash report and dump, and |
306 // SessionManager will terminate the session to logout. | 316 // SessionManager will terminate the session to logout. |
307 CHECK(kbd_grab_status_ != GDK_GRAB_SUCCESS || | 317 CHECK(kbd_grab_status_ != GDK_GRAB_SUCCESS || |
308 mouse_grab_status_ != GDK_GRAB_SUCCESS) | 318 mouse_grab_status_ != GDK_GRAB_SUCCESS) |
309 << "Grab Broke. quitting"; | 319 << "Grab Broke. quitting"; |
310 } | 320 } |
311 | 321 |
312 chromeos::ScreenLocker* screen_locker_; | 322 chromeos::ScreenLocker* screen_locker_; |
313 ScopedRunnableMethodFactory<GrabWidget> task_factory_; | 323 ScopedRunnableMethodFactory<GrabWidget> task_factory_; |
314 | 324 |
315 // The number times the widget tried to grab all focus. | 325 // The number times the widget tried to grab all focus. |
316 int grab_failure_count_; | 326 int grab_failure_count_; |
317 // Status of keyboard and mouse grab. | 327 // Status of keyboard and mouse grab. |
318 GdkGrabStatus kbd_grab_status_; | 328 GdkGrabStatus kbd_grab_status_; |
319 GdkGrabStatus mouse_grab_status_; | 329 GdkGrabStatus mouse_grab_status_; |
320 | 330 |
321 DISALLOW_COPY_AND_ASSIGN(GrabWidget); | 331 DISALLOW_COPY_AND_ASSIGN(GrabWidget); |
322 }; | 332 }; |
323 | 333 |
324 void GrabWidget::TryGrabAllInputs() { | 334 void GrabWidget::TryGrabAllInputs() { |
325 ClearGrab(); | |
326 | |
327 if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { | 335 if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { |
328 kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, | 336 kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, |
329 GDK_CURRENT_TIME); | 337 GDK_CURRENT_TIME); |
330 } | 338 } |
339 // Grab x server so that we can atomically grab and take | |
340 // action when grab fails. | |
341 gdk_x11_grab_server(); | |
Daniel Erat
2010/11/17 01:24:23
Do you mean for this to appear before the call to
oshima
2010/11/17 18:15:38
Doh, yes, that's what I meant.
| |
331 if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { | 342 if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { |
332 mouse_grab_status_ = | 343 mouse_grab_status_ = |
333 gdk_pointer_grab(window_contents()->window, | 344 gdk_pointer_grab(window_contents()->window, |
334 FALSE, | 345 FALSE, |
335 static_cast<GdkEventMask>( | 346 static_cast<GdkEventMask>( |
336 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | | 347 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | |
337 GDK_POINTER_MOTION_MASK), | 348 GDK_POINTER_MOTION_MASK), |
338 NULL, | 349 NULL, |
339 NULL, | 350 NULL, |
340 GDK_CURRENT_TIME); | 351 GDK_CURRENT_TIME); |
341 } | 352 } |
342 if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || | 353 if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || |
343 mouse_grab_status_ != GDK_GRAB_SUCCESS) && | 354 mouse_grab_status_ != GDK_GRAB_SUCCESS) && |
344 grab_failure_count_++ < kMaxGrabFailures) { | 355 grab_failure_count_++ < kMaxGrabFailures) { |
345 LOG(WARNING) << "Failed to grab inputs. Trying again in " | 356 LOG(WARNING) << "Failed to grab inputs. Trying again in " |
346 << kRetryGrabIntervalMs << " ms: kbd=" | 357 << kRetryGrabIntervalMs << " ms: kbd=" |
347 << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; | 358 << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; |
359 TryUngrabOtherClients(); | |
348 MessageLoop::current()->PostDelayedTask( | 360 MessageLoop::current()->PostDelayedTask( |
349 FROM_HERE, | 361 FROM_HERE, |
350 task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), | 362 task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), |
351 kRetryGrabIntervalMs); | 363 kRetryGrabIntervalMs); |
352 } else { | 364 } else { |
353 CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_) | 365 CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_) |
354 << "Failed to grab keyboard input:" << kbd_grab_status_; | 366 << "Failed to grab keyboard input:" << kbd_grab_status_; |
355 CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) | 367 CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) |
356 << "Failed to grab pointer input:" << mouse_grab_status_; | 368 << "Failed to grab pointer input:" << mouse_grab_status_; |
357 DVLOG(1) << "Grab Success"; | 369 DVLOG(1) << "Grab Success"; |
358 screen_locker_->OnGrabInputs(); | 370 screen_locker_->OnGrabInputs(); |
359 } | 371 } |
372 gdk_x11_ungrab_server(); | |
Daniel Erat
2010/11/17 01:24:23
Server grabs block all other clients from doing an
oshima
2010/11/17 18:15:38
Done.
| |
373 } | |
374 | |
375 void GrabWidget::TryUngrabOtherClients() { | |
376 #if !defined(NDEBUG) | |
377 { | |
378 int event_base, error_base; | |
379 int major, minor; | |
380 // Make sure we have XTest extension. | |
381 DCHECK(XTestQueryExtension(x11_util::GetXDisplay(), | |
382 &event_base, &error_base, | |
383 &major, &minor)); | |
384 } | |
385 #endif | |
386 | |
387 // The following code is an attempt to grab inputs by closing | |
388 // supposedly opened menu. This happens when a plugin has a menu | |
389 // opened. | |
390 if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED || | |
391 mouse_grab_status_ == GDK_GRAB_FROZEN) { | |
392 // Successfully grabbed the keyboard, but pointer is still | |
393 // grabbed by other client. Another attempt to close supposedly | |
394 // opened menu by emulating keypress at the left top corner. | |
395 Display* display = x11_util::GetXDisplay(); | |
396 Window root, child; | |
397 int root_x, root_y, win_x, winy; | |
398 unsigned int mask; | |
399 XQueryPointer(display, | |
400 x11_util::GetX11WindowFromGtkWidget(window_contents()), | |
401 &root, &child, &root_x, &root_y, | |
402 &win_x, &winy, &mask); | |
403 XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime); | |
404 XTestFakeButtonEvent(display, 1, True, CurrentTime); | |
405 XTestFakeButtonEvent(display, 1, False, CurrentTime); | |
406 // Move the pointer back. | |
407 XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime); | |
408 XFlush(display); | |
409 } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED || | |
410 kbd_grab_status_ == GDK_GRAB_FROZEN) { | |
411 // Successfully grabbed the pointer, but keyboard is still grabbed | |
412 // by other client. Another attempt to close supposedly opened | |
413 // menu by emulating escape key. Such situation must be very | |
414 // rare, but handling this just in case | |
415 Display* display = x11_util::GetXDisplay(); | |
416 KeyCode escape = XKeysymToKeycode(display, XK_Escape); | |
417 XTestFakeKeyEvent(display, escape, True, CurrentTime); | |
418 XTestFakeKeyEvent(display, escape, False, CurrentTime); | |
419 XFlush(display); | |
420 } | |
360 } | 421 } |
361 | 422 |
362 // BackgroundView for ScreenLocker, which layouts a lock widget in | 423 // BackgroundView for ScreenLocker, which layouts a lock widget in |
363 // addition to other background components. | 424 // addition to other background components. |
364 class ScreenLockerBackgroundView : public chromeos::BackgroundView { | 425 class ScreenLockerBackgroundView : public chromeos::BackgroundView { |
365 public: | 426 public: |
366 ScreenLockerBackgroundView(views::WidgetGtk* lock_widget, | 427 ScreenLockerBackgroundView(views::WidgetGtk* lock_widget, |
367 views::View* screen_lock_view) | 428 views::View* screen_lock_view) |
368 : lock_widget_(lock_widget), | 429 : lock_widget_(lock_widget), |
369 screen_lock_view_(screen_lock_view) { | 430 screen_lock_view_(screen_lock_view) { |
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
605 | 666 |
606 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); | 667 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); |
607 WmIpc::instance()->SetWindowType( | 668 WmIpc::instance()->SetWindowType( |
608 lock_window_->GetNativeView(), | 669 lock_window_->GetNativeView(), |
609 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, | 670 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, |
610 NULL); | 671 NULL); |
611 | 672 |
612 lock_window_->SetContentsView(background_view_); | 673 lock_window_->SetContentsView(background_view_); |
613 lock_window_->Show(); | 674 lock_window_->Show(); |
614 | 675 |
676 cast_lock_widget->ClearGtkGrab(); | |
677 | |
678 // Call this after lock_window_->Show() otherwise the 1st invocation | |
679 // of gdk_xxx_grab() will always fail. | |
615 cast_lock_widget->TryGrabAllInputs(); | 680 cast_lock_widget->TryGrabAllInputs(); |
616 | 681 |
617 // Add the window to its own group so that its grab won't be stolen if | 682 // Add the window to its own group so that its grab won't be stolen if |
618 // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. | 683 // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. |
619 // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this | 684 // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this |
620 // after calling TryGrabAllInputs(), as want to be in the default window group | 685 // after calling ClearGtkGrab(), as want to be in the default window group |
621 // then so we can break any existing GTK grabs. | 686 // then so we can break any existing GTK grabs. |
622 GtkWindowGroup* window_group = gtk_window_group_new(); | 687 GtkWindowGroup* window_group = gtk_window_group_new(); |
623 gtk_window_group_add_window(window_group, | 688 gtk_window_group_add_window(window_group, |
624 GTK_WINDOW(lock_window_->GetNativeView())); | 689 GTK_WINDOW(lock_window_->GetNativeView())); |
625 g_object_unref(window_group); | 690 g_object_unref(window_group); |
626 | 691 |
627 // Don't let X draw default background, which was causing flash on | 692 // Don't let X draw default background, which was causing flash on |
628 // resume. | 693 // resume. |
629 gdk_window_set_back_pixmap(lock_window_->GetNativeView()->window, | 694 gdk_window_set_back_pixmap(lock_window_->GetNativeView()->window, |
630 NULL, false); | 695 NULL, false); |
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
925 | 990 |
926 bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) { | 991 bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) { |
927 if (!background_view_->IsScreenSaverVisible()) { | 992 if (!background_view_->IsScreenSaverVisible()) { |
928 StartScreenSaver(); | 993 StartScreenSaver(); |
929 return true; | 994 return true; |
930 } | 995 } |
931 return false; | 996 return false; |
932 } | 997 } |
933 | 998 |
934 } // namespace chromeos | 999 } // namespace chromeos |
OLD | NEW |