diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 02:51:42 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-08 02:51:42 +0000 |
commit | 93c7843a5f99fe5e0d9665d58f1ba1deaba131ba (patch) | |
tree | 147ba83f7c0d01ba4b93f1e5e04fb09ea3c58588 | |
parent | 8ddfe30967dde6a7297d691c0e72ad8e7b9114e7 (diff) | |
download | chromium_src-93c7843a5f99fe5e0d9665d58f1ba1deaba131ba.zip chromium_src-93c7843a5f99fe5e0d9665d58f1ba1deaba131ba.tar.gz chromium_src-93c7843a5f99fe5e0d9665d58f1ba1deaba131ba.tar.bz2 |
* Show error message when unlocking failed.
* Add "sign out" button
* Let chrome crash when grab is borken.
* send ScreenIsLocked signal after all inputs are grabbed.
BUG=chromium-os:2914,chromium-os:2614
TEST=manual:
set the content of /var/lib/power_manager/use_xscreensaver to 1,
reboot the machine and login
hit ctr-alt-l to enter screenlock mode
1) typing in wrong password should show message bubble with error message
2) pressing "Sign out" will terminate the session and move to login screen
Review URL: http://codereview.chromium.org/2685006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49141 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_lock_view.cc | 103 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_lock_view.h | 9 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_locker.cc | 186 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_locker.h | 32 |
5 files changed, 313 insertions, 23 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 33b2bcc..c39ae8dc 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -7470,6 +7470,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_LOGIN_BUTTON"> Sign in </message> + <message name="IDS_SCREEN_LOCK_ACTIVE_USER"> + Active user + </message> + <message name="IDS_SCREEN_LOCK_SIGN_OUT"> + Sign out + </message> <message name="IDS_CREATE_ACCOUNT_BUTTON"> Create a Google Account now </message> diff --git a/chrome/browser/chromeos/login/screen_lock_view.cc b/chrome/browser/chromeos/login/screen_lock_view.cc index 1aa7bca..2c59cf0 100644 --- a/chrome/browser/chromeos/login/screen_lock_view.cc +++ b/chrome/browser/chromeos/login/screen_lock_view.cc @@ -12,17 +12,21 @@ #include "chrome/common/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" +#include "views/background.h" #include "views/controls/image_view.h" #include "views/controls/label.h" #include "views/grid_layout.h" namespace { +// TODO(oshima): Refactor this and UserContoller and use the +// same codebase to show the user's image. + // Max image size. const int kMaxImageSize = 260; // Gap between edge and image view, and image view and controls. -const int kBorderSize = 30; +const int kBorderSize = 5; // Background color. const SkColor kBackgroundColor = SK_ColorWHITE; @@ -30,14 +34,81 @@ const SkColor kBackgroundColor = SK_ColorWHITE; // Text color. const SkColor kTextColor = SK_ColorWHITE; +// Background color of the login status area. +const SkColor kSignoutBackgroundColor = 0xFF007700; + +// Left margin to the "Active User" text. +const int kSignoutLeftOffset = 10; + +// Command tag for buttons on the lock screen. +enum Command { + UNLOCK, + SIGN_OUT, +}; + } // namespace namespace chromeos { using views::GridLayout; +// The view that shows the Sign out button below the user's image. +class SignoutView : public views::View { + public: + explicit SignoutView(chromeos::ScreenLockView* lock_view) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + const gfx::Font& font = rb.GetFont(ResourceBundle::SmallFont); + + active_user_label_ = new views::Label( + l10n_util::GetString(IDS_SCREEN_LOCK_ACTIVE_USER)); + active_user_label_->SetFont(font); + active_user_label_->SetColor(kTextColor); + + signout_button_ = new views::TextButton( + lock_view, l10n_util::GetString(IDS_SCREEN_LOCK_SIGN_OUT)); + signout_button_->SetFont(font); + signout_button_->SetEnabledColor(kTextColor); + signout_button_->SetNormalHasBorder(false); + signout_button_->set_tag(SIGN_OUT); + + AddChildView(active_user_label_); + AddChildView(signout_button_); + + set_background(views::Background::CreateSolidBackground( + kSignoutBackgroundColor)); + } + + // views::View overrides. + virtual void Layout() { + gfx::Size label = active_user_label_->GetPreferredSize(); + gfx::Size button = signout_button_->GetPreferredSize(); + active_user_label_->SetBounds(kSignoutLeftOffset, + (height() - label.height()) / 2, + label.width(), label.height()); + signout_button_->SetBounds( + width() - button.width(), (height() - button.height()) / 2, + button.width(), button.height()); + } + + virtual gfx::Size GetPreferredSize() { + gfx::Size label = active_user_label_->GetPreferredSize(); + gfx::Size button = signout_button_->GetPreferredSize(); + return gfx::Size(label.width() + button.width(), + std::max(label.height(), button.height())); + } + + private: + friend class ScreenLockView; + + views::Label* active_user_label_; + views::TextButton* signout_button_; + + DISALLOW_COPY_AND_ASSIGN(SignoutView); +}; + ScreenLockView::ScreenLockView(ScreenLocker* screen_locker) - : image_view_(NULL), + : signout_view_(NULL), + image_view_(NULL), password_field_(NULL), unlock_button_(NULL), screen_locker_(screen_locker) { @@ -53,6 +124,8 @@ void ScreenLockView::Init() { main->set_background( views::Background::CreateSolidBackground(kBackgroundColor)); + signout_view_ = new SignoutView(this); + // Password field. password_field_ = new views::Textfield(views::Textfield::STYLE_PASSWORD); password_field_->set_text_to_display_when_empty( @@ -63,6 +136,7 @@ void ScreenLockView::Init() { // TODO(sky|oshima): change ids unlock_button_ = new views::TextButton( this, l10n_util::GetString(IDS_LOGIN_BUTTON)); + unlock_button_->set_tag(UNLOCK); // User icon. image_view_ = new views::ImageView(); @@ -99,6 +173,8 @@ void ScreenLockView::Init() { layout->AddPaddingRow(0, kBorderSize); layout->StartRow(0, 0); layout->AddView(image_view_); + layout->StartRow(0, 0); + layout->AddView(signout_view_); layout->AddPaddingRow(0, kBorderSize); layout->StartRow(0, 1); layout->AddView(password_field_); @@ -129,6 +205,16 @@ void ScreenLockView::ClearAndSetFocusToPassword() { password_field_->SetText(string16()); } +void ScreenLockView::SetSignoutEnabled(bool enabled) { + signout_view_->signout_button_->SetEnabled(enabled); +} + +gfx::Rect ScreenLockView::GetPasswordBoundsRelativeTo(const views::View* view) { + gfx::Point p; + views::View::ConvertPointToView(password_field_, view, &p); + return gfx::Rect(p, size()); +} + void ScreenLockView::SetEnabled(bool enabled) { views::View::SetEnabled(enabled); @@ -143,12 +229,22 @@ void ScreenLockView::SetEnabled(bool enabled) { void ScreenLockView::ButtonPressed(views::Button* sender, const views::Event& event) { - screen_locker_->Authenticate(password_field_->text()); + switch (sender->tag()) { + case UNLOCK: + screen_locker_->Authenticate(password_field_->text()); + break; + case SIGN_OUT: + screen_locker_->Signout(); + break; + default: + NOTREACHED(); + } } bool ScreenLockView::HandleKeystroke( views::Textfield* sender, const views::Textfield::Keystroke& keystroke) { + screen_locker_->ClearErrors(); if (keystroke.GetKeyboardCode() == base::VKEY_RETURN) { screen_locker_->Authenticate(password_field_->text()); return true; @@ -183,4 +279,3 @@ void ScreenLockView::SetImage(const SkBitmap& image, } } // namespace chromeos - diff --git a/chrome/browser/chromeos/login/screen_lock_view.h b/chrome/browser/chromeos/login/screen_lock_view.h index 49de5a2..c496390 100644 --- a/chrome/browser/chromeos/login/screen_lock_view.h +++ b/chrome/browser/chromeos/login/screen_lock_view.h @@ -18,6 +18,7 @@ class ImageView; namespace chromeos { class ScreenLocker; +class SignoutView; namespace test { class ScreenLockerTester; @@ -38,6 +39,12 @@ class ScreenLockView : public views::View, // Clears and sets the focus to the password field. void ClearAndSetFocusToPassword(); + // Enable/Disable signout button. + void SetSignoutEnabled(bool enabled); + + // Returns the bounds of the password field in ScreenLocker's coordinate. + gfx::Rect GetPasswordBoundsRelativeTo(const views::View* view); + // views::View implementation: virtual void SetEnabled(bool enabled); @@ -63,6 +70,8 @@ class ScreenLockView : public views::View, int desired_width, int desired_height); + SignoutView* signout_view_; + // For editing the password. views::ImageView* image_view_; views::Textfield* password_field_; diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 60540c7..bba8f61 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -6,23 +6,33 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "base/message_loop.h" #include "base/singleton.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_list.h" #include "chrome/browser/chromeos/cros/screen_lock_library.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/background_view.h" #include "chrome/browser/chromeos/login/login_utils.h" +#include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/screen_lock_view.h" +#include "chrome/browser/metrics/user_metrics.h" #include "chrome/common/notification_service.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" #include "views/screen.h" +#include "views/widget/root_view.h" #include "views/widget/widget_gtk.h" +#include "chrome/browser/chromeos/wm_ipc.h" +#include "third_party/cros/chromeos_wm_ipc_enums.h" + namespace { // The maxium times that the screen locker should try to grab input, // and its interval. It has to be able to grab all inputs in 30 seconds, // otherwise chromium process fails and the session is terminated. -const int64 kRetryGrabIntervalMs = 1000; -const int kGrabFailureLimit = 30; +const int64 kRetryGrabIntervalMs = 500; +const int kGrabFailureLimit = 60; // Observer to start ScreenLocker when the screen lock class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, @@ -63,19 +73,13 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver); }; -} // namespace - -namespace chromeos { - -// static -ScreenLocker* ScreenLocker::screen_locker_ = NULL; - // A child widget that grabs both keyboard and pointer input. // TODO(oshima): catch grab-broke event and quit if it ever happenes. class GrabWidget : public views::WidgetGtk { public: - GrabWidget() + explicit GrabWidget(chromeos::ScreenLocker* screen_locker) : views::WidgetGtk(views::WidgetGtk::TYPE_CHILD), + screen_locker_(screen_locker), ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), grab_failure_count_(0), kbd_grab_status_(GDK_GRAB_INVALID_TIME), @@ -88,17 +92,34 @@ class GrabWidget : public views::WidgetGtk { if (current_grab_window) gtk_grab_remove(current_grab_window); - DoGrab(); + gtk_grab_add(window_contents()); // Now steal all inputs. TryGrabAllInputs(); } + virtual gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event) { + WidgetGtk::OnButtonPress(widget, event); + // Never propagate event to parent. + return true; + } + // Try to grab all inputs. It initiates another try if it fails to // grab and the retry count is within a limit, or fails with CHECK. void TryGrabAllInputs(); private: + virtual void HandleGrabBroke() { + // Input should never be stolen from ScreenLocker once it's + // grabbed. If this happens, it's a bug and has to be fixed. We + // let chrome crash to get a crash report and dump, and + // SessionManager will terminate the session to logout. + CHECK(kbd_grab_status_ != GDK_GRAB_SUCCESS || + kbd_grab_status_ != GDK_GRAB_SUCCESS) + << "Grab Broke. quitting"; + } + + chromeos::ScreenLocker* screen_locker_; ScopedRunnableMethodFactory<GrabWidget> task_factory_; // The number times the widget tried to grab all focus. @@ -140,6 +161,8 @@ void GrabWidget::TryGrabAllInputs() { << "Failed to grab keyboard input:" << kbd_grab_status_; CHECK_EQ(GDK_GRAB_SUCCESS, mouse_grab_status_) << "Failed to grab pointer input:" << mouse_grab_status_; + DLOG(INFO) << "Grab Success"; + screen_locker_->ScreenLockReady(); } } @@ -147,16 +170,79 @@ void GrabWidget::TryGrabAllInputs() { namespace chromeos { +// static +ScreenLocker* ScreenLocker::screen_locker_ = NULL; + +// A event observer that forwards gtk events from one window to another. +// See screen_locker.h for more details. +class MouseEventRelay : public MessageLoopForUI::Observer { + public: + MouseEventRelay(GdkWindow* src, GdkWindow* dest) : src_(src), dest_(dest) { + DCHECK(src_); + DCHECK(dest_); + gint src_x, src_y, dest_x, dest_y, width, height, depth; + gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth); + gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth); + + offset_.SetPoint(dest_x - src_x, dest_y - src_y); + } + + virtual void WillProcessEvent(GdkEvent* event) {} + + virtual void DidProcessEvent(GdkEvent* event) { + if (event->any.window != src_) { + DLOG(INFO) << "ignore event src non grab window: " << event->type; + return; + } + + if (event->type == GDK_BUTTON_PRESS || + event->type == GDK_BUTTON_RELEASE) { + GdkEvent* copy = gdk_event_copy(event); + copy->button.window = dest_; + g_object_ref(copy->button.window); + copy->button.x -= offset_.x(); + copy->button.y -= offset_.y(); + + gdk_event_put(copy); + gdk_event_free(copy); + } else if (event->type == GDK_MOTION_NOTIFY) { + GdkEvent* copy = gdk_event_copy(event); + copy->button.window = dest_; + g_object_ref(copy->button.window); + copy->motion.x -= offset_.x(); + copy->motion.y -= offset_.y(); + + gdk_event_put(copy); + gdk_event_free(copy); + } + } + + private: + GdkWindow* src_; + GdkWindow* dest_; + + // Offset from src_'s origin to dest_'s origin. + gfx::Point offset_; + + DISALLOW_COPY_AND_ASSIGN(MouseEventRelay); +}; + +} // namespace chromeos + +namespace chromeos { + ScreenLocker::ScreenLocker(const UserManager::User& user) : lock_window_(NULL), lock_widget_(NULL), screen_lock_view_(NULL), - user_(user) { + user_(user), + error_info_(NULL) { DCHECK(!screen_locker_); screen_locker_ = this; } ScreenLocker::~ScreenLocker() { + ClearErrors(); DCHECK(lock_window_); lock_window_->Close(); // lock_widget_ will be deleted by gtk's destroy signal. @@ -170,6 +256,11 @@ void ScreenLocker::Init(const gfx::Rect& bounds) { views::View* screen = new BackgroundView(); lock_window_ = new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP); lock_window_->Init(NULL, bounds); + DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); + WmIpc::instance()->SetWindowType( + lock_window_->GetNativeView(), + WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, + NULL); lock_window_->SetContentsView(screen); lock_window_->Show(); @@ -179,7 +270,7 @@ void ScreenLocker::Init(const gfx::Rect& bounds) { gfx::Size size = screen_lock_view_->GetPreferredSize(); - lock_widget_ = new GrabWidget(); + lock_widget_ = new GrabWidget(this); lock_widget_->MakeTransparent(); lock_widget_->InitWithWidget(lock_window_, gfx::Rect((bounds.width() - size.width()) / 2, @@ -187,8 +278,8 @@ void ScreenLocker::Init(const gfx::Rect& bounds) { size.width(), size.height())); lock_widget_->SetContentsView(screen_lock_view_); + lock_widget_->GetRootView()->SetVisible(false); lock_widget_->Show(); - screen_lock_view_->ClearAndSetFocusToPassword(); } void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { @@ -198,6 +289,34 @@ void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { void ScreenLocker::OnLoginFailure(const std::string& error) { DLOG(INFO) << "OnLoginFailure"; EnableInput(); + // Don't enable signout button here as we're showing + // MessageBubble. + gfx::Rect rect = screen_lock_view_->GetPasswordBoundsRelativeTo( + lock_widget_->GetRootView()); + gfx::Rect lock_widget_bounds; + lock_widget_->GetBounds(&lock_widget_bounds, false); + rect.Offset(lock_widget_bounds.x(), lock_widget_bounds.y()); + + if (error_info_) + error_info_->Close(); + std::wstring msg = l10n_util::GetString(IDS_LOGIN_ERROR_AUTHENTICATING); + if (!error.empty()) + msg += L"\n" + ASCIIToWide(error); + + error_info_ = MessageBubble::ShowNoGrab( + lock_window_, + rect, + BubbleBorder::BOTTOM_LEFT, + ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), + msg, + this); + if (mouse_event_relay_.get()) { + MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get()); + } + mouse_event_relay_.reset( + new MouseEventRelay(lock_widget_->GetNativeView()->window, + error_info_->GetNativeView()->window)); + MessageLoopForUI::current()->AddObserver(mouse_event_relay_.get()); } void ScreenLocker::OnLoginSuccess(const std::string& username, @@ -208,8 +327,19 @@ void ScreenLocker::OnLoginSuccess(const std::string& username, CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenUnlockRequested(); } +void ScreenLocker::InfoBubbleClosing(InfoBubble* info_bubble, + bool closed_by_escape) { + error_info_ = NULL; + screen_lock_view_->SetSignoutEnabled(true); + if (mouse_event_relay_.get()) { + MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get()); + mouse_event_relay_.reset(); + } +} + void ScreenLocker::Authenticate(const string16& password) { screen_lock_view_->SetEnabled(false); + screen_lock_view_->SetSignoutEnabled(false); ChromeThread::PostTask( ChromeThread::FILE, FROM_HERE, NewRunnableMethod(authenticator_.get(), @@ -218,11 +348,36 @@ void ScreenLocker::Authenticate(const string16& password) { UTF16ToUTF8(password))); } +void ScreenLocker::ClearErrors() { + if (error_info_) { + error_info_->Close(); + error_info_ = NULL; + } +} + void ScreenLocker::EnableInput() { screen_lock_view_->SetEnabled(true); screen_lock_view_->ClearAndSetFocusToPassword(); } +void ScreenLocker::Signout() { + if (!error_info_) { + // TODO(oshima): record this action in user metrics. + BrowserList::CloseAllBrowsersAndExit(); + + // Don't hide yet the locker because the chrome screen may become visible + // briefly. + } +} + +void ScreenLocker::ScreenLockReady() { + // Don't show the password field until we grab all inputs. + lock_widget_->GetRootView()->SetVisible(true); + EnableInput(); + if (CrosLibrary::Get()->EnsureLoaded()) + CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted(); +} + // static void ScreenLocker::Show() { DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); @@ -236,9 +391,6 @@ void ScreenLocker::Show() { new ScreenLocker(UserManager::Get()->logged_in_user()); locker->Init(bounds); } - // TODO(oshima): Wait for a message from WM to complete the process. - if (CrosLibrary::Get()->EnsureLoaded()) - CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted(); } // static diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index 2a2a81d..9d0a894 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -10,6 +10,7 @@ #include "base/task.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/user_manager.h" +#include "chrome/browser/views/info_bubble.h" namespace gfx { class Rect; @@ -22,6 +23,8 @@ class WidgetGtk; namespace chromeos { class Authenticator; +class MessageBubble; +class MouseEventRelay; class ScreenLockView; namespace test { @@ -31,7 +34,8 @@ class ScreenLockerTester; // ScreenLocker creates a background view as well as ScreenLockView to // authenticate the user. ScreenLocker manages its life cycle and will // delete itself when it's unlocked. -class ScreenLocker : public LoginStatusConsumer { +class ScreenLocker : public LoginStatusConsumer, + public InfoBubbleDelegate { public: explicit ScreenLocker(const UserManager::User& user); @@ -43,12 +47,27 @@ class ScreenLocker : public LoginStatusConsumer { virtual void OnLoginSuccess(const std::string& username, const std::string& credentials); + // Overridden from views::InfoBubbleDelegate. + virtual void InfoBubbleClosing(InfoBubble* info_bubble, + bool closed_by_escape); + virtual bool CloseOnEscape() { return true; } + virtual bool FadeInOnShow() { return false; } + // Authenticates the user with given |password| and authenticator. void Authenticate(const string16& password); + // Close message bubble to clear error messages. + void ClearErrors(); + // (Re)enable input field. void EnableInput(); + // Exit the chrome, which will sign out the current session. + void Signout(); + + // Called when the screen locker is ready. + void ScreenLockReady(); + // Returns the user to authenticate. const UserManager::User& user() const { return user_; @@ -92,9 +111,18 @@ class ScreenLocker : public LoginStatusConsumer { // Logged in user. UserManager::User user_; - // Used for logging in. + // Used to authenticate the user to unlock. scoped_refptr<Authenticator> authenticator_; + // ScreenLocker grabs all keyboard and mouse events on its + // gdk window and never let other gdk_window to handle inputs. + // This MouseEventRelay object is used to forward events to + // the message bubble's gdk_window so that close button works. + scoped_ptr<MouseEventRelay> mouse_event_relay_; + + // An info bubble to display login failure message. + MessageBubble* error_info_; + // Reference to the single instance of the screen locker object. // This is used to make sure there is only one screen locker instance. static ScreenLocker* screen_locker_; |