diff options
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r-- | chrome/browser/chromeos/login/captcha_view.cc | 119 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/captcha_view.h | 28 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/existing_user_controller.cc | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/helper.cc | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/helper.h | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/login_performer.cc | 9 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_locker.cc | 84 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screen_locker.h | 38 |
8 files changed, 256 insertions, 31 deletions
diff --git a/chrome/browser/chromeos/login/captcha_view.cc b/chrome/browser/chromeos/login/captcha_view.cc index 7c55cae..de66b3e 100644 --- a/chrome/browser/chromeos/login/captcha_view.cc +++ b/chrome/browser/chromeos/login/captcha_view.cc @@ -7,10 +7,16 @@ #include "app/l10n_util.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/login/helper.h" #include "chrome/browser/chromeos/login/image_downloader.h" +#include "chrome/browser/chromeos/login/rounded_rect_painter.h" +#include "chrome/browser/chromeos/login/textfield_with_margin.h" +#include "chrome/browser/chromeos/views/copy_background.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" +#include "views/background.h" +#include "views/controls/button/text_button.h" #include "views/controls/image_view.h" #include "views/controls/label.h" #include "views/grid_layout.h" @@ -24,11 +30,57 @@ using views::WidgetGtk; namespace chromeos { -CaptchaView::CaptchaView(const GURL& captcha_url) +namespace { + +// A Textfield for captcha input, which also sets focus to itself +// when a mouse is clicked on it. This is necessary in screen locker mode +// as mouse events are grabbed in the screen locker. +class CaptchaField : public TextfieldWithMargin { + public: + CaptchaField() + : TextfieldWithMargin() { + } + + // views::View overrides. + virtual bool OnMousePressed(const views::MouseEvent& e) { + RequestFocus(); + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(CaptchaField); +}; + +class WideTextButton : public views::TextButton { + public: + WideTextButton(views::ButtonListener* listener, const std::wstring& text) + : TextButton(listener, text) { + SetFont(font().DeriveFont(kFontSizeCorrectionDelta)); + } + + virtual ~WideTextButton() {} + + private: + virtual gfx::Size GetPreferredSize() { + gfx::Size preferred_size = TextButton::GetPreferredSize(); + // Set minimal width. + if (preferred_size.width() < login::kButtonMinWidth) + preferred_size.set_width(login::kButtonMinWidth); + return preferred_size; + } + + DISALLOW_COPY_AND_ASSIGN(WideTextButton); +}; + +} // namespace + +CaptchaView::CaptchaView(const GURL& captcha_url, bool is_standalone) : delegate_(NULL), captcha_url_(captcha_url), captcha_image_(NULL), - captcha_textfield_(NULL) { + captcha_textfield_(NULL), + is_standalone_(is_standalone), + ok_button_(NULL) { } bool CaptchaView::Accept() { @@ -41,38 +93,67 @@ std::wstring CaptchaView::GetWindowTitle() const { return l10n_util::GetString(IDS_LOGIN_CAPTCHA_DIALOG_TITLE); } +void CaptchaView::SetCaptchaURL(const GURL& captcha_url) { + captcha_url_ = captcha_url; + captcha_textfield_->SetText(string16()); + // ImageDownloader will delete itself once URL is fetched. + // TODO(nkostylev): Make sure that it works after view is deleted. + new ImageDownloader(this, GURL(captcha_url_), std::string()); +} + gfx::Size CaptchaView::GetPreferredSize() { // TODO(nkostylev): Once UI is finalized, create locale settings. - return gfx::Size(views::Window::GetLocalizedContentsSize( + gfx::Size size = gfx::Size(views::Window::GetLocalizedContentsSize( IDS_CAPTCHA_INPUT_DIALOG_WIDTH_CHARS, IDS_CAPTCHA_INPUT_DIALOG_HEIGHT_LINES)); + if (is_standalone_) + size.set_height(size.height() + ok_button_->GetPreferredSize().height()); + + return size; } void CaptchaView::ViewHierarchyChanged(bool is_add, views::View* parent, views::View* child) { - // Can't init before we're inserted into a Container, because we require - // a HWND to parent native child controls to. + // Can't focus before we're inserted into a Container. if (is_add && child == this) - Init(); + captcha_textfield_->RequestFocus(); } bool CaptchaView::HandleKeystroke(views::Textfield* sender, const views::Textfield::Keystroke& keystroke) { if (sender == captcha_textfield_ && keystroke.GetKeyboardCode() == app::VKEY_RETURN) { - GetDialogClientView()->AcceptWindow(); + if (is_standalone_) { + Accept(); + } else { + GetDialogClientView()->AcceptWindow(); + } } return false; } void CaptchaView::OnImageDecoded(const SkBitmap& decoded_image) { captcha_image_->SetImage(decoded_image); - SchedulePaint(); + captcha_textfield_->RequestFocus(); Layout(); } +void CaptchaView::ButtonPressed(views::Button* sender, + const views::Event& event) { + DCHECK(sender == ok_button_); + Accept(); +} + void CaptchaView::Init() { + if (is_standalone_) { + // Use rounded rect background. + set_border(CreateWizardBorder(&BorderDefinition::kUserBorder)); + views::Painter* painter = CreateWizardPainter( + &BorderDefinition::kUserBorder); + set_background(views::Background::CreateBackgroundPainter(true, painter)); + } + views::GridLayout* layout = CreatePanelGridLayout(this); SetLayoutManager(layout); @@ -93,9 +174,10 @@ void CaptchaView::Init() { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); layout->StartRow(0, column_view_set_id); - captcha_textfield_ = new views::Textfield( - views::Textfield::STYLE_DEFAULT); + captcha_textfield_ = new CaptchaField(); captcha_textfield_->SetController(this); + if (is_standalone_) + captcha_textfield_->set_background(new CopyBackground(this)); layout->AddView(captcha_textfield_); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); @@ -106,9 +188,22 @@ void CaptchaView::Init() { layout->AddView(label); layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - captcha_textfield_->RequestFocus(); + if (is_standalone_) { + layout->StartRow(0, column_view_set_id); + ok_button_ = new WideTextButton(this, l10n_util::GetString(IDS_OK)); + ok_button_->set_alignment(views::TextButton::ALIGN_CENTER); + ok_button_->SetFocusable(true); + ok_button_->SetNormalHasBorder(true); + ok_button_->set_animate_on_state_change(false); + ok_button_->SetEnabledColor(SK_ColorBLACK); + ok_button_->SetHighlightColor(SK_ColorBLACK); + ok_button_->SetHoverColor(SK_ColorBLACK); + layout->AddView(ok_button_, 1, 1, + views::GridLayout::CENTER, views::GridLayout::CENTER); + } - // ImageDownloader will disable itself once URL is fetched. + // ImageDownloader will delete itself once URL is fetched. + // TODO(nkostylev): Make sure that it works after view is deleted. new ImageDownloader(this, GURL(captcha_url_), std::string()); } diff --git a/chrome/browser/chromeos/login/captcha_view.h b/chrome/browser/chromeos/login/captcha_view.h index 491c438..37de9f94b 100644 --- a/chrome/browser/chromeos/login/captcha_view.h +++ b/chrome/browser/chromeos/login/captcha_view.h @@ -10,11 +10,13 @@ #include "chrome/browser/chromeos/login/image_decoder.h" #include "googleurl/src/gurl.h" +#include "views/controls/button/button.h" #include "views/controls/textfield/textfield.h" #include "views/window/dialog_delegate.h" namespace views { class ImageView; +class TextButton; class View; class Window; } // namespace views @@ -25,7 +27,8 @@ namespace chromeos { class CaptchaView : public views::View, public views::DialogDelegate, public views::Textfield::Controller, - public ImageDecoder::Delegate { + public ImageDecoder::Delegate, + public views::ButtonListener { public: class Delegate { public: @@ -37,7 +40,8 @@ class CaptchaView : public views::View, }; // |captcha_url| represents CAPTCHA image URL. - explicit CaptchaView(const GURL& captcha_url); + // |is_standalone| is true when CaptchaView is not presented as dialog. + CaptchaView(const GURL& captcha_url, bool is_standalone); virtual ~CaptchaView() {} // views::DialogDelegate overrides: @@ -59,10 +63,20 @@ class CaptchaView : public views::View, // Overriden from ImageDownloader::Delegate: virtual void OnImageDecoded(const SkBitmap& decoded_image); + // Overridden from views::ButtonListener. + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + // Initializes UI. + void Init(); + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + // Instructs to download and display another captcha image. + // Is used when same CaptchaView is reused. + void SetCaptchaURL(const GURL& captcha_url); + protected: // views::View overrides: virtual gfx::Size GetPreferredSize(); @@ -71,14 +85,18 @@ class CaptchaView : public views::View, views::View* child); private: - // Initializes UI. - void Init(); - Delegate* delegate_; GURL captcha_url_; views::ImageView* captcha_image_; views::Textfield* captcha_textfield_; + // True when view is not hosted inside dialog, + // thus should draw OK button/background. + bool is_standalone_; + + // Used in standalone mode. + views::TextButton* ok_button_; + DISALLOW_COPY_AND_ASSIGN(CaptchaView); }; diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 501831f..83aa156 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -413,7 +413,8 @@ void ExistingUserController::OnLoginFailure(const LoginFailure& failure) { failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) { if (!failure.error().captcha().image_url.is_empty()) { CaptchaView* view = - new CaptchaView(failure.error().captcha().image_url); + new CaptchaView(failure.error().captcha().image_url, false); + view->Init(); view->set_delegate(this); views::Window* window = browser::CreateViewsWindow( GetNativeWindow(), gfx::Rect(), view); diff --git a/chrome/browser/chromeos/login/helper.cc b/chrome/browser/chromeos/login/helper.cc index 188a3ce..2cfc600 100644 --- a/chrome/browser/chromeos/login/helper.cc +++ b/chrome/browser/chromeos/login/helper.cc @@ -169,9 +169,6 @@ GURL GetAccountRecoveryHelpUrl() { namespace login { -// Minimal width for the button. -const int kButtonMinWidth = 90; - gfx::Size WideButton::GetPreferredSize() { gfx::Size preferred_size = NativeButton::GetPreferredSize(); // Set minimal width. diff --git a/chrome/browser/chromeos/login/helper.h b/chrome/browser/chromeos/login/helper.h index 1c3851a..b613032 100644 --- a/chrome/browser/chromeos/login/helper.h +++ b/chrome/browser/chromeos/login/helper.h @@ -133,6 +133,9 @@ const int kUserCornerRadius = 6; const int kSelectedLabelHeight = 25; const int kUnselectedLabelHeight = 20; +// Minimal width for the button. +const int kButtonMinWidth = 90; + class WideButton : public views::NativeButton { public: WideButton(views::ButtonListener* listener, const std::wstring& text) diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index d1b2281..02e7b46 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -393,10 +393,11 @@ void LoginPerformer::ResolveLockNetworkAuthFailure() { break; case GoogleServiceAuthError::CAPTCHA_REQUIRED: // User is requested to enter CAPTCHA challenge. - msg = l10n_util::GetString(IDS_LOGIN_ERROR_AUTHENTICATING); - // TODO(nkostylev): Instruct ScreenLocker to show CAPTCHA input. - // http://crosbug.com/9812 - break; + msg = l10n_util::GetString(IDS_LOGIN_ERROR_PASSWORD_CHANGED); + ScreenLocker::default_screen_locker()->ShowCaptchaAndErrorMessage( + last_login_failure_.error().captcha().image_url, + msg); + return; default: // Unless there's new GoogleServiceAuthError state has been added. NOTREACHED(); diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 329a5fc..29730a1 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -249,7 +249,9 @@ class LockWindow : public views::WidgetGtk { // GrabWidget's root view to layout the ScreenLockView at the center // and the Shutdown button at the right bottom. -class GrabWidgetRootView : public views::View { +class GrabWidgetRootView + : public views::View, + public chromeos::ScreenLocker::ScreenLockViewContainer { public: explicit GrabWidgetRootView(chromeos::ScreenLockView* screen_lock_view) : screen_lock_view_(screen_lock_view), @@ -266,6 +268,18 @@ class GrabWidgetRootView : public views::View { shutdown_button_->LayoutIn(this); } + // ScreenLocker::ScreenLockViewContainer implementation: + void SetScreenLockView(views::View* screen_lock_view) { + if (screen_lock_view_) { + RemoveChildView(screen_lock_view_); + } + screen_lock_view_ = screen_lock_view; + if (screen_lock_view_) { + AddChildView(0, screen_lock_view_); + } + Layout(); + } + private: views::View* screen_lock_view_; @@ -435,7 +449,9 @@ void GrabWidget::TryUngrabOtherClients() { // BackgroundView for ScreenLocker, which layouts a lock widget in // addition to other background components. -class ScreenLockerBackgroundView : public chromeos::BackgroundView { +class ScreenLockerBackgroundView + : public chromeos::BackgroundView, + public chromeos::ScreenLocker::ScreenLockViewContainer { public: ScreenLockerBackgroundView(views::WidgetGtk* lock_widget, views::View* screen_lock_view) @@ -463,6 +479,12 @@ class ScreenLockerBackgroundView : public chromeos::BackgroundView { } } + // ScreenLocker::ScreenLockViewContainer implementation: + void SetScreenLockView(views::View* screen_lock_view) { + screen_lock_view_ = screen_lock_view; + Layout(); + } + private: views::WidgetGtk* lock_widget_; @@ -617,6 +639,9 @@ ScreenLocker::ScreenLocker(const UserManager::User& user) : lock_window_(NULL), lock_widget_(NULL), screen_lock_view_(NULL), + captcha_view_(NULL), + grab_container_(NULL), + background_container_(NULL), user_(user), error_info_(NULL), drawn_(false), @@ -663,8 +688,9 @@ void ScreenLocker::Init() { lock_widget_->MakeTransparent(); lock_widget_->InitWithWidget(lock_window_, gfx::Rect()); if (screen_lock_view_) { - lock_widget_->SetContentsView( - new GrabWidgetRootView(screen_lock_view_)); + GrabWidgetRootView* root_view = new GrabWidgetRootView(screen_lock_view_); + grab_container_ = root_view; + lock_widget_->SetContentsView(root_view); } lock_widget_->Show(); @@ -672,8 +698,10 @@ void ScreenLocker::Init() { std::string url_string = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kScreenSaverUrl); - background_view_ = new ScreenLockerBackgroundView(lock_widget_, - screen_lock_view_); + ScreenLockerBackgroundView* screen_lock_background_view_ = + new ScreenLockerBackgroundView(lock_widget_, screen_lock_view_); + background_container_ = screen_lock_background_view_; + background_view_ = screen_lock_background_view_; background_view_->Init(GURL(url_string)); if (background_view_->ScreenSaverEnabled()) StartScreenSaver(); @@ -781,6 +809,29 @@ void ScreenLocker::InfoBubbleClosing(InfoBubble* info_bubble, } } +void ScreenLocker::OnCaptchaEntered(const std::string& captcha) { + // Captcha dialog is only shown when LoginPerformer instance exists, + // i.e. blocking UI after password change is in place. + DCHECK(LoginPerformer::default_performer()); + LoginPerformer::default_performer()->set_captcha(captcha); + + // ScreenLockView ownership is passed to grab_container_. + // Need to save return value here so that compile + // doesn't fail with "unused result" warning. + views::View* view = secondary_view_.release(); + view = NULL; + captcha_view_->SetVisible(false); + grab_container_->SetScreenLockView(screen_lock_view_); + background_container_->SetScreenLockView(screen_lock_view_); + screen_lock_view_->SetVisible(true); + screen_lock_view_->ClearAndSetFocusToPassword(); + + // Take CaptchaView ownership now that it's removed from grab_container_. + secondary_view_.reset(captcha_view_); + ShowErrorMessage(postponed_error_message_, false); + postponed_error_message_.clear(); +} + void ScreenLocker::Authenticate(const string16& password) { authentication_start_time_ = base::Time::Now(); screen_lock_view_->SetEnabled(false); @@ -830,6 +881,27 @@ void ScreenLocker::Signout() { } } +void ScreenLocker::ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const std::wstring& message) { + postponed_error_message_ = message; + if (captcha_view_) { + captcha_view_->SetCaptchaURL(captcha_url); + } else { + captcha_view_ = new CaptchaView(captcha_url, true); + captcha_view_->Init(); + captcha_view_->set_delegate(this); + } + // CaptchaView ownership is passed to grab_container_. + views::View* view = secondary_view_.release(); + view = NULL; + screen_lock_view_->SetVisible(false); + grab_container_->SetScreenLockView(captcha_view_); + background_container_->SetScreenLockView(captcha_view_); + captcha_view_->SetVisible(true); + // Take ScreenLockView ownership now that it's removed from grab_container_. + secondary_view_.reset(screen_lock_view_); +} + void ScreenLocker::ShowErrorMessage(const std::wstring& message, bool sign_out_only) { if (sign_out_only) { diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index 27e1404..a0332cf 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -8,8 +8,10 @@ #include <string> +#include "base/scoped_ptr.h" #include "base/task.h" #include "base/time.h" +#include "chrome/browser/chromeos/login/captcha_view.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/message_bubble.h" #include "chrome/browser/chromeos/login/user_manager.h" @@ -20,6 +22,7 @@ class Rect; } // namespace gfx namespace views { +class View; class WidgetGtk; } // namespace views @@ -43,8 +46,18 @@ class ScreenLockerTester; // delete itself when it's unlocked. class ScreenLocker : public LoginStatusConsumer, public MessageBubbleDelegate, + public CaptchaView::Delegate, public views::AcceleratorTarget { public: + // Interface that helps switching from ScreenLockView to CaptchaView. + class ScreenLockViewContainer { + public: + virtual void SetScreenLockView(views::View* screen_lock_view) = 0; + + protected: + virtual ~ScreenLockViewContainer() {} + }; + explicit ScreenLocker(const UserManager::User& user); // Returns the default instance if it has been created. @@ -69,6 +82,9 @@ class ScreenLocker : public LoginStatusConsumer, virtual bool FadeInOnShow() { return false; } virtual void OnHelpLinkActivated() {} + // CaptchaView::Delegate implementation: + virtual void OnCaptchaEntered(const std::string& captcha); + // Authenticates the user with given |password| and authenticator. void Authenticate(const string16& password); @@ -81,6 +97,11 @@ class ScreenLocker : public LoginStatusConsumer, // Exit the chrome, which will sign out the current session. void Signout(); + // Present user a CAPTCHA challenge with image from |captcha_url|, + // After that shows error bubble with |message|. + void ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const std::wstring& message); + // Disables all UI needed and shows error bubble with |message|. // If |sign_out_only| is true then all other input except "Sign Out" // button is blocked. @@ -156,6 +177,23 @@ class ScreenLocker : public LoginStatusConsumer, // A view that can display html page as background. BackgroundView* background_view_; + // View used to present CAPTCHA challenge input. + CaptchaView* captcha_view_; + + // Containers that hold currently visible view. + // Initially it's ScreenLockView instance. + // When CAPTCHA input dialog is presented it's swapped to CaptchaView + // instance, then back after CAPTCHA input is done. + ScreenLockViewContainer* grab_container_; + ScreenLockViewContainer* background_container_; + + // View that's not owned by grab_container_ - either ScreenLockView or + // CaptchaView instance. Keep that under scoped_ptr so that it's deleted. + scoped_ptr<views::View> secondary_view_; + + // Postponed error message to be shown after CAPTCHA input is done. + std::wstring postponed_error_message_; + // Logged in user. UserManager::User user_; |