Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(229)

Side by Side Diff: chrome/browser/chromeos/login/screen_locker.cc

Issue 5043002: A workaround to close currently opened menu. Send escape key if screen locker fails to grab input. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: update gyp Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/linux/system.gyp ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 hopefully close menus or windows
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(); 335 // Grab x server so that we can atomically grab and take
326 336 // action when grab fails.
337 gdk_x11_grab_server();
327 if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { 338 if (kbd_grab_status_ != GDK_GRAB_SUCCESS) {
328 kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, 339 kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE,
329 GDK_CURRENT_TIME); 340 GDK_CURRENT_TIME);
330 } 341 }
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();
360 gdk_x11_ungrab_server();
348 MessageLoop::current()->PostDelayedTask( 361 MessageLoop::current()->PostDelayedTask(
349 FROM_HERE, 362 FROM_HERE,
350 task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs), 363 task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs),
351 kRetryGrabIntervalMs); 364 kRetryGrabIntervalMs);
352 } else { 365 } else {
366 gdk_x11_ungrab_server();
353 CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_) 367 CHECK_EQ(GDK_GRAB_SUCCESS, kbd_grab_status_)
354 << "Failed to grab keyboard input:" << kbd_grab_status_; 368 << "Failed to grab keyboard input:" << kbd_grab_status_;
355 CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) 369 CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_)
356 << "Failed to grab pointer input:" << mouse_grab_status_; 370 << "Failed to grab pointer input:" << mouse_grab_status_;
357 DVLOG(1) << "Grab Success"; 371 DVLOG(1) << "Grab Success";
358 screen_locker_->OnGrabInputs(); 372 screen_locker_->OnGrabInputs();
359 } 373 }
360 } 374 }
361 375
376 void GrabWidget::TryUngrabOtherClients() {
377 #if !defined(NDEBUG)
378 {
379 int event_base, error_base;
380 int major, minor;
381 // Make sure we have XTest extension.
382 DCHECK(XTestQueryExtension(x11_util::GetXDisplay(),
383 &event_base, &error_base,
384 &major, &minor));
385 }
386 #endif
387
388 // The following code is an attempt to grab inputs by closing
389 // supposedly opened menu. This happens when a plugin has a menu
390 // opened.
391 if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
392 mouse_grab_status_ == GDK_GRAB_FROZEN) {
393 // Successfully grabbed the keyboard, but pointer is still
394 // grabbed by other client. Another attempt to close supposedly
395 // opened menu by emulating keypress at the left top corner.
396 Display* display = x11_util::GetXDisplay();
397 Window root, child;
398 int root_x, root_y, win_x, winy;
399 unsigned int mask;
400 XQueryPointer(display,
401 x11_util::GetX11WindowFromGtkWidget(window_contents()),
402 &root, &child, &root_x, &root_y,
403 &win_x, &winy, &mask);
404 XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime);
405 XTestFakeButtonEvent(display, 1, True, CurrentTime);
406 XTestFakeButtonEvent(display, 1, False, CurrentTime);
407 // Move the pointer back.
408 XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime);
409 XFlush(display);
410 } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
411 kbd_grab_status_ == GDK_GRAB_FROZEN) {
412 // Successfully grabbed the pointer, but keyboard is still grabbed
413 // by other client. Another attempt to close supposedly opened
414 // menu by emulating escape key. Such situation must be very
415 // rare, but handling this just in case
416 Display* display = x11_util::GetXDisplay();
417 KeyCode escape = XKeysymToKeycode(display, XK_Escape);
418 XTestFakeKeyEvent(display, escape, True, CurrentTime);
419 XTestFakeKeyEvent(display, escape, False, CurrentTime);
420 XFlush(display);
421 }
422 }
423
362 // BackgroundView for ScreenLocker, which layouts a lock widget in 424 // BackgroundView for ScreenLocker, which layouts a lock widget in
363 // addition to other background components. 425 // addition to other background components.
364 class ScreenLockerBackgroundView : public chromeos::BackgroundView { 426 class ScreenLockerBackgroundView : public chromeos::BackgroundView {
365 public: 427 public:
366 ScreenLockerBackgroundView(views::WidgetGtk* lock_widget, 428 ScreenLockerBackgroundView(views::WidgetGtk* lock_widget,
367 views::View* screen_lock_view) 429 views::View* screen_lock_view)
368 : lock_widget_(lock_widget), 430 : lock_widget_(lock_widget),
369 screen_lock_view_(screen_lock_view) { 431 screen_lock_view_(screen_lock_view) {
370 } 432 }
371 433
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 667
606 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); 668 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
607 WmIpc::instance()->SetWindowType( 669 WmIpc::instance()->SetWindowType(
608 lock_window_->GetNativeView(), 670 lock_window_->GetNativeView(),
609 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, 671 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
610 NULL); 672 NULL);
611 673
612 lock_window_->SetContentsView(background_view_); 674 lock_window_->SetContentsView(background_view_);
613 lock_window_->Show(); 675 lock_window_->Show();
614 676
677 cast_lock_widget->ClearGtkGrab();
678
679 // Call this after lock_window_->Show(); otherwise the 1st invocation
680 // of gdk_xxx_grab() will always fail.
615 cast_lock_widget->TryGrabAllInputs(); 681 cast_lock_widget->TryGrabAllInputs();
616 682
617 // Add the window to its own group so that its grab won't be stolen if 683 // 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. 684 // 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 685 // 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 686 // after calling ClearGtkGrab(), as want to be in the default window group
621 // then so we can break any existing GTK grabs. 687 // then so we can break any existing GTK grabs.
622 GtkWindowGroup* window_group = gtk_window_group_new(); 688 GtkWindowGroup* window_group = gtk_window_group_new();
623 gtk_window_group_add_window(window_group, 689 gtk_window_group_add_window(window_group,
624 GTK_WINDOW(lock_window_->GetNativeView())); 690 GTK_WINDOW(lock_window_->GetNativeView()));
625 g_object_unref(window_group); 691 g_object_unref(window_group);
626 692
627 // Don't let X draw default background, which was causing flash on 693 // Don't let X draw default background, which was causing flash on
628 // resume. 694 // resume.
629 gdk_window_set_back_pixmap(lock_window_->GetNativeView()->window, 695 gdk_window_set_back_pixmap(lock_window_->GetNativeView()->window,
630 NULL, false); 696 NULL, false);
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
925 991
926 bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) { 992 bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) {
927 if (!background_view_->IsScreenSaverVisible()) { 993 if (!background_view_->IsScreenSaverVisible()) {
928 StartScreenSaver(); 994 StartScreenSaver();
929 return true; 995 return true;
930 } 996 }
931 return false; 997 return false;
932 } 998 }
933 999
934 } // namespace chromeos 1000 } // namespace chromeos
OLDNEW
« no previous file with comments | « build/linux/system.gyp ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698