Home | History | Annotate | Download | only in login
      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:
     77   ScreenLockObserver() {
     78     registrar_.Add(this, NotificationType::LOGIN_USER_CHANGED,
     79                    NotificationService::AllSources());
     80   }
     81 
     82   // NotificationObserver overrides:
     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 
     94   virtual void LockScreen(chromeos::ScreenLockLibrary* obj) {
     95     VLOG(1) << "In: ScreenLockObserver::LockScreen";
     96     SetupInputMethodsForScreenLocker();
     97     chromeos::ScreenLocker::Show();
     98   }
     99 
    100   virtual void UnlockScreen(chromeos::ScreenLockLibrary* obj) {
    101     RestoreInputMethods();
    102     chromeos::ScreenLocker::Hide();
    103   }
    104 
    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).
    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 
    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:
    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.
    209   virtual gboolean OnKeyEvent(GtkWidget* widget, GdkEventKey* event) {
    210     // Don't handle key event in the lock window.
    211     return false;
    212   }
    213 
    214   virtual void OnDestroy(GtkWidget* object) {
    215     VLOG(1) << "OnDestroy: LockWindow destroyed";
    216     views::WidgetGtk::OnDestroy(object);
    217   }
    218 
    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.
    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:
    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.
    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:
    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:
    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 
    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 
    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 
    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 
    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:
    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.
    371   void FailedWithGrabAlreadyGrabbed() {
    372     LOG(FATAL) << "Grab already grabbed";
    373   }
    374   void FailedWithGrabInvalidTime() {
    375     LOG(FATAL) << "Grab invalid time";
    376   }
    377   void FailedWithGrabNotViewable() {
    378     LOG(FATAL) << "Grab not viewable";
    379   }
    380   void FailedWithGrabFrozen() {
    381     LOG(FATAL) << "Grab frozen";
    382   }
    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 
    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 
    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:
    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 
    523   virtual ScreenMode GetScreenMode() const {
    524     return kScreenLockerMode;
    525   }
    526 
    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:
    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:
    568   MouseEventRelay(GdkWindow* src, GdkWindow* dest)
    569       : src_(src),
    570         dest_(dest),
    571         initialized_(false) {
    572     DCHECK(src_);
    573     DCHECK(dest_);
    574   }
    575 
    576   virtual void WillProcessEvent(GdkEvent* event) {}
    577 
    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:
    630   explicit InputEventObserver(ScreenLocker* screen_locker)
    631       : screen_locker_(screen_locker),
    632         activated_(false) {
    633   }
    634 
    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 
    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:
    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 
    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 
    682   virtual void DidProcessEvent(GdkEvent* event) {
    683   }
    684 
    685  private:
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    920 void ScreenLocker::ClearErrors() {
    921   if (error_info_) {
    922     error_info_->Close();
    923     error_info_ = NULL;
    924   }
    925 }
    926 
    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 
    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 
    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 
    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 
    982 void ScreenLocker::OnGrabInputs() {
    983   DVLOG(1) << "OnGrabInputs";
    984   input_grabbed_ = true;
    985   if (drawn_)
    986     ScreenLockReady();
    987 }
    988 
    989 views::View* ScreenLocker::GetViewByID(int id) {
    990   return lock_widget_->GetRootView()->GetViewByID(id);
    991 }
    992 
    993 // static
    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
   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
   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
   1049 void ScreenLocker::InitClass() {
   1050   g_screen_lock_observer.Get();
   1051 }
   1052 
   1053 ////////////////////////////////////////////////////////////////////////////////
   1054 // ScreenLocker, private:
   1055 
   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 
   1085 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) {
   1086   authenticator_ = authenticator;
   1087 }
   1088 
   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 
   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 
   1124 void ScreenLocker::OnWindowManagerReady() {
   1125   DVLOG(1) << "OnClientEvent: drawn for lock";
   1126   drawn_ = true;
   1127   if (input_grabbed_)
   1128     ScreenLockReady();
   1129 }
   1130 
   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 
   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 
   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 
   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