• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/login/screen_locker.h"
6 
7 #include <X11/extensions/XTest.h>
8 #include <X11/keysym.h>
9 #include <gdk/gdkkeysyms.h>
10 #include <gdk/gdkx.h>
11 #include <string>
12 #include <vector>
13 // Evil hack to undo X11 evil #define. See crosbug.com/
14 #undef Status
15 
16 #include "base/command_line.h"
17 #include "base/lazy_instance.h"
18 #include "base/message_loop.h"
19 #include "base/metrics/histogram.h"
20 #include "base/string_util.h"
21 #include "base/timer.h"
22 #include "base/utf_string_conversions.h"
23 #include "chrome/browser/chromeos/cros/input_method_library.h"
24 #include "chrome/browser/chromeos/cros/login_library.h"
25 #include "chrome/browser/chromeos/cros/screen_lock_library.h"
26 #include "chrome/browser/chromeos/input_method/input_method_util.h"
27 #include "chrome/browser/chromeos/language_preferences.h"
28 #include "chrome/browser/chromeos/login/authenticator.h"
29 #include "chrome/browser/chromeos/login/background_view.h"
30 #include "chrome/browser/chromeos/login/login_performer.h"
31 #include "chrome/browser/chromeos/login/login_utils.h"
32 #include "chrome/browser/chromeos/login/message_bubble.h"
33 #include "chrome/browser/chromeos/login/screen_lock_view.h"
34 #include "chrome/browser/chromeos/login/shutdown_button.h"
35 #include "chrome/browser/chromeos/system_key_event_listener.h"
36 #include "chrome/browser/chromeos/view_ids.h"
37 #include "chrome/browser/chromeos/wm_ipc.h"
38 #include "chrome/browser/metrics/user_metrics.h"
39 #include "chrome/browser/profiles/profile.h"
40 #include "chrome/browser/profiles/profile_manager.h"
41 #include "chrome/browser/sync/profile_sync_service.h"
42 #include "chrome/browser/ui/browser.h"
43 #include "chrome/browser/ui/browser_list.h"
44 #include "chrome/browser/ui/browser_window.h"
45 #include "chrome/common/chrome_switches.h"
46 #include "content/browser/browser_thread.h"
47 #include "content/common/notification_service.h"
48 #include "googleurl/src/gurl.h"
49 #include "grit/generated_resources.h"
50 #include "grit/theme_resources.h"
51 #include "third_party/cros/chromeos_wm_ipc_enums.h"
52 #include "ui/base/l10n/l10n_util.h"
53 #include "ui/base/resource/resource_bundle.h"
54 #include "ui/base/x/x11_util.h"
55 #include "views/screen.h"
56 #include "views/widget/root_view.h"
57 #include "views/widget/widget_gtk.h"
58 
59 namespace {
60 
61 // The maximum duration for which locker should try to grab the keyboard and
62 // mouse and its interval for regrabbing on failure.
63 const int kMaxGrabFailureSec = 30;
64 const int64 kRetryGrabIntervalMs = 500;
65 
66 // Maximum number of times we'll try to grab the keyboard and mouse before
67 // giving up.  If we hit the limit, Chrome exits and the session is terminated.
68 const int kMaxGrabFailures = kMaxGrabFailureSec * 1000 / kRetryGrabIntervalMs;
69 
70 // A idle time to show the screen saver in seconds.
71 const int kScreenSaverIdleTimeout = 15;
72 
73 // Observer to start ScreenLocker when the screen lock
74 class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer,
75                            public NotificationObserver {
76  public:
ScreenLockObserver()77   ScreenLockObserver() {
78     registrar_.Add(this, NotificationType::LOGIN_USER_CHANGED,
79                    NotificationService::AllSources());
80   }
81 
82   // NotificationObserver overrides:
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)83   virtual void Observe(NotificationType type,
84                        const NotificationSource& source,
85                        const NotificationDetails& details) {
86     if (type == NotificationType::LOGIN_USER_CHANGED) {
87       // Register Screen Lock after login screen to make sure
88       // we don't show the screen lock on top of the login screen by accident.
89       if (chromeos::CrosLibrary::Get()->EnsureLoaded())
90         chromeos::CrosLibrary::Get()->GetScreenLockLibrary()->AddObserver(this);
91     }
92   }
93 
LockScreen(chromeos::ScreenLockLibrary * obj)94   virtual void LockScreen(chromeos::ScreenLockLibrary* obj) {
95     VLOG(1) << "In: ScreenLockObserver::LockScreen";
96     SetupInputMethodsForScreenLocker();
97     chromeos::ScreenLocker::Show();
98   }
99 
UnlockScreen(chromeos::ScreenLockLibrary * obj)100   virtual void UnlockScreen(chromeos::ScreenLockLibrary* obj) {
101     RestoreInputMethods();
102     chromeos::ScreenLocker::Hide();
103   }
104 
UnlockScreenFailed(chromeos::ScreenLockLibrary * obj)105   virtual void UnlockScreenFailed(chromeos::ScreenLockLibrary* obj) {
106     chromeos::ScreenLocker::UnlockScreenFailed();
107   }
108 
109  private:
110   // Temporarily deactivates all input methods (e.g. Chinese, Japanese, Arabic)
111   // since they are not necessary to input a login password. Users are still
112   // able to use/switch active keyboard layouts (e.g. US qwerty, US dvorak,
113   // French).
SetupInputMethodsForScreenLocker()114   void SetupInputMethodsForScreenLocker() {
115     if (chromeos::CrosLibrary::Get()->EnsureLoaded() &&
116         // The LockScreen function is also called when the OS is suspended, and
117         // in that case |saved_active_input_method_list_| might be non-empty.
118         saved_active_input_method_list_.empty()) {
119       chromeos::InputMethodLibrary* library =
120           chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
121 
122       saved_previous_input_method_id_ = library->previous_input_method().id;
123       saved_current_input_method_id_ = library->current_input_method().id;
124       scoped_ptr<chromeos::InputMethodDescriptors> active_input_method_list(
125           library->GetActiveInputMethods());
126 
127       const std::string hardware_keyboard_id =
128           chromeos::input_method::GetHardwareInputMethodId();
129       // We'll add the hardware keyboard if it's not included in
130       // |active_input_method_list| so that the user can always use the hardware
131       // keyboard on the screen locker.
132       bool should_add_hardware_keyboard = true;
133 
134       chromeos::ImeConfigValue value;
135       value.type = chromeos::ImeConfigValue::kValueTypeStringList;
136       for (size_t i = 0; i < active_input_method_list->size(); ++i) {
137         const std::string& input_method_id = active_input_method_list->at(i).id;
138         saved_active_input_method_list_.push_back(input_method_id);
139         // Skip if it's not a keyboard layout.
140         if (!chromeos::input_method::IsKeyboardLayout(input_method_id))
141           continue;
142         value.string_list_value.push_back(input_method_id);
143         if (input_method_id == hardware_keyboard_id) {
144           should_add_hardware_keyboard = false;
145         }
146       }
147       if (should_add_hardware_keyboard) {
148         value.string_list_value.push_back(hardware_keyboard_id);
149       }
150       // We don't want to shut down the IME, even if the hardware layout is the
151       // only IME left.
152       library->SetEnableAutoImeShutdown(false);
153       library->SetImeConfig(
154           chromeos::language_prefs::kGeneralSectionName,
155           chromeos::language_prefs::kPreloadEnginesConfigName,
156           value);
157     }
158   }
159 
RestoreInputMethods()160   void RestoreInputMethods() {
161     if (chromeos::CrosLibrary::Get()->EnsureLoaded() &&
162         !saved_active_input_method_list_.empty()) {
163       chromeos::InputMethodLibrary* library =
164           chromeos::CrosLibrary::Get()->GetInputMethodLibrary();
165 
166       chromeos::ImeConfigValue value;
167       value.type = chromeos::ImeConfigValue::kValueTypeStringList;
168       value.string_list_value = saved_active_input_method_list_;
169       library->SetEnableAutoImeShutdown(true);
170       library->SetImeConfig(
171           chromeos::language_prefs::kGeneralSectionName,
172           chromeos::language_prefs::kPreloadEnginesConfigName,
173           value);
174       // Send previous input method id first so Ctrl+space would work fine.
175       if (!saved_previous_input_method_id_.empty())
176         library->ChangeInputMethod(saved_previous_input_method_id_);
177       if (!saved_current_input_method_id_.empty())
178         library->ChangeInputMethod(saved_current_input_method_id_);
179 
180       saved_previous_input_method_id_.clear();
181       saved_current_input_method_id_.clear();
182       saved_active_input_method_list_.clear();
183     }
184   }
185 
186   NotificationRegistrar registrar_;
187   std::string saved_previous_input_method_id_;
188   std::string saved_current_input_method_id_;
189   std::vector<std::string> saved_active_input_method_list_;
190 
191   DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver);
192 };
193 
194 static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer(
195     base::LINKER_INITIALIZED);
196 
197 // A ScreenLock window that covers entire screen to keep the keyboard
198 // focus/events inside the grab widget.
199 class LockWindow : public views::WidgetGtk {
200  public:
LockWindow()201   LockWindow()
202       : views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW),
203         toplevel_focus_widget_(NULL) {
204     EnableDoubleBuffer(true);
205   }
206 
207   // GTK propagates key events from parents to children.
208   // Make sure LockWindow will never handle key events.
OnKeyEvent(GtkWidget * widget,GdkEventKey * event)209   virtual gboolean OnKeyEvent(GtkWidget* widget, GdkEventKey* event) {
210     // Don't handle key event in the lock window.
211     return false;
212   }
213 
OnDestroy(GtkWidget * object)214   virtual void OnDestroy(GtkWidget* object) {
215     VLOG(1) << "OnDestroy: LockWindow destroyed";
216     views::WidgetGtk::OnDestroy(object);
217   }
218 
ClearNativeFocus()219   virtual void ClearNativeFocus() {
220     DCHECK(toplevel_focus_widget_);
221     gtk_widget_grab_focus(toplevel_focus_widget_);
222   }
223 
224   // Sets the widget to move the focus to when clearning the native
225   // widget's focus.
set_toplevel_focus_widget(GtkWidget * widget)226   void set_toplevel_focus_widget(GtkWidget* widget) {
227     GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
228     toplevel_focus_widget_ = widget;
229   }
230 
231  private:
232   // The widget we set focus to when clearning the focus on native
233   // widget.  In screen locker, gdk input is grabbed in GrabWidget,
234   // and resetting the focus by using gtk_window_set_focus seems to
235   // confuse gtk and doesn't let focus move to native widget under
236   // GrabWidget.
237   GtkWidget* toplevel_focus_widget_;
238 
239   DISALLOW_COPY_AND_ASSIGN(LockWindow);
240 };
241 
242 // GrabWidget's root view to layout the ScreenLockView at the center
243 // and the Shutdown button at the right bottom.
244 class GrabWidgetRootView
245     : public views::View,
246       public chromeos::ScreenLocker::ScreenLockViewContainer {
247  public:
GrabWidgetRootView(chromeos::ScreenLockView * screen_lock_view)248   explicit GrabWidgetRootView(chromeos::ScreenLockView* screen_lock_view)
249       : screen_lock_view_(screen_lock_view),
250         shutdown_button_(new chromeos::ShutdownButton()) {
251     shutdown_button_->Init();
252     AddChildView(screen_lock_view_);
253     AddChildView(shutdown_button_);
254   }
255 
256   // views::View implementation.
Layout()257   virtual void Layout() {
258     gfx::Size size = screen_lock_view_->GetPreferredSize();
259     screen_lock_view_->SetBounds(0, 0, size.width(), size.height());
260     shutdown_button_->LayoutIn(this);
261   }
262 
263   // ScreenLocker::ScreenLockViewContainer implementation:
SetScreenLockView(views::View * screen_lock_view)264   void SetScreenLockView(views::View* screen_lock_view) {
265     if (screen_lock_view_) {
266       RemoveChildView(screen_lock_view_);
267     }
268     screen_lock_view_ =  screen_lock_view;
269     if (screen_lock_view_) {
270       AddChildViewAt(screen_lock_view_, 0);
271     }
272     Layout();
273   }
274 
275  private:
276   views::View* screen_lock_view_;
277 
278   chromeos::ShutdownButton* shutdown_button_;
279 
280   DISALLOW_COPY_AND_ASSIGN(GrabWidgetRootView);
281 };
282 
283 // A child widget that grabs both keyboard and pointer input.
284 class GrabWidget : public views::WidgetGtk {
285  public:
GrabWidget(chromeos::ScreenLocker * screen_locker)286   explicit GrabWidget(chromeos::ScreenLocker* screen_locker)
287       : views::WidgetGtk(views::WidgetGtk::TYPE_CHILD),
288         screen_locker_(screen_locker),
289         ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
290         grab_failure_count_(0),
291         kbd_grab_status_(GDK_GRAB_INVALID_TIME),
292         mouse_grab_status_(GDK_GRAB_INVALID_TIME),
293         signout_link_(NULL),
294         shutdown_(NULL) {
295   }
296 
Show()297   virtual void Show() {
298     views::WidgetGtk::Show();
299     signout_link_ =
300         screen_locker_->GetViewByID(VIEW_ID_SCREEN_LOCKER_SIGNOUT_LINK);
301     shutdown_ = screen_locker_->GetViewByID(VIEW_ID_SCREEN_LOCKER_SHUTDOWN);
302     // These can be null in guest mode.
303   }
304 
ClearGtkGrab()305   void ClearGtkGrab() {
306     GtkWidget* current_grab_window;
307     // Grab gtk input first so that the menu holding gtk grab will
308     // close itself.
309     gtk_grab_add(window_contents());
310 
311     // Make sure there is no gtk grab widget so that gtk simply propagates
312     // an event.  This is necessary to allow message bubble and password
313     // field, button to process events simultaneously. GTK
314     // maintains grab widgets in a linked-list, so we need to remove
315     // until it's empty.
316     while ((current_grab_window = gtk_grab_get_current()) != NULL)
317       gtk_grab_remove(current_grab_window);
318   }
319 
OnKeyEvent(GtkWidget * widget,GdkEventKey * event)320   virtual gboolean OnKeyEvent(GtkWidget* widget, GdkEventKey* event) {
321     views::KeyEvent key_event(reinterpret_cast<GdkEvent*>(event));
322     // This is a hack to workaround the issue crosbug.com/10655 due to
323     // the limitation that a focus manager cannot handle views in
324     // TYPE_CHILD WidgetGtk correctly.
325     if (signout_link_ &&
326         event->type == GDK_KEY_PRESS &&
327         (event->keyval == GDK_Tab ||
328          event->keyval == GDK_ISO_Left_Tab ||
329          event->keyval == GDK_KP_Tab)) {
330       DCHECK(shutdown_);
331       bool reverse = event->state & GDK_SHIFT_MASK;
332       if (reverse && signout_link_->HasFocus()) {
333         shutdown_->RequestFocus();
334         return true;
335       }
336       if (!reverse && shutdown_->HasFocus()) {
337         signout_link_->RequestFocus();
338         return true;
339       }
340     }
341     return views::WidgetGtk::OnKeyEvent(widget, event);
342   }
343 
OnButtonPress(GtkWidget * widget,GdkEventButton * event)344   virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) {
345     WidgetGtk::OnButtonPress(widget, event);
346     // Never propagate event to parent.
347     return true;
348   }
349 
350   // Try to grab all inputs. It initiates another try if it fails to
351   // grab and the retry count is within a limit, or fails with CHECK.
352   void TryGrabAllInputs();
353 
354   // This method tries to steal pointer/keyboard grab from other
355   // client by sending events that will hopefully close menus or windows
356   // that have the grab.
357   void TryUngrabOtherClients();
358 
359  private:
HandleGtkGrabBroke()360   virtual void HandleGtkGrabBroke() {
361     // Input should never be stolen from ScreenLocker once it's
362     // grabbed.  If this happens, it's a bug and has to be fixed. We
363     // let chrome crash to get a crash report and dump, and
364     // SessionManager will terminate the session to logout.
365     CHECK_NE(GDK_GRAB_SUCCESS, kbd_grab_status_);
366     CHECK_NE(GDK_GRAB_SUCCESS, mouse_grab_status_);
367   }
368 
369   // Define separate methods for each error code so that stack trace
370   // will tell which error the grab failed with.
FailedWithGrabAlreadyGrabbed()371   void FailedWithGrabAlreadyGrabbed() {
372     LOG(FATAL) << "Grab already grabbed";
373   }
FailedWithGrabInvalidTime()374   void FailedWithGrabInvalidTime() {
375     LOG(FATAL) << "Grab invalid time";
376   }
FailedWithGrabNotViewable()377   void FailedWithGrabNotViewable() {
378     LOG(FATAL) << "Grab not viewable";
379   }
FailedWithGrabFrozen()380   void FailedWithGrabFrozen() {
381     LOG(FATAL) << "Grab frozen";
382   }
FailedWithUnknownError()383   void FailedWithUnknownError() {
384     LOG(FATAL) << "Grab uknown";
385   }
386 
387   chromeos::ScreenLocker* screen_locker_;
388   ScopedRunnableMethodFactory<GrabWidget> task_factory_;
389 
390   // The number times the widget tried to grab all focus.
391   int grab_failure_count_;
392   // Status of keyboard and mouse grab.
393   GdkGrabStatus kbd_grab_status_;
394   GdkGrabStatus mouse_grab_status_;
395 
396   views::View* signout_link_;
397   views::View* shutdown_;
398 
399   DISALLOW_COPY_AND_ASSIGN(GrabWidget);
400 };
401 
TryGrabAllInputs()402 void GrabWidget::TryGrabAllInputs() {
403   // Grab x server so that we can atomically grab and take
404   // action when grab fails.
405   gdk_x11_grab_server();
406   if (kbd_grab_status_ != GDK_GRAB_SUCCESS) {
407     kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE,
408                                          GDK_CURRENT_TIME);
409   }
410   if (mouse_grab_status_ != GDK_GRAB_SUCCESS) {
411     mouse_grab_status_ =
412         gdk_pointer_grab(window_contents()->window,
413                          FALSE,
414                          static_cast<GdkEventMask>(
415                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
416                              GDK_POINTER_MOTION_MASK),
417                          NULL,
418                          NULL,
419                          GDK_CURRENT_TIME);
420   }
421   if ((kbd_grab_status_ != GDK_GRAB_SUCCESS ||
422        mouse_grab_status_ != GDK_GRAB_SUCCESS) &&
423       grab_failure_count_++ < kMaxGrabFailures) {
424     LOG(WARNING) << "Failed to grab inputs. Trying again in "
425                  << kRetryGrabIntervalMs << " ms: kbd="
426                  << kbd_grab_status_ << ", mouse=" << mouse_grab_status_;
427     TryUngrabOtherClients();
428     gdk_x11_ungrab_server();
429     MessageLoop::current()->PostDelayedTask(
430         FROM_HERE,
431         task_factory_.NewRunnableMethod(&GrabWidget::TryGrabAllInputs),
432         kRetryGrabIntervalMs);
433   } else {
434     gdk_x11_ungrab_server();
435     GdkGrabStatus status = kbd_grab_status_;
436     if (status == GDK_GRAB_SUCCESS) {
437       status = mouse_grab_status_;
438     }
439     switch (status) {
440       case GDK_GRAB_SUCCESS:
441         break;
442       case GDK_GRAB_ALREADY_GRABBED:
443         FailedWithGrabAlreadyGrabbed();
444         break;
445       case GDK_GRAB_INVALID_TIME:
446         FailedWithGrabInvalidTime();
447         break;
448       case GDK_GRAB_NOT_VIEWABLE:
449         FailedWithGrabNotViewable();
450         break;
451       case GDK_GRAB_FROZEN:
452         FailedWithGrabFrozen();
453         break;
454       default:
455         FailedWithUnknownError();
456         break;
457     }
458     DVLOG(1) << "Grab Success";
459     screen_locker_->OnGrabInputs();
460   }
461 }
462 
TryUngrabOtherClients()463 void GrabWidget::TryUngrabOtherClients() {
464 #if !defined(NDEBUG)
465   {
466     int event_base, error_base;
467     int major, minor;
468     // Make sure we have XTest extension.
469     DCHECK(XTestQueryExtension(ui::GetXDisplay(),
470                                &event_base, &error_base,
471                                &major, &minor));
472   }
473 #endif
474 
475   // The following code is an attempt to grab inputs by closing
476   // supposedly opened menu. This happens when a plugin has a menu
477   // opened.
478   if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
479       mouse_grab_status_ == GDK_GRAB_FROZEN) {
480     // Successfully grabbed the keyboard, but pointer is still
481     // grabbed by other client. Another attempt to close supposedly
482     // opened menu by emulating keypress at the left top corner.
483     Display* display = ui::GetXDisplay();
484     Window root, child;
485     int root_x, root_y, win_x, winy;
486     unsigned int mask;
487     XQueryPointer(display,
488                   ui::GetX11WindowFromGtkWidget(window_contents()),
489                   &root, &child, &root_x, &root_y,
490                   &win_x, &winy, &mask);
491     XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime);
492     XTestFakeButtonEvent(display, 1, True, CurrentTime);
493     XTestFakeButtonEvent(display, 1, False, CurrentTime);
494     // Move the pointer back.
495     XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime);
496     XFlush(display);
497   } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
498              kbd_grab_status_ == GDK_GRAB_FROZEN) {
499     // Successfully grabbed the pointer, but keyboard is still grabbed
500     // by other client. Another attempt to close supposedly opened
501     // menu by emulating escape key.  Such situation must be very
502     // rare, but handling this just in case
503     Display* display = ui::GetXDisplay();
504     KeyCode escape = XKeysymToKeycode(display, XK_Escape);
505     XTestFakeKeyEvent(display, escape, True, CurrentTime);
506     XTestFakeKeyEvent(display, escape, False, CurrentTime);
507     XFlush(display);
508   }
509 }
510 
511 // BackgroundView for ScreenLocker, which layouts a lock widget in
512 // addition to other background components.
513 class ScreenLockerBackgroundView
514     : public chromeos::BackgroundView,
515       public chromeos::ScreenLocker::ScreenLockViewContainer {
516  public:
ScreenLockerBackgroundView(views::WidgetGtk * lock_widget,views::View * screen_lock_view)517   ScreenLockerBackgroundView(views::WidgetGtk* lock_widget,
518                              views::View* screen_lock_view)
519       : lock_widget_(lock_widget),
520         screen_lock_view_(screen_lock_view) {
521   }
522 
GetScreenMode() const523   virtual ScreenMode GetScreenMode() const {
524     return kScreenLockerMode;
525   }
526 
Layout()527   virtual void Layout() {
528     chromeos::BackgroundView::Layout();
529     gfx::Rect screen = bounds();
530     if (screen_lock_view_) {
531       gfx::Size size = screen_lock_view_->GetPreferredSize();
532       gfx::Point origin((screen.width() - size.width()) / 2,
533                         (screen.height() - size.height()) / 2);
534       gfx::Size widget_size(screen.size());
535       widget_size.Enlarge(-origin.x(), -origin.y());
536       lock_widget_->SetBounds(gfx::Rect(origin, widget_size));
537     } else {
538       // No password entry. Move the lock widget to off screen.
539       lock_widget_->SetBounds(gfx::Rect(-100, -100, 1, 1));
540     }
541   }
542 
543   // ScreenLocker::ScreenLockViewContainer implementation:
SetScreenLockView(views::View * screen_lock_view)544   void SetScreenLockView(views::View* screen_lock_view) {
545     screen_lock_view_ =  screen_lock_view;
546     Layout();
547   }
548 
549  private:
550   views::WidgetGtk* lock_widget_;
551 
552   views::View* screen_lock_view_;
553 
554   DISALLOW_COPY_AND_ASSIGN(ScreenLockerBackgroundView);
555 };
556 
557 }  // namespace
558 
559 namespace chromeos {
560 
561 // static
562 ScreenLocker* ScreenLocker::screen_locker_ = NULL;
563 
564 // A event observer that forwards gtk events from one window to another.
565 // See screen_locker.h for more details.
566 class MouseEventRelay : public MessageLoopForUI::Observer {
567  public:
MouseEventRelay(GdkWindow * src,GdkWindow * dest)568   MouseEventRelay(GdkWindow* src, GdkWindow* dest)
569       : src_(src),
570         dest_(dest),
571         initialized_(false) {
572     DCHECK(src_);
573     DCHECK(dest_);
574   }
575 
WillProcessEvent(GdkEvent * event)576   virtual void WillProcessEvent(GdkEvent* event) {}
577 
DidProcessEvent(GdkEvent * event)578   virtual void DidProcessEvent(GdkEvent* event) {
579     if (event->any.window != src_)
580       return;
581     if (!initialized_) {
582       gint src_x, src_y, dest_x, dest_y, width, height, depth;
583       gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth);
584       // wait to compute offset until the info bubble widget's location
585       // is available.
586       if (dest_x < 0 || dest_y < 0)
587         return;
588       gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth);
589       offset_.SetPoint(dest_x - src_x, dest_y - src_y);
590       initialized_ = true;
591     }
592     if (event->type == GDK_BUTTON_PRESS ||
593         event->type == GDK_BUTTON_RELEASE) {
594       GdkEvent* copy = gdk_event_copy(event);
595       copy->button.window = dest_;
596       g_object_ref(copy->button.window);
597       copy->button.x -= offset_.x();
598       copy->button.y -= offset_.y();
599 
600       gdk_event_put(copy);
601       gdk_event_free(copy);
602     } else if (event->type == GDK_MOTION_NOTIFY) {
603       GdkEvent* copy = gdk_event_copy(event);
604       copy->motion.window = dest_;
605       g_object_ref(copy->motion.window);
606       copy->motion.x -= offset_.x();
607       copy->motion.y -= offset_.y();
608 
609       gdk_event_put(copy);
610       gdk_event_free(copy);
611     }
612   }
613 
614  private:
615   GdkWindow* src_;
616   GdkWindow* dest_;
617   bool initialized_;
618 
619   // Offset from src_'s origin to dest_'s origin.
620   gfx::Point offset_;
621 
622   DISALLOW_COPY_AND_ASSIGN(MouseEventRelay);
623 };
624 
625 // A event observer used to unlock the screen upon user's action
626 // without asking password. Used in BWSI and auto login mode.
627 // TODO(oshima): consolidate InputEventObserver and LockerInputEventObserver.
628 class InputEventObserver : public MessageLoopForUI::Observer {
629  public:
InputEventObserver(ScreenLocker * screen_locker)630   explicit InputEventObserver(ScreenLocker* screen_locker)
631       : screen_locker_(screen_locker),
632         activated_(false) {
633   }
634 
WillProcessEvent(GdkEvent * event)635   virtual void WillProcessEvent(GdkEvent* event) {
636     if ((event->type == GDK_KEY_PRESS ||
637          event->type == GDK_BUTTON_PRESS ||
638          event->type == GDK_MOTION_NOTIFY) &&
639         !activated_) {
640       activated_ = true;
641       std::string not_used_string;
642       GaiaAuthConsumer::ClientLoginResult not_used;
643       screen_locker_->OnLoginSuccess(not_used_string,
644                                      not_used_string,
645                                      not_used,
646                                      false);
647     }
648   }
649 
DidProcessEvent(GdkEvent * event)650   virtual void DidProcessEvent(GdkEvent* event) {
651   }
652 
653  private:
654   chromeos::ScreenLocker* screen_locker_;
655 
656   bool activated_;
657 
658   DISALLOW_COPY_AND_ASSIGN(InputEventObserver);
659 };
660 
661 // A event observer used to show the screen locker upon
662 // user action: mouse or keyboard interactions.
663 // TODO(oshima): this has to be disabled while authenticating.
664 class LockerInputEventObserver : public MessageLoopForUI::Observer {
665  public:
LockerInputEventObserver(ScreenLocker * screen_locker)666   explicit LockerInputEventObserver(ScreenLocker* screen_locker)
667       : screen_locker_(screen_locker),
668         ALLOW_THIS_IN_INITIALIZER_LIST(
669             timer_(base::TimeDelta::FromSeconds(kScreenSaverIdleTimeout), this,
670                    &LockerInputEventObserver::StartScreenSaver)) {
671   }
672 
WillProcessEvent(GdkEvent * event)673   virtual void WillProcessEvent(GdkEvent* event) {
674     if ((event->type == GDK_KEY_PRESS ||
675          event->type == GDK_BUTTON_PRESS ||
676          event->type == GDK_MOTION_NOTIFY)) {
677       timer_.Reset();
678       screen_locker_->StopScreenSaver();
679     }
680   }
681 
DidProcessEvent(GdkEvent * event)682   virtual void DidProcessEvent(GdkEvent* event) {
683   }
684 
685  private:
StartScreenSaver()686   void StartScreenSaver() {
687     screen_locker_->StartScreenSaver();
688   }
689 
690   chromeos::ScreenLocker* screen_locker_;
691   base::DelayTimer<LockerInputEventObserver> timer_;
692 
693   DISALLOW_COPY_AND_ASSIGN(LockerInputEventObserver);
694 };
695 
696 //////////////////////////////////////////////////////////////////////////////
697 // ScreenLocker, public:
698 
ScreenLocker(const UserManager::User & user)699 ScreenLocker::ScreenLocker(const UserManager::User& user)
700     : lock_window_(NULL),
701       lock_widget_(NULL),
702       screen_lock_view_(NULL),
703       captcha_view_(NULL),
704       grab_container_(NULL),
705       background_container_(NULL),
706       user_(user),
707       error_info_(NULL),
708       drawn_(false),
709       input_grabbed_(false),
710       // TODO(oshima): support auto login mode (this is not implemented yet)
711       // http://crosbug.com/1881
712       unlock_on_input_(user_.email().empty()),
713       locked_(false),
714       start_time_(base::Time::Now()) {
715   DCHECK(!screen_locker_);
716   screen_locker_ = this;
717 }
718 
Init()719 void ScreenLocker::Init() {
720   static const GdkColor kGdkBlack = {0, 0, 0, 0};
721 
722   authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
723 
724   gfx::Point left_top(1, 1);
725   gfx::Rect init_bounds(views::Screen::GetMonitorAreaNearestPoint(left_top));
726 
727   LockWindow* lock_window = new LockWindow();
728   lock_window_ = lock_window;
729   lock_window_->Init(NULL, init_bounds);
730   gtk_widget_modify_bg(
731       lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack);
732 
733   g_signal_connect(lock_window_->GetNativeView(), "client-event",
734                    G_CALLBACK(OnClientEventThunk), this);
735 
736   // GTK does not like zero width/height.
737   if (!unlock_on_input_) {
738     screen_lock_view_ = new ScreenLockView(this);
739     screen_lock_view_->Init();
740     screen_lock_view_->SetEnabled(false);
741     screen_lock_view_->StartThrobber();
742   } else {
743     input_event_observer_.reset(new InputEventObserver(this));
744     MessageLoopForUI::current()->AddObserver(input_event_observer_.get());
745   }
746 
747   // Hang on to a cast version of the grab widget so we can call its
748   // TryGrabAllInputs() method later.  (Nobody else needs to use it, so moving
749   // its declaration to the header instead of keeping it in an anonymous
750   // namespace feels a bit ugly.)
751   GrabWidget* cast_lock_widget = new GrabWidget(this);
752   lock_widget_ = cast_lock_widget;
753   lock_widget_->MakeTransparent();
754   lock_widget_->InitWithWidget(lock_window_, gfx::Rect());
755   if (screen_lock_view_) {
756     GrabWidgetRootView* root_view = new GrabWidgetRootView(screen_lock_view_);
757     grab_container_ = root_view;
758     lock_widget_->SetContentsView(root_view);
759   }
760   lock_widget_->Show();
761 
762   // Configuring the background url.
763   std::string url_string =
764       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
765           switches::kScreenSaverUrl);
766   ScreenLockerBackgroundView* screen_lock_background_view_ =
767       new ScreenLockerBackgroundView(lock_widget_, screen_lock_view_);
768   background_container_ = screen_lock_background_view_;
769   background_view_ = screen_lock_background_view_;
770   background_view_->Init(GURL(url_string));
771   if (background_view_->ScreenSaverEnabled())
772     StartScreenSaver();
773 
774   DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
775   WmIpc::instance()->SetWindowType(
776       lock_window_->GetNativeView(),
777       WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
778       NULL);
779 
780   lock_window_->SetContentsView(background_view_);
781   lock_window_->Show();
782 
783   cast_lock_widget->ClearGtkGrab();
784 
785   // Call this after lock_window_->Show(); otherwise the 1st invocation
786   // of gdk_xxx_grab() will always fail.
787   cast_lock_widget->TryGrabAllInputs();
788 
789   // Add the window to its own group so that its grab won't be stolen if
790   // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g.
791   // a modal dialog) -- see http://crosbug.com/8999.  We intentionally do this
792   // after calling ClearGtkGrab(), as want to be in the default window group
793   // then so we can break any existing GTK grabs.
794   GtkWindowGroup* window_group = gtk_window_group_new();
795   gtk_window_group_add_window(window_group,
796                               GTK_WINDOW(lock_window_->GetNativeView()));
797   g_object_unref(window_group);
798 
799   lock_window->set_toplevel_focus_widget(lock_widget_->window_contents());
800 
801   // Create the SystemKeyEventListener so it can listen for system keyboard
802   // messages regardless of focus while screen locked.
803   SystemKeyEventListener::GetInstance();
804 }
805 
OnLoginFailure(const LoginFailure & error)806 void ScreenLocker::OnLoginFailure(const LoginFailure& error) {
807   DVLOG(1) << "OnLoginFailure";
808   UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure"));
809   if (authentication_start_time_.is_null()) {
810     LOG(ERROR) << "authentication_start_time_ is not set";
811   } else {
812     base::TimeDelta delta = base::Time::Now() - authentication_start_time_;
813     VLOG(1) << "Authentication failure time: " << delta.InSecondsF();
814     UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta);
815   }
816 
817   EnableInput();
818   // Don't enable signout button here as we're showing
819   // MessageBubble.
820 
821   string16 msg = l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_AUTHENTICATING);
822   const std::string error_text = error.GetErrorString();
823   if (!error_text.empty())
824     msg += ASCIIToUTF16("\n") + ASCIIToUTF16(error_text);
825 
826   InputMethodLibrary* input_method_library =
827       CrosLibrary::Get()->GetInputMethodLibrary();
828   if (input_method_library->GetNumActiveInputMethods() > 1)
829     msg += ASCIIToUTF16("\n") +
830         l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_KEYBOARD_SWITCH_HINT);
831 
832   ShowErrorBubble(UTF16ToWide(msg), BubbleBorder::BOTTOM_LEFT);
833 }
834 
OnLoginSuccess(const std::string & username,const std::string & password,const GaiaAuthConsumer::ClientLoginResult & unused,bool pending_requests)835 void ScreenLocker::OnLoginSuccess(
836     const std::string& username,
837     const std::string& password,
838     const GaiaAuthConsumer::ClientLoginResult& unused,
839     bool pending_requests) {
840   VLOG(1) << "OnLoginSuccess: Sending Unlock request.";
841   if (authentication_start_time_.is_null()) {
842     if (!username.empty())
843       LOG(WARNING) << "authentication_start_time_ is not set";
844   } else {
845     base::TimeDelta delta = base::Time::Now() - authentication_start_time_;
846     VLOG(1) << "Authentication success time: " << delta.InSecondsF();
847     UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta);
848   }
849 
850   Profile* profile = ProfileManager::GetDefaultProfile();
851   if (profile) {
852     ProfileSyncService* service = profile->GetProfileSyncService(username);
853     if (service && !service->HasSyncSetupCompleted()) {
854       // If sync has failed somehow, try setting the sync passphrase here.
855       service->SetPassphrase(password, false, true);
856     }
857   }
858 
859   if (CrosLibrary::Get()->EnsureLoaded())
860     CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenUnlockRequested();
861 }
862 
BubbleClosing(Bubble * bubble,bool closed_by_escape)863 void ScreenLocker::BubbleClosing(Bubble* bubble, bool closed_by_escape) {
864   error_info_ = NULL;
865   screen_lock_view_->SetSignoutEnabled(true);
866   if (mouse_event_relay_.get()) {
867     MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get());
868     mouse_event_relay_.reset();
869   }
870 }
871 
OnCaptchaEntered(const std::string & captcha)872 void ScreenLocker::OnCaptchaEntered(const std::string& captcha) {
873   // Captcha dialog is only shown when LoginPerformer instance exists,
874   // i.e. blocking UI after password change is in place.
875   DCHECK(LoginPerformer::default_performer());
876   LoginPerformer::default_performer()->set_captcha(captcha);
877 
878   // ScreenLockView ownership is passed to grab_container_.
879   // Need to save return value here so that compile
880   // doesn't fail with "unused result" warning.
881   views::View* view = secondary_view_.release();
882   view = NULL;
883   captcha_view_->SetVisible(false);
884   grab_container_->SetScreenLockView(screen_lock_view_);
885   background_container_->SetScreenLockView(screen_lock_view_);
886   screen_lock_view_->SetVisible(true);
887   screen_lock_view_->ClearAndSetFocusToPassword();
888 
889   // Take CaptchaView ownership now that it's removed from grab_container_.
890   secondary_view_.reset(captcha_view_);
891   ShowErrorMessage(postponed_error_message_, false);
892   postponed_error_message_.clear();
893 }
894 
Authenticate(const string16 & password)895 void ScreenLocker::Authenticate(const string16& password) {
896   if (password.empty())
897     return;
898 
899   authentication_start_time_ = base::Time::Now();
900   screen_lock_view_->SetEnabled(false);
901   screen_lock_view_->SetSignoutEnabled(false);
902   screen_lock_view_->StartThrobber();
903 
904   // If LoginPerformer instance exists,
905   // initial online login phase is still active.
906   if (LoginPerformer::default_performer()) {
907     DVLOG(1) << "Delegating authentication to LoginPerformer.";
908     LoginPerformer::default_performer()->Login(user_.email(),
909                                                UTF16ToUTF8(password));
910   } else {
911     BrowserThread::PostTask(
912         BrowserThread::UI, FROM_HERE,
913         NewRunnableMethod(authenticator_.get(),
914                           &Authenticator::AuthenticateToUnlock,
915                           user_.email(),
916                           UTF16ToUTF8(password)));
917   }
918 }
919 
ClearErrors()920 void ScreenLocker::ClearErrors() {
921   if (error_info_) {
922     error_info_->Close();
923     error_info_ = NULL;
924   }
925 }
926 
EnableInput()927 void ScreenLocker::EnableInput() {
928   if (screen_lock_view_) {
929     screen_lock_view_->SetEnabled(true);
930     screen_lock_view_->ClearAndSetFocusToPassword();
931     screen_lock_view_->StopThrobber();
932   }
933 }
934 
Signout()935 void ScreenLocker::Signout() {
936   if (!error_info_) {
937     UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Signout"));
938     WmIpc::instance()->NotifyAboutSignout();
939     if (CrosLibrary::Get()->EnsureLoaded()) {
940       CrosLibrary::Get()->GetLoginLibrary()->StopSession("");
941     }
942 
943     // Don't hide yet the locker because the chrome screen may become visible
944     // briefly.
945   }
946 }
947 
ShowCaptchaAndErrorMessage(const GURL & captcha_url,const std::wstring & message)948 void ScreenLocker::ShowCaptchaAndErrorMessage(const GURL& captcha_url,
949                                               const std::wstring& message) {
950   postponed_error_message_ = message;
951   if (captcha_view_) {
952     captcha_view_->SetCaptchaURL(captcha_url);
953   } else {
954     captcha_view_ = new CaptchaView(captcha_url, true);
955     captcha_view_->Init();
956     captcha_view_->set_delegate(this);
957   }
958   // CaptchaView ownership is passed to grab_container_.
959   views::View* view = secondary_view_.release();
960   view = NULL;
961   screen_lock_view_->SetVisible(false);
962   grab_container_->SetScreenLockView(captcha_view_);
963   background_container_->SetScreenLockView(captcha_view_);
964   captcha_view_->SetVisible(true);
965   // Take ScreenLockView ownership now that it's removed from grab_container_.
966   secondary_view_.reset(screen_lock_view_);
967 }
968 
ShowErrorMessage(const std::wstring & message,bool sign_out_only)969 void ScreenLocker::ShowErrorMessage(const std::wstring& message,
970                                     bool sign_out_only) {
971   if (sign_out_only) {
972     screen_lock_view_->SetEnabled(false);
973   } else {
974     EnableInput();
975   }
976   screen_lock_view_->SetSignoutEnabled(sign_out_only);
977   // Make sure that active Sign Out button is not hidden behind the bubble.
978   ShowErrorBubble(message, sign_out_only ?
979       BubbleBorder::BOTTOM_RIGHT : BubbleBorder::BOTTOM_LEFT);
980 }
981 
OnGrabInputs()982 void ScreenLocker::OnGrabInputs() {
983   DVLOG(1) << "OnGrabInputs";
984   input_grabbed_ = true;
985   if (drawn_)
986     ScreenLockReady();
987 }
988 
GetViewByID(int id)989 views::View* ScreenLocker::GetViewByID(int id) {
990   return lock_widget_->GetRootView()->GetViewByID(id);
991 }
992 
993 // static
Show()994 void ScreenLocker::Show() {
995   VLOG(1) << "In ScreenLocker::Show";
996   UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Show"));
997   DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
998 
999   // Exit fullscreen.
1000   Browser* browser = BrowserList::GetLastActive();
1001   // browser can be NULL if we receive a lock request before the first browser
1002   // window is shown.
1003   if (browser && browser->window()->IsFullscreen()) {
1004     browser->ToggleFullscreenMode();
1005   }
1006 
1007   if (!screen_locker_) {
1008     VLOG(1) << "Show: Locking screen";
1009     ScreenLocker* locker =
1010         new ScreenLocker(UserManager::Get()->logged_in_user());
1011     locker->Init();
1012   } else {
1013     // PowerManager re-sends lock screen signal if it doesn't
1014     // receive the response within timeout. Just send complete
1015     // signal.
1016     VLOG(1) << "Show: locker already exists. Just sending completion event.";
1017     if (CrosLibrary::Get()->EnsureLoaded())
1018       CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted();
1019   }
1020 }
1021 
1022 // static
Hide()1023 void ScreenLocker::Hide() {
1024   DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
1025   DCHECK(screen_locker_);
1026   VLOG(1) << "Hide: Deleting ScreenLocker: " << screen_locker_;
1027   MessageLoopForUI::current()->DeleteSoon(FROM_HERE, screen_locker_);
1028 }
1029 
1030 // static
UnlockScreenFailed()1031 void ScreenLocker::UnlockScreenFailed() {
1032   DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
1033   if (screen_locker_) {
1034     // Power manager decided no to unlock the screen even if a user
1035     // typed in password, for example, when a user closed the lid
1036     // immediately after typing in the password.
1037     VLOG(1) << "UnlockScreenFailed: re-enabling screen locker.";
1038     screen_locker_->EnableInput();
1039   } else {
1040     // This can happen when a user requested unlock, but PowerManager
1041     // rejected because the computer is closed, then PowerManager unlocked
1042     // because it's open again and the above failure message arrives.
1043     // This'd be extremely rare, but may still happen.
1044     VLOG(1) << "UnlockScreenFailed: screen is already unlocked.";
1045   }
1046 }
1047 
1048 // static
InitClass()1049 void ScreenLocker::InitClass() {
1050   g_screen_lock_observer.Get();
1051 }
1052 
1053 ////////////////////////////////////////////////////////////////////////////////
1054 // ScreenLocker, private:
1055 
~ScreenLocker()1056 ScreenLocker::~ScreenLocker() {
1057   DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
1058   ClearErrors();
1059   if (input_event_observer_.get())
1060     MessageLoopForUI::current()->RemoveObserver(input_event_observer_.get());
1061   if (locker_input_event_observer_.get()) {
1062     lock_widget_->GetFocusManager()->UnregisterAccelerator(
1063         views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this);
1064     MessageLoopForUI::current()->RemoveObserver(
1065         locker_input_event_observer_.get());
1066   }
1067 
1068   gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1069   gdk_pointer_ungrab(GDK_CURRENT_TIME);
1070 
1071   DCHECK(lock_window_);
1072   VLOG(1) << "~ScreenLocker(): Closing ScreenLocker window.";
1073   lock_window_->Close();
1074   // lock_widget_ will be deleted by gtk's destroy signal.
1075   screen_locker_ = NULL;
1076   bool state = false;
1077   NotificationService::current()->Notify(
1078       NotificationType::SCREEN_LOCK_STATE_CHANGED,
1079       Source<ScreenLocker>(this),
1080       Details<bool>(&state));
1081   if (CrosLibrary::Get()->EnsureLoaded())
1082     CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenUnlockCompleted();
1083 }
1084 
SetAuthenticator(Authenticator * authenticator)1085 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) {
1086   authenticator_ = authenticator;
1087 }
1088 
ScreenLockReady()1089 void ScreenLocker::ScreenLockReady() {
1090   VLOG(1) << "ScreenLockReady: sending completed signal to power manager.";
1091   locked_ = true;
1092   base::TimeDelta delta = base::Time::Now() - start_time_;
1093   VLOG(1) << "Screen lock time: " << delta.InSecondsF();
1094   UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta);
1095 
1096   if (background_view_->ScreenSaverEnabled()) {
1097     lock_widget_->GetFocusManager()->RegisterAccelerator(
1098         views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this);
1099     locker_input_event_observer_.reset(new LockerInputEventObserver(this));
1100     MessageLoopForUI::current()->AddObserver(
1101         locker_input_event_observer_.get());
1102   } else {
1103     // Don't enable the password field until we grab all inputs.
1104     EnableInput();
1105   }
1106 
1107   bool state = true;
1108   NotificationService::current()->Notify(
1109       NotificationType::SCREEN_LOCK_STATE_CHANGED,
1110       Source<ScreenLocker>(this),
1111       Details<bool>(&state));
1112   if (CrosLibrary::Get()->EnsureLoaded())
1113     CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted();
1114 }
1115 
OnClientEvent(GtkWidget * widge,GdkEventClient * event)1116 void ScreenLocker::OnClientEvent(GtkWidget* widge, GdkEventClient* event) {
1117   WmIpc::Message msg;
1118   WmIpc::instance()->DecodeMessage(*event, &msg);
1119   if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK) {
1120     OnWindowManagerReady();
1121   }
1122 }
1123 
OnWindowManagerReady()1124 void ScreenLocker::OnWindowManagerReady() {
1125   DVLOG(1) << "OnClientEvent: drawn for lock";
1126   drawn_ = true;
1127   if (input_grabbed_)
1128     ScreenLockReady();
1129 }
1130 
ShowErrorBubble(const std::wstring & message,BubbleBorder::ArrowLocation arrow_location)1131 void ScreenLocker::ShowErrorBubble(const std::wstring& message,
1132                                    BubbleBorder::ArrowLocation arrow_location) {
1133   if (error_info_)
1134     error_info_->Close();
1135 
1136   gfx::Rect rect = screen_lock_view_->GetPasswordBoundsRelativeTo(
1137       lock_widget_->GetRootView());
1138   gfx::Rect lock_widget_bounds = lock_widget_->GetClientAreaScreenBounds();
1139   rect.Offset(lock_widget_bounds.x(), lock_widget_bounds.y());
1140   error_info_ = MessageBubble::ShowNoGrab(
1141       lock_window_,
1142       rect,
1143       arrow_location,
1144       ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING),
1145       message,
1146       std::wstring(),  // TODO(nkostylev): Add help link.
1147       this);
1148 
1149   if (mouse_event_relay_.get())
1150     MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get());
1151   mouse_event_relay_.reset(
1152       new MouseEventRelay(lock_widget_->GetNativeView()->window,
1153                           error_info_->GetNativeView()->window));
1154   MessageLoopForUI::current()->AddObserver(mouse_event_relay_.get());
1155 }
1156 
StopScreenSaver()1157 void ScreenLocker::StopScreenSaver() {
1158   if (background_view_->IsScreenSaverVisible()) {
1159     VLOG(1) << "StopScreenSaver";
1160     background_view_->HideScreenSaver();
1161     if (screen_lock_view_) {
1162       screen_lock_view_->SetVisible(true);
1163       screen_lock_view_->RequestFocus();
1164     }
1165     EnableInput();
1166   }
1167 }
1168 
StartScreenSaver()1169 void ScreenLocker::StartScreenSaver() {
1170   if (!background_view_->IsScreenSaverVisible()) {
1171     VLOG(1) << "StartScreenSaver";
1172     UserMetrics::RecordAction(
1173         UserMetricsAction("ScreenLocker_StartScreenSaver"));
1174     background_view_->ShowScreenSaver();
1175     if (screen_lock_view_) {
1176       screen_lock_view_->SetEnabled(false);
1177       screen_lock_view_->SetVisible(false);
1178     }
1179     ClearErrors();
1180   }
1181 }
1182 
AcceleratorPressed(const views::Accelerator & accelerator)1183 bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) {
1184   if (!background_view_->IsScreenSaverVisible()) {
1185     StartScreenSaver();
1186     return true;
1187   }
1188   return false;
1189 }
1190 
1191 }  // namespace chromeos
1192