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

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: use grab_server 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 | « no previous file | no next file » | 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 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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698