diff options
30 files changed, 1989 insertions, 1041 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 4685d49..167e397 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4733,6 +4733,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_USE_MORE_WEBUI_DESCRIPTION" desc="Description for the flag to use more WebUI."> Enable experimental HTML implementations of some minor UI components such as various dialogs. </message> + <message name="IDS_FLAGS_WEBUI_LOCK_NAME" desc="Title for the flag to use WebUI lock screen."> + Use WebUI lock screen. + </message> + <message name="IDS_FLAGS_WEBUI_LOCK_DESCRIPTION" desc="Description for the flag to use WebUI lock screen."> + Use the experimental HTML implementation of the lock screen. + </message> <message name="IDS_FLAGS_ENABLE_NTP_BOOKMARK_FEATURES_NAME" desc="Title for the flag to enable NTP bookmark features."> Enable NTP bookmark features </message> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index baca9ce..b085238 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -396,6 +396,17 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(switches::kUseMoreWebUI) }, { + "webui-lock-screen", + IDS_FLAGS_WEBUI_LOCK_NAME, + IDS_FLAGS_WEBUI_LOCK_DESCRIPTION, + kOsCrOS, +#if defined(OS_CHROMEOS) + SINGLE_VALUE_TYPE(switches::kWebUILockScreen) +#else + SINGLE_VALUE_TYPE("") +#endif + }, + { "enable-ntp-bookmark-features", IDS_FLAGS_ENABLE_NTP_BOOKMARK_FEATURES_NAME, IDS_FLAGS_ENABLE_NTP_BOOKMARK_FEATURES_DESCRIPTION, diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 43376d8..58e33f5 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -130,6 +130,9 @@ <include name="IDR_KEYBOARD_OVERLAY_DATA_JS" file="resources\keyboard_overlay_data.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_KEYBOARD_OVERLAY_HTML" file="resources\keyboard_overlay.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_KEYBOARD_OVERLAY_JS" file="resources\keyboard_overlay.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCK_SCREEN_CSS" file="resources\chromeos\login\lock_screen.css" type="BINDATA" /> + <include name="IDR_LOCK_SCREEN_HTML" file="resources\chromeos\login\lock_screen.html" type="BINDATA" /> + <include name="IDR_LOCK_SCREEN_JS" file="resources\chromeos\login\lock_screen.js" type="BINDATA" /> <include name="IDR_LOGIN_HTML" file="resources\chromeos\login\login.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_MENU_HTML" file="resources\menu.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_MOBILE_MANIFEST" file="resources\mobile_app\manifest.json" type="BINDATA" /> diff --git a/chrome/browser/chromeos/cros_stubs_aura.cc b/chrome/browser/chromeos/cros_stubs_aura.cc index 265198d..01a5293 100644 --- a/chrome/browser/chromeos/cros_stubs_aura.cc +++ b/chrome/browser/chromeos/cros_stubs_aura.cc @@ -125,6 +125,10 @@ ScreenLocker::ScreenLocker(const UserManager::User& user) { NOTIMPLEMENTED(); } +ScreenLocker::~ScreenLocker() { + NOTIMPLEMENTED(); +} + void ScreenLocker::Init() { NOTIMPLEMENTED(); } @@ -142,25 +146,6 @@ void ScreenLocker::OnLoginSuccess( NOTIMPLEMENTED(); } -void ScreenLocker::BubbleClosing(Bubble* bubble, bool closed_by_escape) { - NOTIMPLEMENTED(); -} - -bool ScreenLocker::CloseOnEscape() { - return true; -} - -bool ScreenLocker::FadeInOnShow() { - return false; -} - -void ScreenLocker::OnLinkActivated(size_t index) { -} - -void ScreenLocker::OnCaptchaEntered(const std::string& captcha) { - NOTIMPLEMENTED(); -} - void ScreenLocker::Authenticate(const string16& password) { NOTIMPLEMENTED(); } @@ -178,23 +163,15 @@ void ScreenLocker::Signout() { } void ScreenLocker::ShowCaptchaAndErrorMessage(const GURL& captcha_url, - const std::wstring& message) { + const string16& message) { NOTIMPLEMENTED(); } -void ScreenLocker::ShowErrorMessage(const std::wstring& message, +void ScreenLocker::ShowErrorMessage(const string16& message, bool sign_out_only) { NOTIMPLEMENTED(); } -void ScreenLocker::OnGrabInputs() { - NOTIMPLEMENTED(); -} - -views::View* ScreenLocker::GetViewByID(int id) { - return NULL; -} - void ScreenLocker::SetLoginStatusConsumer( chromeos::LoginStatusConsumer* consumer) { NOTIMPLEMENTED(); @@ -220,6 +197,10 @@ void ScreenLocker::InitClass() { NOTIMPLEMENTED(); } +void ScreenLocker::ScreenLockReady() { + NOTIMPLEMENTED(); +} + } // namespace chromeos class BalloonCollectionStub : public BalloonCollection { diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index 9b2de10..a58c4cf 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -513,8 +513,7 @@ void LoginPerformer::ResolveLockNetworkAuthFailure() { captcha_token_ = last_login_failure_.error().captcha().token; msg = l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_PASSWORD_CHANGED); ScreenLocker::default_screen_locker()->ShowCaptchaAndErrorMessage( - last_login_failure_.error().captcha().image_url, - UTF16ToWide(msg)); + last_login_failure_.error().captcha().image_url, msg); return; default: // Unless there's new GoogleServiceAuthError state has been added. @@ -522,8 +521,7 @@ void LoginPerformer::ResolveLockNetworkAuthFailure() { break; } - ScreenLocker::default_screen_locker()->ShowErrorMessage(UTF16ToWide(msg), - sign_out_only); + ScreenLocker::default_screen_locker()->ShowErrorMessage(msg, sign_out_only); } void LoginPerformer::ResolveScreenLocked() { diff --git a/chrome/browser/chromeos/login/screen_lock_view.cc b/chrome/browser/chromeos/login/screen_lock_view.cc index 900a9ac..6a572eb 100644 --- a/chrome/browser/chromeos/login/screen_lock_view.cc +++ b/chrome/browser/chromeos/login/screen_lock_view.cc @@ -17,7 +17,6 @@ #include "chrome/common/chrome_notification_types.h" #include "content/common/notification_service.h" #include "grit/generated_resources.h" -#include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "views/background.h" @@ -27,6 +26,7 @@ #include "views/controls/textfield/native_textfield_wrapper.h" #include "views/controls/textfield/textfield.h" #include "views/layout/grid_layout.h" +#include "views/widget/native_widget_gtk.h" namespace chromeos { diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc index 489935c..26a0762 100644 --- a/chrome/browser/chromeos/login/screen_locker.cc +++ b/chrome/browser/chromeos/login/screen_locker.cc @@ -4,14 +4,8 @@ #include "chrome/browser/chromeos/login/screen_locker.h" -#include <X11/extensions/XTest.h> -#include <X11/keysym.h> -#include <gdk/gdkkeysyms.h> -#include <gdk/gdkx.h> #include <string> #include <vector> -// Evil hack to undo X11 evil #define. See crosbug.com/ -#undef Status #include "base/bind.h" #include "base/command_line.h" @@ -30,14 +24,10 @@ #include "chrome/browser/chromeos/input_method/xkeyboard.h" #include "chrome/browser/chromeos/language_preferences.h" #include "chrome/browser/chromeos/login/authenticator.h" -#include "chrome/browser/chromeos/login/background_view.h" #include "chrome/browser/chromeos/login/login_performer.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/chromeos/login/shutdown_button.h" -#include "chrome/browser/chromeos/system_key_event_listener.h" -#include "chrome/browser/chromeos/view_ids.h" +#include "chrome/browser/chromeos/login/screen_locker_views.h" +#include "chrome/browser/chromeos/login/screen_locker_webui.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" @@ -46,19 +36,13 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" #include "content/browser/browser_thread.h" #include "content/browser/user_metrics.h" #include "content/common/notification_service.h" #include "googleurl/src/gurl.h" #include "grit/generated_resources.h" -#include "grit/theme_resources.h" #include "third_party/cros_system_api/window_manager/chromeos_wm_ipc_enums.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/base/x/x11_util.h" -#include "ui/gfx/screen.h" -#include "views/widget/native_widget_gtk.h" #if defined(TOOLKIT_USES_GTK) #include "chrome/browser/chromeos/wm_ipc.h" @@ -66,18 +50,6 @@ namespace { -// The maximum duration for which locker should try to grab the keyboard and -// mouse and its interval for regrabbing on failure. -const int kMaxGrabFailureSec = 30; -const int64 kRetryGrabIntervalMs = 500; - -// Maximum number of times we'll try to grab the keyboard and mouse before -// giving up. If we hit the limit, Chrome exits and the session is terminated. -const int kMaxGrabFailures = kMaxGrabFailureSec * 1000 / kRetryGrabIntervalMs; - -// A idle time to show the screen saver in seconds. -const int kScreenSaverIdleTimeout = 15; - // Observer to start ScreenLocker when the screen lock class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, public NotificationObserver { @@ -202,377 +174,6 @@ class ScreenLockObserver : public chromeos::ScreenLockLibrary::Observer, static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer( base::LINKER_INITIALIZED); -// A ScreenLock window that covers entire screen to keep the keyboard -// focus/events inside the grab widget. -class LockWindow : public views::NativeWidgetGtk { - public: - LockWindow() - : views::NativeWidgetGtk(new views::Widget), - toplevel_focus_widget_(NULL) { - EnableDoubleBuffer(true); - } - - // GTK propagates key events from parents to children. - // Make sure LockWindow will never handle key events. - virtual gboolean OnEventKey(GtkWidget* widget, GdkEventKey* event) OVERRIDE { - // Don't handle key event in the lock window. - return false; - } - - virtual gboolean OnButtonPress(GtkWidget* widget, - GdkEventButton* event) OVERRIDE { - // Don't handle mouse event in the lock wnidow and - // nor propagate to child. - return true; - } - - virtual void OnDestroy(GtkWidget* object) OVERRIDE { - VLOG(1) << "OnDestroy: LockWindow destroyed"; - views::NativeWidgetGtk::OnDestroy(object); - } - - virtual void ClearNativeFocus() OVERRIDE { - DCHECK(toplevel_focus_widget_); - gtk_widget_grab_focus(toplevel_focus_widget_); - } - - // Sets the widget to move the focus to when clearning the native - // widget's focus. - void set_toplevel_focus_widget(GtkWidget* widget) { - gtk_widget_set_can_focus(widget, TRUE); - toplevel_focus_widget_ = widget; - } - - private: - // The widget we set focus to when clearning the focus on native - // widget. In screen locker, gdk input is grabbed in GrabWidget, - // and resetting the focus by using gtk_window_set_focus seems to - // confuse gtk and doesn't let focus move to native widget under - // GrabWidget. - GtkWidget* toplevel_focus_widget_; - - DISALLOW_COPY_AND_ASSIGN(LockWindow); -}; - -// GrabWidget's root view to layout the ScreenLockView at the center -// and the Shutdown button at the left top. -class GrabWidgetRootView - : public views::View, - public chromeos::ScreenLocker::ScreenLockViewContainer { - public: - explicit GrabWidgetRootView(chromeos::ScreenLockView* screen_lock_view) - : screen_lock_view_(screen_lock_view), - shutdown_button_(new chromeos::ShutdownButton()) { - shutdown_button_->Init(); - AddChildView(screen_lock_view_); - AddChildView(shutdown_button_); - } - - // views::View implementation. - virtual void Layout() OVERRIDE { - gfx::Size size = screen_lock_view_->GetPreferredSize(); - gfx::Rect b = bounds(); - screen_lock_view_->SetBounds( - b.width() - size.width(), b.height() - size.height(), - size.width(), size.height()); - shutdown_button_->LayoutIn(this); - } - - // ScreenLocker::ScreenLockViewContainer implementation: - void SetScreenLockView(views::View* screen_lock_view) OVERRIDE { - if (screen_lock_view_) { - RemoveChildView(screen_lock_view_); - } - screen_lock_view_ = screen_lock_view; - if (screen_lock_view_) { - AddChildViewAt(screen_lock_view_, 0); - } - Layout(); - } - - private: - views::View* screen_lock_view_; - - chromeos::ShutdownButton* shutdown_button_; - - DISALLOW_COPY_AND_ASSIGN(GrabWidgetRootView); -}; - -// A child widget that grabs both keyboard and pointer input. -class GrabWidget : public views::NativeWidgetGtk { - public: - explicit GrabWidget(chromeos::ScreenLocker* screen_locker) - : views::NativeWidgetGtk(new views::Widget), - screen_locker_(screen_locker), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), - grab_failure_count_(0), - kbd_grab_status_(GDK_GRAB_INVALID_TIME), - mouse_grab_status_(GDK_GRAB_INVALID_TIME), - signout_link_(NULL), - shutdown_(NULL) { - } - - virtual void Show() OVERRIDE { - views::NativeWidgetGtk::Show(); - signout_link_ = - screen_locker_->GetViewByID(VIEW_ID_SCREEN_LOCKER_SIGNOUT_LINK); - shutdown_ = screen_locker_->GetViewByID(VIEW_ID_SCREEN_LOCKER_SHUTDOWN); - // These can be null in guest mode. - } - - void ClearGtkGrab() { - GtkWidget* current_grab_window; - // Grab gtk input first so that the menu holding gtk grab will - // close itself. - gtk_grab_add(window_contents()); - - // Make sure there is no gtk grab widget so that gtk simply propagates - // an event. This is necessary to allow message bubble and password - // field, button to process events simultaneously. GTK - // maintains grab widgets in a linked-list, so we need to remove - // until it's empty. - while ((current_grab_window = gtk_grab_get_current()) != NULL) - gtk_grab_remove(current_grab_window); - } - - virtual gboolean OnEventKey(GtkWidget* widget, GdkEventKey* event) OVERRIDE { - views::KeyEvent key_event(reinterpret_cast<GdkEvent*>(event)); - // This is a hack to workaround the issue crosbug.com/10655 due to - // the limitation that a focus manager cannot handle views in - // TYPE_CHILD NativeWidgetGtk correctly. - if (signout_link_ && - event->type == GDK_KEY_PRESS && - (event->keyval == GDK_Tab || - event->keyval == GDK_ISO_Left_Tab || - event->keyval == GDK_KP_Tab)) { - DCHECK(shutdown_); - bool reverse = event->state & GDK_SHIFT_MASK; - if (reverse && signout_link_->HasFocus()) { - shutdown_->RequestFocus(); - return true; - } - if (!reverse && shutdown_->HasFocus()) { - signout_link_->RequestFocus(); - return true; - } - } - return views::NativeWidgetGtk::OnEventKey(widget, event); - } - - virtual gboolean OnButtonPress(GtkWidget* widget, - GdkEventButton* event) OVERRIDE { - NativeWidgetGtk::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(); - - // This method tries to steal pointer/keyboard grab from other - // client by sending events that will hopefully close menus or windows - // that have the grab. - void TryUngrabOtherClients(); - - private: - virtual void HandleGtkGrabBroke() OVERRIDE { - // 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_NE(GDK_GRAB_SUCCESS, kbd_grab_status_); - CHECK_NE(GDK_GRAB_SUCCESS, mouse_grab_status_); - } - - // Define separate methods for each error code so that stack trace - // will tell which error the grab failed with. - void FailedWithGrabAlreadyGrabbed() { - LOG(FATAL) << "Grab already grabbed"; - } - void FailedWithGrabInvalidTime() { - LOG(FATAL) << "Grab invalid time"; - } - void FailedWithGrabNotViewable() { - LOG(FATAL) << "Grab not viewable"; - } - void FailedWithGrabFrozen() { - LOG(FATAL) << "Grab frozen"; - } - void FailedWithUnknownError() { - LOG(FATAL) << "Grab uknown"; - } - - chromeos::ScreenLocker* screen_locker_; - base::WeakPtrFactory<GrabWidget> weak_factory_; - - // The number times the widget tried to grab all focus. - int grab_failure_count_; - // Status of keyboard and mouse grab. - GdkGrabStatus kbd_grab_status_; - GdkGrabStatus mouse_grab_status_; - - views::View* signout_link_; - views::View* shutdown_; - - DISALLOW_COPY_AND_ASSIGN(GrabWidget); -}; - -void GrabWidget::TryGrabAllInputs() { - // Grab x server so that we can atomically grab and take - // action when grab fails. - gdk_x11_grab_server(); - if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { - kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, - GDK_CURRENT_TIME); - } - if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { - mouse_grab_status_ = - gdk_pointer_grab(window_contents()->window, - FALSE, - static_cast<GdkEventMask>( - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK), - NULL, - NULL, - GDK_CURRENT_TIME); - } - if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || - mouse_grab_status_ != GDK_GRAB_SUCCESS) && - grab_failure_count_++ < kMaxGrabFailures) { - LOG(WARNING) << "Failed to grab inputs. Trying again in " - << kRetryGrabIntervalMs << " ms: kbd=" - << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; - TryUngrabOtherClients(); - gdk_x11_ungrab_server(); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&GrabWidget::TryGrabAllInputs, weak_factory_.GetWeakPtr()), - kRetryGrabIntervalMs); - } else { - gdk_x11_ungrab_server(); - GdkGrabStatus status = kbd_grab_status_; - if (status == GDK_GRAB_SUCCESS) { - status = mouse_grab_status_; - } - switch (status) { - case GDK_GRAB_SUCCESS: - break; - case GDK_GRAB_ALREADY_GRABBED: - FailedWithGrabAlreadyGrabbed(); - break; - case GDK_GRAB_INVALID_TIME: - FailedWithGrabInvalidTime(); - break; - case GDK_GRAB_NOT_VIEWABLE: - FailedWithGrabNotViewable(); - break; - case GDK_GRAB_FROZEN: - FailedWithGrabFrozen(); - break; - default: - FailedWithUnknownError(); - break; - } - DVLOG(1) << "Grab Success"; - screen_locker_->OnGrabInputs(); - } -} - -void GrabWidget::TryUngrabOtherClients() { -#if !defined(NDEBUG) - { - int event_base, error_base; - int major, minor; - // Make sure we have XTest extension. - DCHECK(XTestQueryExtension(ui::GetXDisplay(), - &event_base, &error_base, - &major, &minor)); - } -#endif - - // The following code is an attempt to grab inputs by closing - // supposedly opened menu. This happens when a plugin has a menu - // opened. - if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED || - mouse_grab_status_ == GDK_GRAB_FROZEN) { - // Successfully grabbed the keyboard, but pointer is still - // grabbed by other client. Another attempt to close supposedly - // opened menu by emulating keypress at the left top corner. - Display* display = ui::GetXDisplay(); - Window root, child; - int root_x, root_y, win_x, winy; - unsigned int mask; - XQueryPointer(display, - ui::GetX11WindowFromGtkWidget(window_contents()), - &root, &child, &root_x, &root_y, - &win_x, &winy, &mask); - XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime); - XTestFakeButtonEvent(display, 1, True, CurrentTime); - XTestFakeButtonEvent(display, 1, False, CurrentTime); - // Move the pointer back. - XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime); - XFlush(display); - } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED || - kbd_grab_status_ == GDK_GRAB_FROZEN) { - // Successfully grabbed the pointer, but keyboard is still grabbed - // by other client. Another attempt to close supposedly opened - // menu by emulating escape key. Such situation must be very - // rare, but handling this just in case - Display* display = ui::GetXDisplay(); - KeyCode escape = XKeysymToKeycode(display, XK_Escape); - XTestFakeKeyEvent(display, escape, True, CurrentTime); - XTestFakeKeyEvent(display, escape, False, CurrentTime); - XFlush(display); - } -} - -// BackgroundView for ScreenLocker, which layouts a lock widget in -// addition to other background components. -class ScreenLockerBackgroundView - : public chromeos::BackgroundView, - public chromeos::ScreenLocker::ScreenLockViewContainer { - public: - ScreenLockerBackgroundView(views::Widget* lock_widget, - views::View* screen_lock_view) - : lock_widget_(lock_widget), - screen_lock_view_(screen_lock_view) { - } - - virtual ScreenMode GetScreenMode() const OVERRIDE { - return kScreenLockerMode; - } - - virtual void Layout() OVERRIDE { - chromeos::BackgroundView::Layout(); - gfx::Rect screen = bounds(); - if (screen_lock_view_) { - gfx::Size size = screen_lock_view_->GetPreferredSize(); - gfx::Point origin((screen.width() - size.width()) / 2, - (screen.height() - size.height()) / 2); - gfx::Size widget_size(screen.size()); - widget_size.Enlarge(-origin.x(), -origin.y()); - lock_widget_->SetBounds(gfx::Rect(widget_size)); - } else { - // No password entry. Move the lock widget to off screen. - lock_widget_->SetBounds(gfx::Rect(-100, -100, 1, 1)); - } - } - - // ScreenLocker::ScreenLockViewContainer implementation: - virtual void SetScreenLockView(views::View* screen_lock_view) OVERRIDE { - screen_lock_view_ = screen_lock_view; - Layout(); - } - - private: - views::Widget* lock_widget_; - - views::View* screen_lock_view_; - - DISALLOW_COPY_AND_ASSIGN(ScreenLockerBackgroundView); -}; - } // namespace namespace chromeos { @@ -580,174 +181,11 @@ 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), - initialized_(false) { - DCHECK(src_); - DCHECK(dest_); - } - - virtual void WillProcessEvent(GdkEvent* event) OVERRIDE {} - - virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { - if (event->any.window != src_) - return; - if (!initialized_) { - gint src_x, src_y, dest_x, dest_y, width, height, depth; - gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth); - // wait to compute offset until the info bubble widget's location - // is available. - if (dest_x < 0 || dest_y < 0) - return; - gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth); - offset_.SetPoint(dest_x - src_x, dest_y - src_y); - initialized_ = true; - } - 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->motion.window = dest_; - g_object_ref(copy->motion.window); - copy->motion.x -= offset_.x(); - copy->motion.y -= offset_.y(); - - gdk_event_put(copy); - gdk_event_free(copy); - } - } - - private: - GdkWindow* src_; - GdkWindow* dest_; - bool initialized_; - - // Offset from src_'s origin to dest_'s origin. - gfx::Point offset_; - - DISALLOW_COPY_AND_ASSIGN(MouseEventRelay); -}; - -// A event observer used to unlock the screen upon user's action -// without asking password. Used in BWSI and auto login mode. -// TODO(oshima): consolidate InputEventObserver and LockerInputEventObserver. -class InputEventObserver : public MessageLoopForUI::Observer { - public: - explicit InputEventObserver(ScreenLocker* screen_locker) - : screen_locker_(screen_locker), - activated_(false) { - } - -#if defined(TOUCH_UI) - virtual base::EventStatus WillProcessEvent( - const base::NativeEvent& event) OVERRIDE { - return base::EVENT_CONTINUE; - } - - virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { - } -#else - virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { - if ((event->type == GDK_KEY_PRESS || - event->type == GDK_BUTTON_PRESS || - event->type == GDK_MOTION_NOTIFY) && - !activated_) { - activated_ = true; - std::string not_used_string; - GaiaAuthConsumer::ClientLoginResult not_used; - screen_locker_->OnLoginSuccess(not_used_string, - not_used_string, - not_used, - false, - false); - } - } - - virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { - } -#endif - - private: - chromeos::ScreenLocker* screen_locker_; - - bool activated_; - - DISALLOW_COPY_AND_ASSIGN(InputEventObserver); -}; - -// A event observer used to show the screen locker upon -// user action: mouse or keyboard interactions. -// TODO(oshima): this has to be disabled while authenticating. -class LockerInputEventObserver : public MessageLoopForUI::Observer { - public: - explicit LockerInputEventObserver(ScreenLocker* screen_locker) - : screen_locker_(screen_locker), - ALLOW_THIS_IN_INITIALIZER_LIST( - timer_(FROM_HERE, - base::TimeDelta::FromSeconds(kScreenSaverIdleTimeout), this, - &LockerInputEventObserver::StartScreenSaver)) { - } - -#if defined(TOUCH_UI) - virtual base::EventStatus WillProcessEvent( - const base::NativeEvent& event) OVERRIDE { - return base::EVENT_CONTINUE; - } - - virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { - } -#else - virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { - if ((event->type == GDK_KEY_PRESS || - event->type == GDK_BUTTON_PRESS || - event->type == GDK_MOTION_NOTIFY)) { - timer_.Reset(); - screen_locker_->StopScreenSaver(); - } - } - - virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { - } -#endif - - private: - void StartScreenSaver() { - screen_locker_->StartScreenSaver(); - } - - chromeos::ScreenLocker* screen_locker_; - base::DelayTimer<LockerInputEventObserver> timer_; - - DISALLOW_COPY_AND_ASSIGN(LockerInputEventObserver); -}; - ////////////////////////////////////////////////////////////////////////////// // ScreenLocker, public: 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), - input_grabbed_(false), + : user_(user), // TODO(oshima): support auto login mode (this is not implemented yet) // http://crosbug.com/1881 unlock_on_input_(user_.email().empty()), @@ -759,107 +197,12 @@ ScreenLocker::ScreenLocker(const UserManager::User& user) } void ScreenLocker::Init() { - static const GdkColor kGdkBlack = {0, 0, 0, 0}; - authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); - - gfx::Point left_top(1, 1); - gfx::Rect init_bounds(gfx::Screen::GetMonitorAreaNearestPoint(left_top)); - - LockWindow* lock_window = new LockWindow(); - lock_window_ = lock_window->GetWidget(); - views::Widget::InitParams params( - views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.bounds = init_bounds; - params.native_widget = lock_window; - lock_window_->Init(params); - gtk_widget_modify_bg( - lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack); - - g_signal_connect(lock_window_->GetNativeView(), "client-event", - G_CALLBACK(OnClientEventThunk), this); - - // GTK does not like zero width/height. - if (!unlock_on_input_) { - screen_lock_view_ = new ScreenLockView(this); - screen_lock_view_->Init(); - screen_lock_view_->SetEnabled(false); - screen_lock_view_->StartThrobber(); - } else { - input_event_observer_.reset(new InputEventObserver(this)); - MessageLoopForUI::current()->AddObserver(input_event_observer_.get()); - } - - // Hang on to a cast version of the grab widget so we can call its - // TryGrabAllInputs() method later. (Nobody else needs to use it, so moving - // its declaration to the header instead of keeping it in an anonymous - // namespace feels a bit ugly.) - GrabWidget* grab_widget = new GrabWidget(this); - lock_widget_ = grab_widget->GetWidget(); - views::Widget::InitParams lock_params( - views::Widget::InitParams::TYPE_CONTROL); - lock_params.transparent = true; - lock_params.parent_widget = lock_window_; - lock_params.native_widget = grab_widget; - lock_widget_->Init(lock_params); - if (screen_lock_view_) { - GrabWidgetRootView* root_view = new GrabWidgetRootView(screen_lock_view_); - grab_container_ = root_view; - lock_widget_->SetContentsView(root_view); - } - lock_widget_->Show(); - - // Configuring the background url. - std::string url_string = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kScreenSaverUrl); - 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)); - - // Gets user profile and sets default user 24hour flag since we don't - // expose user profile in ScreenLockerBackgroundView. - Profile* profile = ProfileManager::GetDefaultProfile(); - if (profile) { - background_view_->SetDefaultUse24HourClock( - profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock)); - } - - if (background_view_->ScreenSaverEnabled()) - StartScreenSaver(); - -#if defined(TOOLKIT_USES_GTK) - DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); - WmIpc::instance()->SetWindowType( - lock_window_->GetNativeView(), - WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, - NULL); -#endif - - lock_window_->SetContentsView(background_view_); - lock_window_->Show(); - - grab_widget->ClearGtkGrab(); - - // Call this after lock_window_->Show(); otherwise the 1st invocation - // of gdk_xxx_grab() will always fail. - grab_widget->TryGrabAllInputs(); - - // Add the window to its own group so that its grab won't be stolen if - // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. - // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this - // after calling ClearGtkGrab(), as want to be in the default window group - // then so we can break any existing GTK grabs. - GtkWindowGroup* window_group = gtk_window_group_new(); - gtk_window_group_add_window(window_group, - GTK_WINDOW(lock_window_->GetNativeView())); - g_object_unref(window_group); - - lock_window->set_toplevel_focus_widget( - static_cast<views::NativeWidgetGtk*>(lock_widget_->native_widget())-> - window_contents()); + if (UseWebUILockScreen()) + delegate_.reset(new ScreenLockerWebUI(this)); + else + delegate_.reset(new ScreenLockerViews(this)); + delegate_->Init(unlock_on_input_); } void ScreenLocker::OnLoginFailure(const LoginFailure& error) { @@ -895,7 +238,7 @@ void ScreenLocker::OnLoginFailure(const LoginFailure& error) { msg += ASCIIToUTF16("\n") + l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_KEYBOARD_SWITCH_HINT); - ShowErrorBubble(UTF16ToWide(msg), views::BubbleBorder::BOTTOM_LEFT); + delegate_->ShowErrorMessage(msg, false); if (login_status_consumer_) login_status_consumer_->OnLoginFailure(error); @@ -934,54 +277,11 @@ void ScreenLocker::OnLoginSuccess( using_oauth); } -void ScreenLocker::BubbleClosing(Bubble* 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(); - } -} - -bool ScreenLocker::CloseOnEscape() { - return true; -} - -bool ScreenLocker::FadeInOnShow() { - return false; -} - -void ScreenLocker::OnLinkActivated(size_t index) { -} - -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); - screen_lock_view_->SetSignoutEnabled(false); - screen_lock_view_->StartThrobber(); + delegate_->SetInputEnabled(false); + delegate_->SetSignoutEnabled(false); + delegate_->OnAuthenticate(); // If LoginPerformer instance exists, // initial online login phase is still active. @@ -997,80 +297,38 @@ void ScreenLocker::Authenticate(const string16& password) { } } +void ScreenLocker::ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& message) { + delegate_->ShowCaptchaAndErrorMessage(captcha_url, message); +} + void ScreenLocker::ClearErrors() { - if (error_info_) { - error_info_->Close(); - error_info_ = NULL; - } + delegate_->ClearErrors(); } void ScreenLocker::EnableInput() { - if (screen_lock_view_) { - screen_lock_view_->SetEnabled(true); - screen_lock_view_->ClearAndSetFocusToPassword(); - screen_lock_view_->StopThrobber(); - } + delegate_->SetInputEnabled(true); } void ScreenLocker::Signout() { - if (!error_info_) { - UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Signout")); + // TODO(flackr): For proper functionality, check if (error_info) is NULL. + delegate_->ClearErrors(); + UserMetrics::RecordAction(UserMetricsAction("ScreenLocker_Signout")); #if defined(TOOLKIT_USES_GTK) - WmIpc::instance()->NotifyAboutSignout(); + WmIpc::instance()->NotifyAboutSignout(); #endif - if (CrosLibrary::Get()->EnsureLoaded()) { - CrosLibrary::Get()->GetLoginLibrary()->StopSession(""); - } - - // Don't hide yet the locker because the chrome screen may become visible - // briefly. - } -} + if (CrosLibrary::Get()->EnsureLoaded()) + CrosLibrary::Get()->GetLoginLibrary()->StopSession(""); -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_); + // Don't hide yet the locker because the chrome screen may become visible + // briefly. } -void ScreenLocker::ShowErrorMessage(const std::wstring& message, +void ScreenLocker::ShowErrorMessage(const string16& message, bool sign_out_only) { - if (sign_out_only) { - screen_lock_view_->SetEnabled(false); - } else { - EnableInput(); - } - screen_lock_view_->SetSignoutEnabled(sign_out_only); - // Make sure that active Sign Out button is not hidden behind the bubble. - ShowErrorBubble( - message, sign_out_only ? - views::BubbleBorder::BOTTOM_RIGHT : views::BubbleBorder::BOTTOM_LEFT); -} - -void ScreenLocker::OnGrabInputs() { - DVLOG(1) << "OnGrabInputs"; - input_grabbed_ = true; - if (drawn_) - ScreenLockReady(); -} - -views::View* ScreenLocker::GetViewByID(int id) { - return lock_widget_->GetRootView()->GetViewByID(id); + delegate_->SetInputEnabled(!sign_out_only); + delegate_->SetSignoutEnabled(sign_out_only); + delegate_->ShowErrorMessage(message, sign_out_only); } void ScreenLocker::SetLoginStatusConsumer( @@ -1097,10 +355,13 @@ void ScreenLocker::Show() { ScreenLocker* locker = new ScreenLocker(UserManager::Get()->logged_in_user()); #if defined(TOUCH_UI) - // The screen locker does not reliably work on TOUCH_UI builds. In order - // to effectively "lock" the screen we will sign out the user for now. + // The views screen locker does not reliably work on TOUCH_UI builds. In + // order to effectively "lock" the screen we will sign out the user for now. // TODO(flackr): Implement lock screen in WebUI and remove this hack. - locker->Signout(); + if (ScreenLocker::UseWebUILockScreen()) + locker->Init(); + else + locker->Signout(); #else locker->Init(); #endif @@ -1141,6 +402,12 @@ void ScreenLocker::UnlockScreenFailed() { } // static +bool ScreenLocker::UseWebUILockScreen() { + return CommandLine::ForCurrentProcess()->HasSwitch( + switches::kWebUILockScreen); +} + +// static void ScreenLocker::InitClass() { g_screen_lock_observer.Get(); } @@ -1151,22 +418,7 @@ void ScreenLocker::InitClass() { ScreenLocker::~ScreenLocker() { DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); ClearErrors(); - if (input_event_observer_.get()) - MessageLoopForUI::current()->RemoveObserver(input_event_observer_.get()); - if (locker_input_event_observer_.get()) { - lock_widget_->GetFocusManager()->UnregisterAccelerator( - views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); - MessageLoopForUI::current()->RemoveObserver( - locker_input_event_observer_.get()); - } - gdk_keyboard_ungrab(GDK_CURRENT_TIME); - gdk_pointer_ungrab(GDK_CURRENT_TIME); - - DCHECK(lock_window_); - VLOG(1) << "~ScreenLocker(): Closing ScreenLocker window."; - lock_window_->Close(); - // lock_widget_ will be deleted by gtk's destroy signal. screen_locker_ = NULL; bool state = false; NotificationService::current()->Notify( @@ -1188,17 +440,6 @@ void ScreenLocker::ScreenLockReady() { VLOG(1) << "Screen lock time: " << delta.InSecondsF(); UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); - if (background_view_->ScreenSaverEnabled()) { - lock_widget_->GetFocusManager()->RegisterAccelerator( - views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); - locker_input_event_observer_.reset(new LockerInputEventObserver(this)); - MessageLoopForUI::current()->AddObserver( - locker_input_event_observer_.get()); - } else { - // Don't enable the password field until we grab all inputs. - EnableInput(); - } - bool state = true; NotificationService::current()->Notify( chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, @@ -1208,87 +449,4 @@ void ScreenLocker::ScreenLockReady() { CrosLibrary::Get()->GetScreenLockLibrary()->NotifyScreenLockCompleted(); } -void ScreenLocker::OnClientEvent(GtkWidget* widge, GdkEventClient* event) { -#if defined(TOOLKIT_USES_GTK) - WmIpc::Message msg; - WmIpc::instance()->DecodeMessage(*event, &msg); - if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK) { - OnWindowManagerReady(); - } -#endif -} - -void ScreenLocker::OnWindowManagerReady() { - DVLOG(1) << "OnClientEvent: drawn for lock"; - drawn_ = true; - if (input_grabbed_) - ScreenLockReady(); -} - -void ScreenLocker::ShowErrorBubble( - const std::wstring& message, - views::BubbleBorder::ArrowLocation arrow_location) { - if (error_info_) - error_info_->Close(); - - gfx::Rect rect = screen_lock_view_->GetPasswordBoundsRelativeTo( - lock_widget_->GetRootView()); - gfx::Rect lock_widget_bounds = lock_widget_->GetClientAreaScreenBounds(); - rect.Offset(lock_widget_bounds.x(), lock_widget_bounds.y()); - error_info_ = MessageBubble::ShowNoGrab( - lock_window_, - rect, - arrow_location, - ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), - message, - std::wstring(), // TODO(nkostylev): Add help link. - this); - -#if !defined(TOUCH_UI) - 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()); -#endif -} - -void ScreenLocker::StopScreenSaver() { - if (background_view_->IsScreenSaverVisible()) { - VLOG(1) << "StopScreenSaver"; - if (screen_lock_view_) { - screen_lock_view_->SetVisible(true); - // Place the screen_lock_view_ to the center of the screen. - // Must be called when the view is visible: crosbug.com/15213. - background_view_->Layout(); - screen_lock_view_->RequestFocus(); - } - background_view_->HideScreenSaver(); - EnableInput(); - } -} - -void ScreenLocker::StartScreenSaver() { - if (!background_view_->IsScreenSaverVisible()) { - VLOG(1) << "StartScreenSaver"; - UserMetrics::RecordAction( - UserMetricsAction("ScreenLocker_StartScreenSaver")); - background_view_->ShowScreenSaver(); - if (screen_lock_view_) { - screen_lock_view_->SetEnabled(false); - screen_lock_view_->SetVisible(false); - } - ClearErrors(); - } -} - -bool ScreenLocker::AcceleratorPressed(const views::Accelerator& accelerator) { - if (!background_view_->IsScreenSaverVisible()) { - StartScreenSaver(); - return true; - } - return false; -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker.h b/chrome/browser/chromeos/login/screen_locker.h index d226c8d..6991c64 100644 --- a/chrome/browser/chromeos/login/screen_locker.h +++ b/chrome/browser/chromeos/login/screen_locker.h @@ -11,9 +11,8 @@ #include "base/memory/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/screen_locker_delegate.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "views/accelerator.h" @@ -28,35 +27,17 @@ class View; namespace chromeos { class Authenticator; -class BackgroundView; -class InputEventObserver; -class LockerInputEventObserver; -class MessageBubble; -class MouseEventRelay; -class ScreenLockView; class LoginFailure; namespace test { class ScreenLockerTester; } // namespace test -// 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, - public MessageBubbleDelegate, - public CaptchaView::Delegate, - public views::AcceleratorTarget { +// ScreenLocker creates a ScreenLockerDelegate which will display the lock UI. +// As well, it takes care of authenticating the user and managing a global +// instance of itself which will be deleted when the system is unlocked. +class ScreenLocker : public LoginStatusConsumer { 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. @@ -80,15 +61,6 @@ class ScreenLocker : public LoginStatusConsumer, bool pending_requests, bool using_oauth) OVERRIDE; - // Overridden from views::BubbleDelegate. - virtual void BubbleClosing(Bubble* bubble, bool closed_by_escape) OVERRIDE; - virtual bool CloseOnEscape() OVERRIDE; - virtual bool FadeInOnShow() OVERRIDE; - virtual void OnLinkActivated(size_t index) OVERRIDE; - - // CaptchaView::Delegate implementation: - virtual void OnCaptchaEntered(const std::string& captcha) OVERRIDE; - // Authenticates the user with given |password| and authenticator. void Authenticate(const string16& password); @@ -104,15 +76,12 @@ class ScreenLocker : public LoginStatusConsumer, // 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); + const string16& 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. - void ShowErrorMessage(const std::wstring& message, bool sign_out_only); - - // Called when the all inputs are grabbed. - void OnGrabInputs(); + void ShowErrorMessage(const string16& message, bool sign_out_only); #if defined(TOOLKIT_USES_GTK) // Returns the user to authenticate. @@ -121,9 +90,6 @@ class ScreenLocker : public LoginStatusConsumer, } #endif - // Returns a view that has given view |id|, or null if it doesn't exist. - views::View* GetViewByID(int id); - // Allow a LoginStatusConsumer to listen for // the same login events that ScreenLocker does. void SetLoginStatusConsumer(chromeos::LoginStatusConsumer* consumer); @@ -139,17 +105,21 @@ class ScreenLocker : public LoginStatusConsumer, // Hide the screen locker. static void Hide(); + // Queries the value of the webui lock screen flag. + static bool UseWebUILockScreen(); + // Notifies that PowerManager rejected UnlockScreen request. static void UnlockScreenFailed(); #if defined(TOOLKIT_USES_GTK) // Returns the tester static test::ScreenLockerTester* GetTester(); +#endif private: friend class DeleteTask<ScreenLocker>; friend class test::ScreenLockerTester; - friend class LockerInputEventObserver; + friend class ScreenLockerDelegate; virtual ~ScreenLocker(); @@ -159,83 +129,16 @@ class ScreenLocker : public LoginStatusConsumer, // Called when the screen lock is ready. void ScreenLockReady(); - // Called when the window manager is ready to handle locked state. - void OnWindowManagerReady(); - - // Shows error_info_ bubble with the |message| and |arrow_location| specified. - // Assumes that UI controls were locked before that. - void ShowErrorBubble(const std::wstring& message, - views::BubbleBorder::ArrowLocation arrow_location); - - // Stops screen saver. - void StopScreenSaver(); - - // Starts screen saver. - void StartScreenSaver(); - - // Overridden from AcceleratorTarget: - virtual bool AcceleratorPressed(const views::Accelerator& accelerator); - - // Event handler for client-event. - CHROMEGTK_CALLBACK_1(ScreenLocker, void, OnClientEvent, GdkEventClient*); - - // The screen locker window. - views::Widget* lock_window_; - - // Child widget to grab the keyboard/mouse input. - views::Widget* lock_widget_; - - // A view that accepts password. - ScreenLockView* screen_lock_view_; - - // 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_; + // ScreenLockerDelegate instance in use. + scoped_ptr<ScreenLockerDelegate> delegate_; // Logged in user. UserManager::User user_; +#if defined(TOOLKIT_USES_GTK) // 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_; - - // A message loop observer to detect user's keyboard/mouse event. - // Used when |unlock_on_input_| is true. - scoped_ptr<InputEventObserver> input_event_observer_; - - // A message loop observer to detect user's keyboard/mouse event. - // Used when to show the screen locker upon such an event. - scoped_ptr<LockerInputEventObserver> locker_input_event_observer_; - - // An info bubble to display login failure message. - MessageBubble* error_info_; - - // True if the screen locker's window has been drawn. - bool drawn_; - - // True if both mouse input and keyboard input are grabbed. - bool input_grabbed_; +#endif // Unlock the screen when it detects key/mouse event without asking // password. True when chrome is in BWSI or auto login mode. @@ -259,8 +162,6 @@ class ScreenLocker : public LoginStatusConsumer, // Tests can use this to receive login status events. LoginStatusConsumer* login_status_consumer_; -#endif // TOOLKIT_USES_GTK - DISALLOW_COPY_AND_ASSIGN(ScreenLocker); }; diff --git a/chrome/browser/chromeos/login/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/screen_locker_browsertest.cc index b03517e..3d8c25f 100644 --- a/chrome/browser/chromeos/login/screen_locker_browsertest.cc +++ b/chrome/browser/chromeos/login/screen_locker_browsertest.cc @@ -23,6 +23,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "views/controls/textfield/textfield.h" +#include "views/widget/native_widget_gtk.h" namespace { diff --git a/chrome/browser/chromeos/login/screen_locker_delegate.cc b/chrome/browser/chromeos/login/screen_locker_delegate.cc new file mode 100644 index 0000000..c6b9813 --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_delegate.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/chromeos/login/screen_locker_delegate.h" + +namespace chromeos { + +ScreenLockerDelegate::ScreenLockerDelegate(ScreenLocker* screen_locker) + : screen_locker_(screen_locker) { +} + +ScreenLockerDelegate::~ScreenLockerDelegate() { +} + +void ScreenLockerDelegate::ScreenLockReady() { + screen_locker_->ScreenLockReady(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker_delegate.h b/chrome/browser/chromeos/login/screen_locker_delegate.h new file mode 100644 index 0000000..bc0a602 --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_delegate.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_DELEGATE_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_DELEGATE_H_ +#pragma once + +#include "base/string16.h" + +namespace chromeos { + +class ScreenLocker; + +// ScreenLockerDelegate takes care of displaying the lock screen UI. +class ScreenLockerDelegate { + public: + explicit ScreenLockerDelegate(ScreenLocker* screen_locker); + virtual ~ScreenLockerDelegate(); + + // Initialize the screen locker delegate. This will call ScreenLockReady when + // done to notify ScreenLocker. + virtual void Init(bool unlock_on_input) = 0; + + // Inform the screen locker that the screen has been locked + virtual void ScreenLockReady(); + + // This function is called when ScreenLocker::Authenticate is called to + // attempt to authenticate with a given password. + virtual void OnAuthenticate() = 0; + + // Enable/disable password input. + virtual void SetInputEnabled(bool enabled) = 0; + + // Enable/disable signout. + virtual void SetSignoutEnabled(bool enabled) = 0; + + // 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. + virtual void ShowErrorMessage(const string16& message, + bool sign_out_only) = 0; + + // Present user a CAPTCHA challenge with image from |captcha_url|, + // After that shows error bubble with |message|. + virtual void ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& message) = 0; + + // Close message bubble to clear error messages. + virtual void ClearErrors() = 0; + + protected: + // ScreenLocker that owns this delegate. + ScreenLocker* screen_locker_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockerDelegate); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_DELEGATE_H_ diff --git a/chrome/browser/chromeos/login/screen_locker_tester.cc b/chrome/browser/chromeos/login/screen_locker_tester.cc index 81b41b5..2043f84 100644 --- a/chrome/browser/chromeos/login/screen_locker_tester.cc +++ b/chrome/browser/chromeos/login/screen_locker_tester.cc @@ -11,6 +11,7 @@ #include "chrome/browser/chromeos/login/mock_authenticator.h" #include "chrome/browser/chromeos/login/screen_lock_view.h" #include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/chromeos/login/screen_locker_views.h" #include "views/controls/button/button.h" #include "views/controls/label.h" #include "views/controls/textfield/textfield.h" @@ -55,29 +56,36 @@ void ScreenLockerTester::EnterPassword(const std::string& password) { GdkEvent* event = gdk_event_new(GDK_KEY_PRESS); event->key.keyval = GDK_Return; views::KeyEvent key_event(event); - ScreenLocker::screen_locker_->screen_lock_view_->HandleKeyEvent( - pass, key_event); + screen_locker_views()->screen_lock_view_->HandleKeyEvent(pass, key_event); gdk_event_free(event); } void ScreenLockerTester::EmulateWindowManagerReady() { DCHECK(ScreenLocker::screen_locker_); - ScreenLocker::screen_locker_->OnWindowManagerReady(); + screen_locker_views()->OnWindowManagerReady(); } views::Textfield* ScreenLockerTester::GetPasswordField() const { DCHECK(ScreenLocker::screen_locker_); - return ScreenLocker::screen_locker_->screen_lock_view_->password_field_; + return screen_locker_views()->screen_lock_view_->password_field_; } views::Widget* ScreenLockerTester::GetWidget() const { DCHECK(ScreenLocker::screen_locker_); - return ScreenLocker::screen_locker_->lock_window_; + return screen_locker_views()->lock_window_; } views::Widget* ScreenLockerTester::GetChildWidget() const { DCHECK(ScreenLocker::screen_locker_); - return ScreenLocker::screen_locker_->lock_widget_; + return screen_locker_views()->lock_widget_; +} + +ScreenLockerViews* ScreenLockerTester::screen_locker_views() const { + DCHECK(ScreenLocker::screen_locker_); + // TODO(flackr): Generalize testing infrastructure to work with WebUI. + DCHECK(!ScreenLocker::UseWebUILockScreen()); + return static_cast<ScreenLockerViews*>( + ScreenLocker::screen_locker_->delegate_.get()); } } // namespace test diff --git a/chrome/browser/chromeos/login/screen_locker_tester.h b/chrome/browser/chromeos/login/screen_locker_tester.h index dc362fa..19975fc 100644 --- a/chrome/browser/chromeos/login/screen_locker_tester.h +++ b/chrome/browser/chromeos/login/screen_locker_tester.h @@ -19,6 +19,7 @@ class Widget; namespace chromeos { class ScreenLocker; +class ScreenLockerViews; namespace test { @@ -50,6 +51,9 @@ class ScreenLockerTester { views::Widget* GetChildWidget() const; + // Returns the ScreenLockerViews object. + ScreenLockerViews* screen_locker_views() const; + private: friend class chromeos::ScreenLocker; diff --git a/chrome/browser/chromeos/login/screen_locker_views.cc b/chrome/browser/chromeos/login/screen_locker_views.cc new file mode 100644 index 0000000..d0606f5 --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_views.cc @@ -0,0 +1,936 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/screen_locker_views.h" + +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> + +// Evil hack to undo X11 evil #define. +#undef None +#undef Status + +#include "base/command_line.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/login/background_view.h" +#include "chrome/browser/chromeos/login/login_performer.h" +#include "chrome/browser/chromeos/login/message_bubble.h" +#include "chrome/browser/chromeos/login/screen_lock_view.h" +#include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/chromeos/login/shutdown_button.h" +#include "chrome/browser/chromeos/view_ids.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "content/browser/user_metrics.h" +#include "grit/theme_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/screen.h" + +#if defined(TOOLKIT_USES_GTK) +#include "chrome/browser/chromeos/wm_ipc.h" +#endif + +namespace { + +// The active ScreenLockerDelegate. +chromeos::ScreenLockerViews* screen_locker_view_ = NULL; + +// The maximum duration for which locker should try to grab the keyboard and +// mouse and its interval for regrabbing on failure. +const int kMaxGrabFailureSec = 30; +const int64 kRetryGrabIntervalMs = 500; + +// Maximum number of times we'll try to grab the keyboard and mouse before +// giving up. If we hit the limit, Chrome exits and the session is terminated. +const int kMaxGrabFailures = kMaxGrabFailureSec * 1000 / kRetryGrabIntervalMs; + +// A idle time to show the screen saver in seconds. +const int kScreenSaverIdleTimeout = 15; + +// A ScreenLock window that covers entire screen to keep the keyboard +// focus/events inside the grab widget. +class LockWindow : public views::NativeWidgetGtk { + public: + LockWindow() + : views::NativeWidgetGtk(new views::Widget), + toplevel_focus_widget_(NULL) { + EnableDoubleBuffer(true); + } + + // GTK propagates key events from parents to children. + // Make sure LockWindow will never handle key events. + virtual gboolean OnEventKey(GtkWidget* widget, GdkEventKey* event) OVERRIDE { + // Don't handle key event in the lock window. + return false; + } + + virtual gboolean OnButtonPress(GtkWidget* widget, + GdkEventButton* event) OVERRIDE { + // Don't handle mouse event in the lock wnidow and + // nor propagate to child. + return true; + } + + virtual void OnDestroy(GtkWidget* object) OVERRIDE { + VLOG(1) << "OnDestroy: LockWindow destroyed"; + views::NativeWidgetGtk::OnDestroy(object); + } + + virtual void ClearNativeFocus() OVERRIDE { + DCHECK(toplevel_focus_widget_); + gtk_widget_grab_focus(toplevel_focus_widget_); + } + + // Sets the widget to move the focus to when clearning the native + // widget's focus. + void set_toplevel_focus_widget(GtkWidget* widget) { + gtk_widget_set_can_focus(widget, TRUE); + toplevel_focus_widget_ = widget; + } + + private: + // The widget we set focus to when clearning the focus on native + // widget. In screen locker, gdk input is grabbed in GrabWidget, + // and resetting the focus by using gtk_window_set_focus seems to + // confuse gtk and doesn't let focus move to native widget under + // GrabWidget. + GtkWidget* toplevel_focus_widget_; + + DISALLOW_COPY_AND_ASSIGN(LockWindow); +}; + +// GrabWidget's root view to layout the ScreenLockView at the center +// and the Shutdown button at the left top. +class GrabWidgetRootView + : public views::View, + public chromeos::ScreenLockerViews::ScreenLockViewContainer { + public: + explicit GrabWidgetRootView(chromeos::ScreenLockView* screen_lock_view) + : screen_lock_view_(screen_lock_view), + shutdown_button_(new chromeos::ShutdownButton()) { + shutdown_button_->Init(); + AddChildView(screen_lock_view_); + AddChildView(shutdown_button_); + } + + // views::View implementation. + virtual void Layout() OVERRIDE { + gfx::Size size = screen_lock_view_->GetPreferredSize(); + gfx::Rect b = bounds(); + screen_lock_view_->SetBounds( + b.width() - size.width(), b.height() - size.height(), + size.width(), size.height()); + shutdown_button_->LayoutIn(this); + } + + // ScreenLockerViews::ScreenLockViewContainer implementation: + void SetScreenLockView(views::View* screen_lock_view) OVERRIDE { + if (screen_lock_view_) { + RemoveChildView(screen_lock_view_); + } + screen_lock_view_ = screen_lock_view; + if (screen_lock_view_) { + AddChildViewAt(screen_lock_view_, 0); + } + Layout(); + } + + private: + views::View* screen_lock_view_; + + chromeos::ShutdownButton* shutdown_button_; + + DISALLOW_COPY_AND_ASSIGN(GrabWidgetRootView); +}; + +// A child widget that grabs both keyboard and pointer input. +class GrabWidget : public views::NativeWidgetGtk { + public: + explicit GrabWidget(chromeos::ScreenLocker* screen_locker) + : views::NativeWidgetGtk(new views::Widget), + screen_locker_(screen_locker), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + grab_failure_count_(0), + kbd_grab_status_(GDK_GRAB_INVALID_TIME), + mouse_grab_status_(GDK_GRAB_INVALID_TIME), + signout_link_(NULL), + shutdown_(NULL) { + } + + virtual void Show() OVERRIDE { + views::NativeWidgetGtk::Show(); + signout_link_ = + screen_locker_view_->GetViewByID(VIEW_ID_SCREEN_LOCKER_SIGNOUT_LINK); + shutdown_ = screen_locker_view_->GetViewByID( + VIEW_ID_SCREEN_LOCKER_SHUTDOWN); + // These can be null in guest mode. + } + + void ClearGtkGrab() { + GtkWidget* current_grab_window; + // Grab gtk input first so that the menu holding gtk grab will + // close itself. + gtk_grab_add(window_contents()); + + // Make sure there is no gtk grab widget so that gtk simply propagates + // an event. This is necessary to allow message bubble and password + // field, button to process events simultaneously. GTK + // maintains grab widgets in a linked-list, so we need to remove + // until it's empty. + while ((current_grab_window = gtk_grab_get_current()) != NULL) + gtk_grab_remove(current_grab_window); + } + + virtual gboolean OnEventKey(GtkWidget* widget, GdkEventKey* event) OVERRIDE { + views::KeyEvent key_event(reinterpret_cast<GdkEvent*>(event)); + // This is a hack to workaround the issue crosbug.com/10655 due to + // the limitation that a focus manager cannot handle views in + // TYPE_CHILD NativeWidgetGtk correctly. + if (signout_link_ && + event->type == GDK_KEY_PRESS && + (event->keyval == GDK_Tab || + event->keyval == GDK_ISO_Left_Tab || + event->keyval == GDK_KP_Tab)) { + DCHECK(shutdown_); + bool reverse = event->state & GDK_SHIFT_MASK; + if (reverse && signout_link_->HasFocus()) { + shutdown_->RequestFocus(); + return true; + } + if (!reverse && shutdown_->HasFocus()) { + signout_link_->RequestFocus(); + return true; + } + } + return views::NativeWidgetGtk::OnEventKey(widget, event); + } + + virtual gboolean OnButtonPress(GtkWidget* widget, + GdkEventButton* event) OVERRIDE { + NativeWidgetGtk::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(); + + // This method tries to steal pointer/keyboard grab from other + // client by sending events that will hopefully close menus or windows + // that have the grab. + void TryUngrabOtherClients(); + + private: + virtual void HandleGtkGrabBroke() OVERRIDE { + // 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_NE(GDK_GRAB_SUCCESS, kbd_grab_status_); + CHECK_NE(GDK_GRAB_SUCCESS, mouse_grab_status_); + } + + // Define separate methods for each error code so that stack trace + // will tell which error the grab failed with. + void FailedWithGrabAlreadyGrabbed() { + LOG(FATAL) << "Grab already grabbed"; + } + void FailedWithGrabInvalidTime() { + LOG(FATAL) << "Grab invalid time"; + } + void FailedWithGrabNotViewable() { + LOG(FATAL) << "Grab not viewable"; + } + void FailedWithGrabFrozen() { + LOG(FATAL) << "Grab frozen"; + } + void FailedWithUnknownError() { + LOG(FATAL) << "Grab uknown"; + } + + chromeos::ScreenLocker* screen_locker_; + base::WeakPtrFactory<GrabWidget> weak_factory_; + + // The number times the widget tried to grab all focus. + int grab_failure_count_; + // Status of keyboard and mouse grab. + GdkGrabStatus kbd_grab_status_; + GdkGrabStatus mouse_grab_status_; + + views::View* signout_link_; + views::View* shutdown_; + + DISALLOW_COPY_AND_ASSIGN(GrabWidget); +}; + +void GrabWidget::TryGrabAllInputs() { + // Grab x server so that we can atomically grab and take + // action when grab fails. + gdk_x11_grab_server(); + if (kbd_grab_status_ != GDK_GRAB_SUCCESS) { + kbd_grab_status_ = gdk_keyboard_grab(window_contents()->window, FALSE, + GDK_CURRENT_TIME); + } + if (mouse_grab_status_ != GDK_GRAB_SUCCESS) { + mouse_grab_status_ = + gdk_pointer_grab(window_contents()->window, + FALSE, + static_cast<GdkEventMask>( + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK), + NULL, + NULL, + GDK_CURRENT_TIME); + } + if ((kbd_grab_status_ != GDK_GRAB_SUCCESS || + mouse_grab_status_ != GDK_GRAB_SUCCESS) && + grab_failure_count_++ < kMaxGrabFailures) { + LOG(WARNING) << "Failed to grab inputs. Trying again in " + << kRetryGrabIntervalMs << " ms: kbd=" + << kbd_grab_status_ << ", mouse=" << mouse_grab_status_; + TryUngrabOtherClients(); + gdk_x11_ungrab_server(); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&GrabWidget::TryGrabAllInputs, weak_factory_.GetWeakPtr()), + kRetryGrabIntervalMs); + } else { + gdk_x11_ungrab_server(); + GdkGrabStatus status = kbd_grab_status_; + if (status == GDK_GRAB_SUCCESS) { + status = mouse_grab_status_; + } + switch (status) { + case GDK_GRAB_SUCCESS: + break; + case GDK_GRAB_ALREADY_GRABBED: + FailedWithGrabAlreadyGrabbed(); + break; + case GDK_GRAB_INVALID_TIME: + FailedWithGrabInvalidTime(); + break; + case GDK_GRAB_NOT_VIEWABLE: + FailedWithGrabNotViewable(); + break; + case GDK_GRAB_FROZEN: + FailedWithGrabFrozen(); + break; + default: + FailedWithUnknownError(); + break; + } + DVLOG(1) << "Grab Success"; + screen_locker_view_->OnGrabInputs(); + } +} + +void GrabWidget::TryUngrabOtherClients() { +#if !defined(NDEBUG) + { + int event_base, error_base; + int major, minor; + // Make sure we have XTest extension. + DCHECK(XTestQueryExtension(ui::GetXDisplay(), + &event_base, &error_base, + &major, &minor)); + } +#endif + + // The following code is an attempt to grab inputs by closing + // supposedly opened menu. This happens when a plugin has a menu + // opened. + if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED || + mouse_grab_status_ == GDK_GRAB_FROZEN) { + // Successfully grabbed the keyboard, but pointer is still + // grabbed by other client. Another attempt to close supposedly + // opened menu by emulating keypress at the left top corner. + Display* display = ui::GetXDisplay(); + Window root, child; + int root_x, root_y, win_x, winy; + unsigned int mask; + XQueryPointer(display, + ui::GetX11WindowFromGtkWidget(window_contents()), + &root, &child, &root_x, &root_y, + &win_x, &winy, &mask); + XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime); + XTestFakeButtonEvent(display, 1, True, CurrentTime); + XTestFakeButtonEvent(display, 1, False, CurrentTime); + // Move the pointer back. + XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime); + XFlush(display); + } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED || + kbd_grab_status_ == GDK_GRAB_FROZEN) { + // Successfully grabbed the pointer, but keyboard is still grabbed + // by other client. Another attempt to close supposedly opened + // menu by emulating escape key. Such situation must be very + // rare, but handling this just in case + Display* display = ui::GetXDisplay(); + KeyCode escape = XKeysymToKeycode(display, XK_Escape); + XTestFakeKeyEvent(display, escape, True, CurrentTime); + XTestFakeKeyEvent(display, escape, False, CurrentTime); + XFlush(display); + } +} + +// BackgroundView for ScreenLocker, which layouts a lock widget in +// addition to other background components. +class ScreenLockerBackgroundView + : public chromeos::BackgroundView, + public chromeos::ScreenLockerViews::ScreenLockViewContainer { + public: + ScreenLockerBackgroundView(views::Widget* lock_widget, + views::View* screen_lock_view) + : lock_widget_(lock_widget), + screen_lock_view_(screen_lock_view) { + } + + virtual ScreenMode GetScreenMode() const OVERRIDE { + return kScreenLockerMode; + } + + virtual void Layout() OVERRIDE { + chromeos::BackgroundView::Layout(); + gfx::Rect screen = bounds(); + if (screen_lock_view_) { + gfx::Size size = screen_lock_view_->GetPreferredSize(); + gfx::Point origin((screen.width() - size.width()) / 2, + (screen.height() - size.height()) / 2); + gfx::Size widget_size(screen.size()); + widget_size.Enlarge(-origin.x(), -origin.y()); + lock_widget_->SetBounds(gfx::Rect(widget_size)); + } else { + // No password entry. Move the lock widget to off screen. + lock_widget_->SetBounds(gfx::Rect(-100, -100, 1, 1)); + } + } + + // ScreenLockerViews::ScreenLockViewContainer implementation: + virtual void SetScreenLockView(views::View* screen_lock_view) OVERRIDE { + screen_lock_view_ = screen_lock_view; + Layout(); + } + + private: + views::Widget* lock_widget_; + + views::View* screen_lock_view_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockerBackgroundView); +}; + +} // namespace + +namespace chromeos { + +// 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), + initialized_(false) { + DCHECK(src_); + DCHECK(dest_); + } + + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE {} + + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { + if (event->any.window != src_) + return; + if (!initialized_) { + gint src_x, src_y, dest_x, dest_y, width, height, depth; + gdk_window_get_geometry(dest_, &dest_x, &dest_y, &width, &height, &depth); + // wait to compute offset until the info bubble widget's location + // is available. + if (dest_x < 0 || dest_y < 0) + return; + gdk_window_get_geometry(src_, &src_x, &src_y, &width, &height, &depth); + offset_.SetPoint(dest_x - src_x, dest_y - src_y); + initialized_ = true; + } + 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->motion.window = dest_; + g_object_ref(copy->motion.window); + copy->motion.x -= offset_.x(); + copy->motion.y -= offset_.y(); + + gdk_event_put(copy); + gdk_event_free(copy); + } + } + + private: + GdkWindow* src_; + GdkWindow* dest_; + bool initialized_; + + // Offset from src_'s origin to dest_'s origin. + gfx::Point offset_; + + DISALLOW_COPY_AND_ASSIGN(MouseEventRelay); +}; + +// A event observer used to unlock the screen upon user's action +// without asking password. Used in BWSI and auto login mode. +// TODO(oshima): consolidate InputEventObserver and LockerInputEventObserver. +class InputEventObserver : public MessageLoopForUI::Observer { + public: + explicit InputEventObserver(ScreenLocker* screen_locker) + : screen_locker_(screen_locker), + activated_(false) { + } + +#if defined(TOUCH_UI) + virtual base::EventStatus WillProcessEvent( + const base::NativeEvent& event) OVERRIDE { + return base::EVENT_CONTINUE; + } + + virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { + } +#else + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { + if ((event->type == GDK_KEY_PRESS || + event->type == GDK_BUTTON_PRESS || + event->type == GDK_MOTION_NOTIFY) && + !activated_) { + activated_ = true; + std::string not_used_string; + GaiaAuthConsumer::ClientLoginResult not_used; + screen_locker_->OnLoginSuccess(not_used_string, + not_used_string, + not_used, + false, + false); + } + } + + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { + } +#endif + + private: + chromeos::ScreenLocker* screen_locker_; + + bool activated_; + + DISALLOW_COPY_AND_ASSIGN(InputEventObserver); +}; + +// A event observer used to show the screen locker upon +// user action: mouse or keyboard interactions. +// TODO(oshima): this has to be disabled while authenticating. +class LockerInputEventObserver : public MessageLoopForUI::Observer { + public: + explicit LockerInputEventObserver(ScreenLockerViews* screen_locker_view) + : screen_locker_view_(screen_locker_view), + ALLOW_THIS_IN_INITIALIZER_LIST( + timer_(FROM_HERE, + base::TimeDelta::FromSeconds(kScreenSaverIdleTimeout), this, + &LockerInputEventObserver::StartScreenSaver)) { + } + +#if defined(TOUCH_UI) + virtual base::EventStatus WillProcessEvent( + const base::NativeEvent& event) OVERRIDE { + return base::EVENT_CONTINUE; + } + + virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { + } +#else + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { + if ((event->type == GDK_KEY_PRESS || + event->type == GDK_BUTTON_PRESS || + event->type == GDK_MOTION_NOTIFY)) { + timer_.Reset(); + screen_locker_view_->StopScreenSaver(); + } + } + + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { + } +#endif + + private: + void StartScreenSaver() { + screen_locker_view_->StartScreenSaver(); + } + + ScreenLockerViews* screen_locker_view_; + base::DelayTimer<LockerInputEventObserver> timer_; + + DISALLOW_COPY_AND_ASSIGN(LockerInputEventObserver); +}; + +ScreenLockerViews::ScreenLockViewContainer::~ScreenLockViewContainer() { +} + +//////////////////////////////////////////////////////////////////////////////// +// ScreenLockerViews implementation. + +ScreenLockerViews::ScreenLockerViews(ScreenLocker* screen_locker) + : ScreenLockerDelegate(screen_locker), + lock_window_(NULL), + lock_widget_(NULL), + screen_lock_view_(NULL), + captcha_view_(NULL), + grab_container_(NULL), + background_container_(NULL), + error_info_(NULL), + drawn_(false), + input_grabbed_(false) { + screen_locker_view_ = this; +} + +void ScreenLockerViews::Init(bool unlock_on_input) { + static const GdkColor kGdkBlack = {0, 0, 0, 0}; + + gfx::Point left_top(1, 1); + gfx::Rect init_bounds(gfx::Screen::GetMonitorAreaNearestPoint(left_top)); + + LockWindow* lock_window = new LockWindow(); + lock_window_ = lock_window->GetWidget(); + views::Widget::InitParams params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.bounds = init_bounds; + params.native_widget = lock_window; + lock_window_->Init(params); + gtk_widget_modify_bg( + lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack); + + g_signal_connect(lock_window_->GetNativeView(), "client-event", + G_CALLBACK(OnClientEventThunk), this); + + // GTK does not like zero width/height. + if (!unlock_on_input) { + screen_lock_view_ = new ScreenLockView(screen_locker_); + screen_lock_view_->Init(); + screen_lock_view_->SetEnabled(false); + screen_lock_view_->StartThrobber(); + } else { + input_event_observer_.reset(new InputEventObserver(screen_locker_)); + MessageLoopForUI::current()->AddObserver(input_event_observer_.get()); + } + + // Hang on to a cast version of the grab widget so we can call its + // TryGrabAllInputs() method later. (Nobody else needs to use it, so moving + // its declaration to the header instead of keeping it in an anonymous + // namespace feels a bit ugly.) + GrabWidget* grab_widget = new GrabWidget(screen_locker_); + lock_widget_ = grab_widget->GetWidget(); + views::Widget::InitParams lock_params( + views::Widget::InitParams::TYPE_CONTROL); + lock_params.transparent = true; + lock_params.parent_widget = lock_window_; + lock_params.native_widget = grab_widget; + lock_widget_->Init(lock_params); + if (screen_lock_view_) { + GrabWidgetRootView* root_view = new GrabWidgetRootView(screen_lock_view_); + grab_container_ = root_view; + lock_widget_->SetContentsView(root_view); + } + lock_widget_->Show(); + + // Configuring the background url. + std::string url_string = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kScreenSaverUrl); + 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)); + + // Gets user profile and sets default user 24hour flag since we don't + // expose user profile in ScreenLockerBackgroundView. + Profile* profile = ProfileManager::GetDefaultProfile(); + if (profile) { + background_view_->SetDefaultUse24HourClock( + profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock)); + } + + if (background_view_->ScreenSaverEnabled()) + StartScreenSaver(); + +#if defined(TOOLKIT_USES_GTK) + DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); + WmIpc::instance()->SetWindowType( + lock_window_->GetNativeView(), + WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, + NULL); +#endif + + lock_window_->SetContentsView(background_view_); + lock_window_->Show(); + + grab_widget->ClearGtkGrab(); + + // Call this after lock_window_->Show(); otherwise the 1st invocation + // of gdk_xxx_grab() will always fail. + grab_widget->TryGrabAllInputs(); + + // Add the window to its own group so that its grab won't be stolen if + // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. + // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this + // after calling ClearGtkGrab(), as want to be in the default window group + // then so we can break any existing GTK grabs. + GtkWindowGroup* window_group = gtk_window_group_new(); + gtk_window_group_add_window(window_group, + GTK_WINDOW(lock_window_->GetNativeView())); + g_object_unref(window_group); + + lock_window->set_toplevel_focus_widget( + static_cast<views::NativeWidgetGtk*>(lock_widget_->native_widget())-> + window_contents()); +} + +void ScreenLockerViews::OnGrabInputs() { + DVLOG(1) << "OnGrabInputs"; + input_grabbed_ = true; + if (drawn_) + ScreenLockReady(); +} + +void ScreenLockerViews::StartScreenSaver() { + if (!background_view_->IsScreenSaverVisible()) { + VLOG(1) << "StartScreenSaver"; + UserMetrics::RecordAction( + UserMetricsAction("ScreenLocker_StartScreenSaver")); + background_view_->ShowScreenSaver(); + if (screen_lock_view_) { + screen_lock_view_->SetEnabled(false); + screen_lock_view_->SetVisible(false); + } + screen_locker_->ClearErrors(); + } +} + +void ScreenLockerViews::StopScreenSaver() { + if (background_view_->IsScreenSaverVisible()) { + VLOG(1) << "StopScreenSaver"; + if (screen_lock_view_) { + screen_lock_view_->SetVisible(true); + // Place the screen_lock_view_ to the center of the screen. + // Must be called when the view is visible: crosbug.com/15213. + background_view_->Layout(); + screen_lock_view_->RequestFocus(); + } + background_view_->HideScreenSaver(); + screen_locker_->EnableInput(); + } +} + +views::View* ScreenLockerViews::GetViewByID(int id) { + return lock_widget_->GetRootView()->GetViewByID(id); +} + +void ScreenLockerViews::ScreenLockReady() { + ScreenLockerDelegate::ScreenLockReady(); + + if (background_view_->ScreenSaverEnabled()) { + lock_widget_->GetFocusManager()->RegisterAccelerator( + views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); + locker_input_event_observer_.reset(new LockerInputEventObserver(this)); + MessageLoopForUI::current()->AddObserver( + locker_input_event_observer_.get()); + } else { + // Don't enable the password field until we grab all inputs. + SetInputEnabled(true); + } +} + +void ScreenLockerViews::OnAuthenticate() { + screen_lock_view_->StartThrobber(); +} + +void ScreenLockerViews::SetInputEnabled(bool enabled) { + if (screen_lock_view_) { + screen_lock_view_->SetEnabled(enabled); + if (enabled) { + screen_lock_view_->ClearAndSetFocusToPassword(); + screen_lock_view_->StopThrobber(); + } + } +} + +void ScreenLockerViews::SetSignoutEnabled(bool enabled) { + screen_lock_view_->SetSignoutEnabled(enabled); +} + +void ScreenLockerViews::ShowErrorMessage(const string16& message, + bool sign_out_only) { + // Make sure that active Sign Out button is not hidden behind the bubble. + ShowErrorBubble( + message, sign_out_only ? + views::BubbleBorder::BOTTOM_RIGHT : views::BubbleBorder::BOTTOM_LEFT); +} + +// Present user a CAPTCHA challenge with image from |captcha_url|, +// After that shows error bubble with |message|. +void ScreenLockerViews::ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& 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 ScreenLockerViews::ClearErrors() { + if (error_info_) { + error_info_->Close(); + error_info_ = NULL; + } +} + +void ScreenLockerViews::BubbleClosing(Bubble* bubble, bool closed_by_escape) { + error_info_ = NULL; + SetSignoutEnabled(true); + if (mouse_event_relay_.get()) { + MessageLoopForUI::current()->RemoveObserver(mouse_event_relay_.get()); + mouse_event_relay_.reset(); + } +} + +bool ScreenLockerViews::CloseOnEscape() { + return true; +} + +bool ScreenLockerViews::FadeInOnShow() { + return false; +} + +void ScreenLockerViews::OnLinkActivated(size_t index) { +} + +void ScreenLockerViews::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(); +} + +ScreenLockerViews::~ScreenLockerViews() { + if (input_event_observer_.get()) + MessageLoopForUI::current()->RemoveObserver(input_event_observer_.get()); + if (locker_input_event_observer_.get()) { + lock_widget_->GetFocusManager()->UnregisterAccelerator( + views::Accelerator(ui::VKEY_ESCAPE, false, false, false), this); + MessageLoopForUI::current()->RemoveObserver( + locker_input_event_observer_.get()); + } + + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_pointer_ungrab(GDK_CURRENT_TIME); + + DCHECK(lock_window_); + VLOG(1) << "~ScreenLocker(): Closing ScreenLocker window."; + lock_window_->Close(); + // lock_widget_ will be deleted by gtk's destroy signal. + screen_locker_view_ = NULL; +} + +void ScreenLockerViews::OnWindowManagerReady() { + DVLOG(1) << "OnClientEvent: drawn for lock"; + drawn_ = true; + if (input_grabbed_) + ScreenLockReady(); +} + +void ScreenLockerViews::ShowErrorBubble( + const string16& message, + views::BubbleBorder::ArrowLocation arrow_location) { + if (error_info_) + error_info_->Close(); + + gfx::Rect rect = screen_lock_view_->GetPasswordBoundsRelativeTo( + lock_widget_->GetRootView()); + gfx::Rect lock_widget_bounds = lock_widget_->GetClientAreaScreenBounds(); + rect.Offset(lock_widget_bounds.x(), lock_widget_bounds.y()); + error_info_ = MessageBubble::ShowNoGrab( + lock_window_, + rect, + arrow_location, + ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING), + UTF16ToWide(message), + UTF16ToWide(string16()), // TODO(nkostylev): Add help link. + this); + +#if !defined(TOUCH_UI) + 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()); +#endif +} + +bool ScreenLockerViews::AcceleratorPressed( + const views::Accelerator& accelerator) { + if (!background_view_->IsScreenSaverVisible()) { + screen_locker_view_->StartScreenSaver(); + return true; + } + return false; +} + +void ScreenLockerViews::OnClientEvent(GtkWidget* widge, GdkEventClient* event) { +#if defined(TOOLKIT_USES_GTK) + WmIpc::Message msg; + WmIpc::instance()->DecodeMessage(*event, &msg); + if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK) { + OnWindowManagerReady(); + } +#endif +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker_views.h b/chrome/browser/chromeos/login/screen_locker_views.h new file mode 100644 index 0000000..89efdcc --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_views.h @@ -0,0 +1,157 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_VIEWS_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_VIEWS_H_ +#pragma once + +#include "chrome/browser/chromeos/login/captcha_view.h" +#include "chrome/browser/chromeos/login/message_bubble.h" +#include "chrome/browser/chromeos/login/screen_locker_delegate.h" + +namespace chromeos { + +class BackgroundView; +class InputEventObserver; +class LockerInputEventObserver; +class MessageBubble; +class MouseEventRelay; +class ScreenLocker; +class ScreenLockView; + +namespace test { +class ScreenLockerTester; +} + +// This version of ScreenLockerDelegate displays a views interface. This class +// shows a BackgroundView and a Signout button as well as creating a +// ScreenLockView to allow the user to log in. +class ScreenLockerViews : public ScreenLockerDelegate, + 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 ScreenLockerViews(ScreenLocker* screen_locker); + + // Called when the all inputs are grabbed. + void OnGrabInputs(); + + // Starts screen saver. + virtual void StartScreenSaver(); + + // Stops screen saver. + virtual void StopScreenSaver(); + + // Returns a view that has given view |id|, or null if it doesn't exist. + views::View* GetViewByID(int id); + + // ScreenLockerDelegate implementation: + virtual void Init(bool unlock_on_input) OVERRIDE; + virtual void ScreenLockReady() OVERRIDE; + virtual void OnAuthenticate() OVERRIDE; + virtual void SetInputEnabled(bool enabled) OVERRIDE; + virtual void SetSignoutEnabled(bool enabled) OVERRIDE; + virtual void ShowErrorMessage(const string16& message, + bool sign_out_only) OVERRIDE; + virtual void ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& message) OVERRIDE; + virtual void ClearErrors() OVERRIDE; + + // Overridden from views::BubbleDelegate. + virtual void BubbleClosing(Bubble* bubble, bool closed_by_escape) OVERRIDE; + virtual bool CloseOnEscape() OVERRIDE; + virtual bool FadeInOnShow() OVERRIDE; + virtual void OnLinkActivated(size_t index) OVERRIDE; + + // CaptchaView::Delegate implementation: + virtual void OnCaptchaEntered(const std::string& captcha) OVERRIDE; + + private: + friend class LockerInputEventObserver; + friend class test::ScreenLockerTester; + + virtual ~ScreenLockerViews(); + + // Called when the window manager is ready to handle locked state. + void OnWindowManagerReady(); + + // Shows error_info_ bubble with the |message| and |arrow_location| specified. + // Assumes that UI controls were locked before that. + void ShowErrorBubble(const string16& message, + views::BubbleBorder::ArrowLocation arrow_location); + + // Overridden from AcceleratorTarget: + virtual bool AcceleratorPressed(const views::Accelerator& accelerator) + OVERRIDE; + + // Event handler for client-event. + CHROMEGTK_CALLBACK_1(ScreenLockerViews, void, OnClientEvent, GdkEventClient*); + + // The screen locker window. + views::Widget* lock_window_; + + // Child widget to grab the keyboard/mouse input. + views::Widget* lock_widget_; + + // A view that accepts password. + ScreenLockView* screen_lock_view_; + + // 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. + string16 postponed_error_message_; + + // 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_; + + // A message loop observer to detect user's keyboard/mouse event. + // Used when |unlock_on_input_| is true. + scoped_ptr<InputEventObserver> input_event_observer_; + + // A message loop observer to detect user's keyboard/mouse event. + // Used when to show the screen locker upon such an event. + scoped_ptr<LockerInputEventObserver> locker_input_event_observer_; + + // An info bubble to display login failure message. + MessageBubble* error_info_; + + // True if the screen locker's window has been drawn. + bool drawn_; + + // True if both mouse input and keyboard input are grabbed. + bool input_grabbed_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockerViews); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_VIEWS_H_ diff --git a/chrome/browser/chromeos/login/screen_locker_webui.cc b/chrome/browser/chromeos/login/screen_locker_webui.cc new file mode 100644 index 0000000..62ca554 --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_webui.cc @@ -0,0 +1,353 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/login/screen_locker_webui.h" + +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> + +// Evil hack to undo X11 evil #define. +#undef None +#undef Status + +#include "base/command_line.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/chromeos/wm_ipc.h" +#include "chrome/common/url_constants.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/screen.h" +#include "views/widget/native_widget_gtk.h" + +namespace { + +chromeos::ScreenLockerWebUI* screen_locker_webui_ = NULL; + +// A ScreenLock window that covers entire screen to keep the keyboard +// focus/events inside the grab widget. +class LockWindow : public views::NativeWidgetGtk { + public: + LockWindow() + : views::NativeWidgetGtk(new views::Widget), + toplevel_focus_widget_(NULL) { + EnableDoubleBuffer(true); + } + + // GTK propagates key events from parents to children. + // Make sure LockWindow will never handle key events. + virtual gboolean OnEventKey(GtkWidget* widget, GdkEventKey* event) OVERRIDE { + // Don't handle key event in the lock window. + return false; + } + + virtual gboolean OnButtonPress(GtkWidget* widget, + GdkEventButton* event) OVERRIDE { + // Don't handle mouse event in the lock wnidow and + // nor propagate to child. + return true; + } + + virtual void OnDestroy(GtkWidget* object) OVERRIDE { + VLOG(1) << "OnDestroy: LockWindow destroyed"; + views::NativeWidgetGtk::OnDestroy(object); + } + + virtual void ClearNativeFocus() OVERRIDE { + DCHECK(toplevel_focus_widget_); + gtk_widget_grab_focus(toplevel_focus_widget_); + } + + // Sets the widget to move the focus to when clearning the native + // widget's focus. + void set_toplevel_focus_widget(GtkWidget* widget) { + gtk_widget_set_can_focus(widget, TRUE); + toplevel_focus_widget_ = widget; + } + + private: + // The widget we set focus to when clearning the focus on native + // widget. In screen locker, gdk input is grabbed in GrabWidget, + // and resetting the focus by using gtk_window_set_focus seems to + // confuse gtk and doesn't let focus move to native widget under + // GrabWidget. + GtkWidget* toplevel_focus_widget_; + + DISALLOW_COPY_AND_ASSIGN(LockWindow); +}; + +} // namespace + +namespace chromeos { + +// A event observer used to unlock the screen upon user's action +// without asking password. Used in BWSI and auto login mode. +class InputEventObserver : public MessageLoopForUI::Observer { + public: + explicit InputEventObserver(ScreenLocker* screen_locker) + : screen_locker_(screen_locker), + activated_(false) { + } + +#if defined(TOUCH_UI) + virtual base::EventStatus WillProcessEvent( + const base::NativeEvent& event) OVERRIDE { + return base::EVENT_CONTINUE; + } + + virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { + } +#else + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE { + if ((event->type == GDK_KEY_PRESS || + event->type == GDK_BUTTON_PRESS || + event->type == GDK_MOTION_NOTIFY) && + !activated_) { + activated_ = true; + std::string not_used_string; + GaiaAuthConsumer::ClientLoginResult not_used; + screen_locker_->OnLoginSuccess(not_used_string, + not_used_string, + not_used, + false, + false); + } + } + + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE { + } +#endif + + private: + chromeos::ScreenLocker* screen_locker_; + + bool activated_; + + DISALLOW_COPY_AND_ASSIGN(InputEventObserver); +}; + +// ScreenLockWebUI creates components necessary to authenticate a user to +// unlock the screen. +class ScreenLockWebUI : public WebUILoginView { + public: + explicit ScreenLockWebUI(ScreenLocker* screen_locker); + virtual ~ScreenLockWebUI(); + + // WebUILoginView overrides: + virtual void Init() OVERRIDE; + + // Clears and sets the focus to the password field. + void ClearAndSetFocusToPassword(); + + // Enable/Disable signout button. + void SetSignoutEnabled(bool enabled); + + // Display error message. + void DisplayErrorMessage(string16 message); + + void SubmitPassword(); + + // Overridden from views::Views: + virtual bool AcceleratorPressed( + const views::Accelerator& accelerator) OVERRIDE; + virtual void HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) OVERRIDE; + + protected: + // WebUILoginView overrides: + virtual views::Widget* GetLoginWindow() OVERRIDE; + + private: + friend class test::ScreenLockerTester; + + // ScreenLocker is owned by itself. + ScreenLocker* screen_locker_; + + std::string password_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockWebUI); +}; + +//////////////////////////////////////////////////////////////////////////////// +// ScreenLockerWebUI implementation. + +ScreenLockerWebUI::ScreenLockerWebUI(ScreenLocker* screen_locker) + : ScreenLockerDelegate(screen_locker), + lock_window_(NULL), + screen_lock_webui_(NULL) { + screen_locker_webui_ = this; +} + +void ScreenLockerWebUI::Init(bool unlock_on_input) { + static const GdkColor kGdkBlack = {0, 0, 0, 0}; + + gfx::Point left_top(1, 1); + gfx::Rect init_bounds(gfx::Screen::GetMonitorAreaNearestPoint(left_top)); + + LockWindow* lock_window = new LockWindow(); + lock_window_ = lock_window->GetWidget(); + views::Widget::InitParams params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.bounds = init_bounds; + params.native_widget = lock_window; + lock_window_->Init(params); + gtk_widget_modify_bg( + lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack); + + g_signal_connect(lock_window_->GetNativeView(), "client-event", + G_CALLBACK(OnClientEventThunk), this); + + // GTK does not like zero width/height. + if (!unlock_on_input) { + screen_lock_webui_ = new ScreenLockWebUI(screen_locker_); + screen_lock_webui_->Init(); + screen_lock_webui_->SetEnabled(false); + } else { + input_event_observer_.reset(new InputEventObserver(screen_locker_)); + MessageLoopForUI::current()->AddObserver(input_event_observer_.get()); + } + + DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView())); + WmIpc::instance()->SetWindowType( + lock_window_->GetNativeView(), + WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, + NULL); + + if (screen_lock_webui_) + lock_window_->SetContentsView(screen_lock_webui_); + lock_window_->Show(); + + // Add the window to its own group so that its grab won't be stolen if + // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g. + // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this + // after calling ClearGtkGrab(), as want to be in the default window group + // then so we can break any existing GTK grabs. + GtkWindowGroup* window_group = gtk_window_group_new(); + gtk_window_group_add_window(window_group, + GTK_WINDOW(lock_window_->GetNativeView())); + g_object_unref(window_group); + + lock_window->set_toplevel_focus_widget(lock_window->window_contents()); +} + +void ScreenLockerWebUI::ScreenLockReady() { + ScreenLockerDelegate::ScreenLockReady(); + SetInputEnabled(true); +} + +void ScreenLockerWebUI::OnAuthenticate() { +} + +void ScreenLockerWebUI::SetInputEnabled(bool enabled) { + // TODO(flackr): Implement enabling / disabling WebUI input. +} + +void ScreenLockerWebUI::SetSignoutEnabled(bool enabled) { + // TODO(flackr): Implement enabling / disabling signout. +} + +void ScreenLockerWebUI::ShowErrorMessage(const string16& message, + bool sign_out_only) { + if (screen_lock_webui_) + screen_lock_webui_->DisplayErrorMessage(message); +} + +void ScreenLockerWebUI::ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& message) { + ShowErrorMessage(message, true); +} + +void ScreenLockerWebUI::ClearErrors() { + if (screen_lock_webui_) + screen_lock_webui_->DisplayErrorMessage(string16()); +} + +ScreenLockerWebUI::~ScreenLockerWebUI() { + screen_locker_webui_ = NULL; + if (input_event_observer_.get()) + MessageLoopForUI::current()->RemoveObserver(input_event_observer_.get()); + + DCHECK(lock_window_); + VLOG(1) << "~ScreenLocker(): Closing ScreenLocker window."; + lock_window_->Close(); +} + +void ScreenLockerWebUI::OnWindowManagerReady() { + DVLOG(1) << "OnClientEvent: drawn for lock"; + ScreenLockReady(); +} + +void ScreenLockerWebUI::OnClientEvent(GtkWidget* widge, GdkEventClient* event) { + WmIpc::Message msg; + WmIpc::instance()->DecodeMessage(*event, &msg); + if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK) + OnWindowManagerReady(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ScreenLockWebUI implementation. + +ScreenLockWebUI::ScreenLockWebUI(ScreenLocker* screen_locker) + : screen_locker_(screen_locker) { + DCHECK(screen_locker_); +} + +ScreenLockWebUI::~ScreenLockWebUI() { +} + +void ScreenLockWebUI::Init() { + WebUILoginView::Init(); + LoadURL(GURL(chrome::kChromeUILockScreenURL)); +} + +void ScreenLockWebUI::ClearAndSetFocusToPassword() { + // TODO(flackr): Send a javascript message to clear and focus the password + // field. + NOTIMPLEMENTED(); +} + +void ScreenLockWebUI::SetSignoutEnabled(bool enabled) { + // TODO(flackr): Send a Javascript message to enable / disable the signout + // link. + NOTIMPLEMENTED(); +} + +void ScreenLockWebUI::DisplayErrorMessage(string16 message) { + WebUI* webui = GetWebUI(); + if (webui) { + base::StringValue errorMessage(message); + webui->CallJavascriptFunction("lockScreen.displayError", errorMessage); + } +} + +void ScreenLockWebUI::SubmitPassword() { + screen_locker_->Authenticate(ASCIIToUTF16(password_.c_str())); +} + +bool ScreenLockWebUI::AcceleratorPressed( + const views::Accelerator& accelerator) { + // Override WebUILoginView::AcceleratorPressed to prevent call to WebUI + // cr.ui.Oobe functions. + // TODO(flackr): This might be able to be removed once merging with the login + // screen WebUI is complete. + return true; +} + +void ScreenLockWebUI::HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) { + // Override WebUILoginView::HandleKeyboardEvent to prevent call to WebUI + // cr.ui.Oobe functions. + // TODO(flackr): This might be able to be removed once merging with the login + // screen WebUI is complete. +} + +views::Widget* ScreenLockWebUI::GetLoginWindow() { + DCHECK(screen_locker_webui_); + return screen_locker_webui_->lock_window_; +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/screen_locker_webui.h b/chrome/browser/chromeos/login/screen_locker_webui.h new file mode 100644 index 0000000..2eddee7 --- /dev/null +++ b/chrome/browser/chromeos/login/screen_locker_webui.h @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_WEBUI_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_WEBUI_H_ +#pragma once + +#include "chrome/browser/chromeos/login/helper.h" +#include "chrome/browser/chromeos/login/screen_locker_delegate.h" +#include "chrome/browser/chromeos/login/user_view.h" +#include "chrome/browser/chromeos/login/webui_login_view.h" + +#if defined(TOOLKIT_USES_GTK) +#include "views/widget/native_widget_gtk.h" +#endif + +namespace views { +class ImageView; +} + +namespace chromeos { + +class InputEventObserver; +class ScreenLocker; +class ScreenLockWebUI; +class UserView; + +namespace test { +class ScreenLockerTester; +} + +class ScreenLockerWebUI : public ScreenLockerDelegate { + public: + explicit ScreenLockerWebUI(ScreenLocker* screen_locker); + + // ScreenLockerDelegate implementation: + virtual void Init(bool unlock_on_input) OVERRIDE; + virtual void ScreenLockReady() OVERRIDE; + virtual void OnAuthenticate() OVERRIDE; + virtual void SetInputEnabled(bool enabled) OVERRIDE; + virtual void SetSignoutEnabled(bool enabled) OVERRIDE; + virtual void ShowErrorMessage(const string16& message, + bool sign_out_only) OVERRIDE; + virtual void ShowCaptchaAndErrorMessage(const GURL& captcha_url, + const string16& message) OVERRIDE; + virtual void ClearErrors() OVERRIDE; + + private: + friend class ScreenLockWebUI; + + virtual ~ScreenLockerWebUI(); + + // Called when the window manager is ready to handle locked state. + void OnWindowManagerReady(); + + // Event handler for client-event. + CHROMEGTK_CALLBACK_1(ScreenLockerWebUI, void, OnClientEvent, GdkEventClient*); + + // The screen locker window. + views::Widget* lock_window_; + + // A view that displays a WebUI lock screen. + ScreenLockWebUI* screen_lock_webui_; + + // A message loop observer to detect user's keyboard/mouse event. + // Used when |unlock_on_input_| is true. + scoped_ptr<InputEventObserver> input_event_observer_; + + DISALLOW_COPY_AND_ASSIGN(ScreenLockerWebUI); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SCREEN_LOCKER_WEBUI_H_ diff --git a/chrome/browser/chromeos/login/webui_login_view.cc b/chrome/browser/chromeos/login/webui_login_view.cc index eef9f8f..fced55a 100644 --- a/chrome/browser/chromeos/login/webui_login_view.cc +++ b/chrome/browser/chromeos/login/webui_login_view.cc @@ -330,7 +330,7 @@ void WebUILoginView::InitStatusArea() { status_area_->Init(); status_area_->SetVisible(status_area_visibility_on_init_); - views::Widget* login_window = WebUILoginDisplay::GetLoginWindow(); + views::Widget* login_window = GetLoginWindow(); // Width of |status_window| is meant to be large enough. // The current value of status_area_->GetPreferredSize().width() // will be too small when button status is changed. @@ -415,4 +415,8 @@ void WebUILoginView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { web_ui->CallJavascriptFunction("cr.ui.Oobe.clearErrors"); } +views::Widget* WebUILoginView::GetLoginWindow() { + return WebUILoginDisplay::GetLoginWindow(); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/login/webui_login_view.h b/chrome/browser/chromeos/login/webui_login_view.h index 984cff5..36fe431 100644 --- a/chrome/browser/chromeos/login/webui_login_view.h +++ b/chrome/browser/chromeos/login/webui_login_view.h @@ -98,6 +98,9 @@ class WebUILoginView : public views::View, // Creates and adds the status area (separate window). virtual void InitStatusArea(); + // Get the views widget hosting this WebUILoginView. + virtual views::Widget* GetLoginWindow(); + StatusAreaView* status_area_; // DOMView for rendering a webpage as a webui login. diff --git a/chrome/browser/resources/chromeos/login/lock_screen.css b/chrome/browser/resources/chromeos/login/lock_screen.css new file mode 100644 index 0000000..aef5c5b3 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/lock_screen.css @@ -0,0 +1,51 @@ +/* TODO(flackr): Merge the style (and possibly scripting) of the lock screen + * with the account picker login screen. See http://crbug.com/97980. */ +body { + background-color: black; +} + +#wrapper { + -webkit-box-orient: vertical; + display: -webkit-box; + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +.spacer { + -webkit-box-flex: 1; +} + +#login-box { + -webkit-border-radius: 10px; + background: white; + border: 1px solid #777; + margin: auto; + padding: 10px; + text-align: center; + width: 280px; +} + +#signout { + bottom: 10px; + color: white; + cursor: pointer; + position: absolute; + right: 10px; +} + +#error-box { + -webkit-border-radius: 10px; + background: white; + border: 1px solid red; + color: black; + display: none; + text-align: center; + margin-top: 15px; +} + +#error-box.visible { + display: block; +} diff --git a/chrome/browser/resources/chromeos/login/lock_screen.html b/chrome/browser/resources/chromeos/login/lock_screen.html new file mode 100644 index 0000000..29878f4 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/lock_screen.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html i18n-values="dir:textdirection"> +<head> + <meta charset="utf-8"> + <title>Lock Scree</title> + <link rel="stylesheet" href="lock_screen.css"> + <script src="chrome://lock/strings.js"></script> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/i18n_template.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="chrome://lock/lock_screen.js"></script> +</head> +<body> + <div id="wrapper"> + <div class="spacer"></div> + <div id="login-box"> + <div><img id="user-image"></div> + <div> + <p i18n-content="password"></p> + <input id="password" type="password" value=""> + </div> + <input id="unlock" type="button" i18n-values="value:unlock"> + </div> + <div class="spacer"> + <div id="error-box"></div> + </div> + </div> + <div id="signout"> + <a id="signout-link"><span i18n-content="signout"></span></a> + </div> +</body> +</html> diff --git a/chrome/browser/resources/chromeos/login/lock_screen.js b/chrome/browser/resources/chromeos/login/lock_screen.js new file mode 100644 index 0000000..94efe2f --- /dev/null +++ b/chrome/browser/resources/chromeos/login/lock_screen.js @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('lockScreen', function() { + 'use strict'; + + /** + * Initialize the lock screen by setting up buttons and substituting in + * translated strings. + */ + function initialize() { + i18nTemplate.process(document, templateData); + + $('unlock').onclick = unlockScreen; + $('signout-link').onclick = signout; + $('user-image').src = 'chrome://userimage/' + templateData.email; + $('password').focus(); + displayError(''); + } + + function displayError(message) { + $('error-box').textContent = message; + if (message) { + $('error-box').className = 'visible'; + } else { + $('error-box').className = ''; + } + } + + /** + * Attempt to unlock the screen with the current password. + */ + function unlockScreen() { + chrome.send('unlockScreenRequest', [$('password').value]); + } + + /** + * Request signing out the current user. + */ + function signout() { + chrome.send('signoutRequest'); + } + + return { + initialize: initialize, + displayError: displayError, + }; +}); + +document.addEventListener('DOMContentLoaded', lockScreen.initialize); diff --git a/chrome/browser/ui/webui/chrome_web_ui_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_factory.cc index 7dc77c3..8a24f7f 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_factory.cc @@ -50,6 +50,7 @@ #include "chrome/browser/ui/webui/chromeos/enterprise_enrollment_ui.h" #include "chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.h" #include "chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h" +#include "chrome/browser/ui/webui/chromeos/login/lock_screen_ui.h" #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" #include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h" #include "chrome/browser/ui/webui/chromeos/proxy_settings_ui.h" @@ -212,6 +213,8 @@ static WebUIFactoryFunction GetWebUIFactoryFunction(Profile* profile, return &NewWebUI<ImageBurnUI>; if (url.host() == chrome::kChromeUIKeyboardOverlayHost) return &NewWebUI<KeyboardOverlayUI>; + if (url.host() == chrome::kChromeUILockScreenHost) + return &NewWebUI<chromeos::LockScreenUI>; if (url.host() == chrome::kChromeUIMobileSetupHost) return &NewWebUI<MobileSetupUI>; if (url.host() == chrome::kChromeUIOobeHost) diff --git a/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.cc b/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.cc new file mode 100644 index 0000000..dbb99a1 --- /dev/null +++ b/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/chromeos/login/lock_screen_ui.h" + +#include "base/bind.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/chromeos/login/screen_locker.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/chrome_web_ui_data_source.h" +#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h" +#include "chrome/common/url_constants.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" + +namespace chromeos { + +LockScreenUI::LockScreenUI(TabContents* contents) : ChromeWebUI(contents) { + // Set up the chrome://lock source. + ChromeWebUIDataSource* html_source = + new ChromeWebUIDataSource(chrome::kChromeUILockScreenHost); + + // Register callback handler. + RegisterMessageCallback("unlockScreenRequest", + base::Bind(&LockScreenUI::UnlockScreenRequest, base::Unretained(this))); + RegisterMessageCallback("signoutRequest", + base::Bind(&LockScreenUI::SignoutRequest, base::Unretained(this))); + + // Email address of current user. + html_source->AddString("email", ASCIIToUTF16( + chromeos::UserManager::Get()->logged_in_user().email())); + + // Localized strings. + // TODO(flackr): Change button text to unlock. + html_source->AddLocalizedString("unlock", IDS_LOGIN_BUTTON); + html_source->AddLocalizedString("password", IDS_LOGIN_PASSWORD); + html_source->AddLocalizedString("signout", IDS_SCREEN_LOCK_SIGN_OUT); + html_source->set_json_path("strings.js"); + + // Add required resources. + html_source->add_resource_path("lock_screen.css", IDR_LOCK_SCREEN_CSS); + html_source->add_resource_path("lock_screen.js", IDR_LOCK_SCREEN_JS); + html_source->set_default_resource(IDR_LOCK_SCREEN_HTML); + + Profile* profile = Profile::FromBrowserContext(contents->browser_context()); + profile->GetChromeURLDataManager()->AddDataSource(html_source); + + // Set up the chrome://userimage/ source. + UserImageSource* user_image_source = new UserImageSource(); + profile->GetChromeURLDataManager()->AddDataSource(user_image_source); +} + +LockScreenUI::~LockScreenUI() { +} + +void LockScreenUI::UnlockScreenRequest(const base::ListValue* args) { + string16 password; + if (!args->GetString(0, &password)) + return; + + chromeos::ScreenLocker* screen_locker_ = + chromeos::ScreenLocker::default_screen_locker(); + if (!screen_locker_) + return; + + screen_locker_->Authenticate(password); +} + +void LockScreenUI::SignoutRequest(const base::ListValue* args) { + chromeos::ScreenLocker* screen_locker_ = + chromeos::ScreenLocker::default_screen_locker(); + if (!screen_locker_) + return; + + screen_locker_->Signout(); +} + +} // namespace chromeos diff --git a/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.h b/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.h new file mode 100644 index 0000000..878a8bd --- /dev/null +++ b/chrome/browser/ui/webui/chromeos/login/lock_screen_ui.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_LOCK_SCREEN_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_LOCK_SCREEN_UI_H_ +#pragma once + +#include "base/values.h" +#include "chrome/browser/ui/webui/chrome_web_ui.h" + +namespace chromeos { + +// The WebUI for Screen Locker. +class LockScreenUI : public ChromeWebUI { + public: + explicit LockScreenUI(TabContents* contents); + virtual ~LockScreenUI(); + + private: + // Send an unlock request to the active screen locker. + void UnlockScreenRequest(const base::ListValue* args); + + // Signout the active user. + void SignoutRequest(const base::ListValue* args); + + DISALLOW_COPY_AND_ASSIGN(LockScreenUI); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_LOCK_SCREEN_UI_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index fc6a708..c477393 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -602,6 +602,12 @@ 'browser/chromeos/login/screen_lock_view.h', 'browser/chromeos/login/screen_locker.cc', 'browser/chromeos/login/screen_locker.h', + 'browser/chromeos/login/screen_locker_delegate.cc', + 'browser/chromeos/login/screen_locker_delegate.h', + 'browser/chromeos/login/screen_locker_views.cc', + 'browser/chromeos/login/screen_locker_views.h', + 'browser/chromeos/login/screen_locker_webui.cc', + 'browser/chromeos/login/screen_locker_webui.h', 'browser/chromeos/login/screen_observer.h', 'browser/chromeos/login/shutdown_button.cc', 'browser/chromeos/login/shutdown_button.h', @@ -3627,6 +3633,8 @@ 'browser/ui/webui/chromeos/login/enterprise_oauth_enrollment_screen_handler.h', 'browser/ui/webui/chromeos/login/eula_screen_handler.cc', 'browser/ui/webui/chromeos/login/eula_screen_handler.h', + 'browser/ui/webui/chromeos/login/lock_screen_ui.cc', + 'browser/ui/webui/chromeos/login/lock_screen_ui.h', 'browser/ui/webui/chromeos/login/network_dropdown.cc', 'browser/ui/webui/chromeos/login/network_dropdown.h', 'browser/ui/webui/chromeos/login/network_dropdown_handler.cc', @@ -5088,6 +5096,8 @@ ['exclude', '^browser/chromeos/frame/'], ['exclude', '^browser/chromeos/login/background_view.cc'], ['exclude', '^browser/chromeos/login/screen_locker_browsertest.cc'], + ['exclude', '^browser/chromeos/login/screen_locker_views.cc'], + ['exclude', '^browser/chromeos/login/screen_locker_webui.cc'], ['exclude', '^browser/chromeos/login/screen_locker.cc'], ['exclude', '^browser/chromeos/login/screen_lock_view.cc'], ['exclude', '^browser/chromeos/login/shutdown_button.cc'], diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 1cd7ffa..94fd516 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -1058,6 +1058,9 @@ const char kVersion[] = "version"; const char kWinHttpProxyResolver[] = "winhttp-proxy-resolver"; #if defined(OS_CHROMEOS) +// Enable WebUI based lock screen. +const char kWebUILockScreen[] = "webui-lock-screen"; + // Enable WebUI based OOBE and login. const char kWebUILogin[] = "webui-login"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 0812fdb..453b166 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -290,6 +290,7 @@ extern const char kVersion[]; extern const char kWinHttpProxyResolver[]; #if defined(OS_CHROMEOS) +extern const char kWebUILockScreen[]; extern const char kWebUILogin[]; extern const char kSkipOAuthLogin[]; extern const char kEnableBluetooth[]; diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 87370e4..25206ff 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc @@ -80,6 +80,7 @@ const char kChromeUIEnterpriseEnrollmentURL[] = "chrome://enterprise-enrollment/"; const char kChromeUIImageBurnerURL[] = "chrome://imageburner/"; const char kChromeUIKeyboardOverlayURL[] = "chrome://keyboardoverlay/"; +const char kChromeUILockScreenURL[] = "chrome://lock/"; const char kChromeUIMediaplayerURL[] = "chrome://mediaplayer/"; const char kChromeUIMobileSetupURL[] = "chrome://mobilesetup/"; const char kChromeUIOSCreditsURL[] = "chrome://os-credits/"; @@ -192,6 +193,7 @@ const char kChromeUIDiscardsHost[] = "discards"; const char kChromeUIEnterpriseEnrollmentHost[] = "enterprise-enrollment"; const char kChromeUIImageBurnerHost[] = "imageburner"; const char kChromeUIKeyboardOverlayHost[] = "keyboardoverlay"; +const char kChromeUILockScreenHost[] = "lock"; const char kChromeUILoginContainerHost[] = "login-container"; const char kChromeUILoginHost[] = "login"; const char kChromeUIMediaplayerHost[] = "mediaplayer"; diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index a3c6a5d..bcea8f1 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h @@ -73,6 +73,7 @@ extern const char kChromeUIDiscardsURL[]; extern const char kChromeUIEnterpriseEnrollmentURL[]; extern const char kChromeUIImageBurnerURL[]; extern const char kChromeUIKeyboardOverlayURL[]; +extern const char kChromeUILockScreenURL[]; extern const char kChromeUIMediaplayerURL[]; extern const char kChromeUIMobileSetupURL[]; extern const char kChromeUIOSCreditsURL[]; @@ -183,6 +184,7 @@ extern const char kChromeUIDiscardsHost[]; extern const char kChromeUIEnterpriseEnrollmentHost[]; extern const char kChromeUIImageBurnerHost[]; extern const char kChromeUIKeyboardOverlayHost[]; +extern const char kChromeUILockScreenHost[]; extern const char kChromeUILoginContainerHost[]; extern const char kChromeUILoginHost[]; extern const char kChromeUIMediaplayerHost[]; |