Commit 59de2f5c authored by flackr@chromium.org's avatar flackr@chromium.org

Enable WebUI ScreenLocker for aura by abstracting lock window.

This CL abstracts the actual lock window allowing for a GTK implementation and an aura implementation. The aura screenlocker stubs are removed and the screenlocker is enabled on aura using the aura lock window.

BUG=100723
TEST=Build chrome with use_aura=1, trigger screen lock and test unlocking screen.


Review URL: http://codereview.chromium.org/8711003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112709 0039d316-1c4b-4281-b951-d872f2087c98
parent 919bd435
......@@ -2,12 +2,7 @@
// 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/user.h"
#include "chrome/browser/chromeos/notifications/system_notification.h"
#include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/notifications/balloon_collection.h"
namespace chromeos {
......@@ -79,87 +74,4 @@ std::string SystemNotification::Delegate::id() const {
return id_;
}
//////////////////////////////////////////////////////////////////////////////
// ScreenLocker
ScreenLocker::ScreenLocker(const chromeos::User& user) : user_(user) {
NOTIMPLEMENTED();
}
ScreenLocker::~ScreenLocker() {
NOTIMPLEMENTED();
}
void ScreenLocker::Init() {
NOTIMPLEMENTED();
}
void ScreenLocker::OnLoginFailure(const LoginFailure& error) {
NOTIMPLEMENTED();
}
void ScreenLocker::OnLoginSuccess(
const std::string&,
const std::string&,
const GaiaAuthConsumer::ClientLoginResult&,
bool,
bool) {
NOTIMPLEMENTED();
}
void ScreenLocker::Authenticate(const string16& password) {
NOTIMPLEMENTED();
}
void ScreenLocker::ClearErrors() {
NOTIMPLEMENTED();
}
void ScreenLocker::EnableInput() {
NOTIMPLEMENTED();
}
void ScreenLocker::Signout() {
NOTIMPLEMENTED();
}
void ScreenLocker::ShowCaptchaAndErrorMessage(const GURL& captcha_url,
const string16& message) {
NOTIMPLEMENTED();
}
void ScreenLocker::ShowErrorMessage(const string16& message,
bool sign_out_only) {
NOTIMPLEMENTED();
}
void ScreenLocker::SetLoginStatusConsumer(
chromeos::LoginStatusConsumer* consumer) {
NOTIMPLEMENTED();
}
// static
void ScreenLocker::Show() {
NOTIMPLEMENTED();
}
// static
void ScreenLocker::Hide() {
NOTIMPLEMENTED();
}
// static
void ScreenLocker::UnlockScreenFailed() {
NOTIMPLEMENTED();
}
// static
void ScreenLocker::InitClass() {
NOTIMPLEMENTED();
}
void ScreenLocker::ScreenLockReady() {
NOTIMPLEMENTED();
}
} // namespace chromeos
// 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/lock_window.h"
#include <cstddef>
namespace chromeos {
LockWindow::LockWindow() : observer_(NULL) {
}
} // namespace chromeos
// 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_LOCK_WINDOW_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_H_
#pragma once
#include "base/basictypes.h"
class DOMView;
namespace views {
class Widget;
}
namespace chromeos {
// This is the interface which lock windows used for the WebUI screen locker
// implement.
class LockWindow {
public:
// This class provides an interface for the lock window to notify an observer
// about its status.
class Observer {
public:
// This method will be called when the lock window has finished all
// initialization.
virtual void OnLockWindowReady() = 0;
};
LockWindow();
// Attempt to grab inputs on |dom_view|, the actual view displaying the lock
// screen DOM.
virtual void Grab(DOMView* dom_view) = 0;
// Returns the actual widget for the lock window.
virtual views::Widget* GetWidget() = 0;
// Sets the observer class which is notified on lock window events.
void set_observer(Observer* observer) {
observer_ = observer;
}
// Creates an instance of the platform specific lock window.
static LockWindow* Create();
protected:
// The observer's OnLockWindowReady method will be called when the lock
// window has finished all initialization.
Observer* observer_;
DISALLOW_COPY_AND_ASSIGN(LockWindow);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_H_
// 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/lock_window_aura.h"
#include "ui/aura/desktop.h"
#include "ui/aura/window.h"
#include "ui/aura_shell/shell.h"
#include "ui/aura_shell/shell_window_ids.h"
namespace chromeos {
LockWindow* LockWindow::Create() {
return new LockWindowAura();
}
////////////////////////////////////////////////////////////////////////////////
// LockWindow implementation:
void LockWindowAura::Grab(DOMView* dom_view) {
// We already have grab from the lock screen container, just call the ready
// callback immediately.
if (observer_)
observer_->OnLockWindowReady();
}
views::Widget* LockWindowAura::GetWidget() {
return this;
}
////////////////////////////////////////////////////////////////////////////////
// LockWindowAura private:
LockWindowAura::LockWindowAura() {
Init();
}
LockWindowAura::~LockWindowAura() {
}
void LockWindowAura::Init() {
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = gfx::Rect(aura::Desktop::GetInstance()->GetHostSize());
// TODO(flackr): Use a property to specify this container rather than
// depending on shell implementation.
params.parent = aura_shell::Shell::GetInstance()->GetContainer(
aura_shell::internal::kShellWindowId_LockScreenContainer);
views::Widget::Init(params);
}
} // namespace chromeos
// 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_LOCK_WINDOW_AURA_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_AURA_H_
#pragma once
#include "base/compiler_specific.h"
#include "chrome/browser/chromeos/login/lock_window.h"
#include "ui/views/widget/widget.h"
namespace chromeos {
class LockWindowAura : public views::Widget,
public LockWindow {
public:
// LockWindow implementation:
virtual void Grab(DOMView* dom_view) OVERRIDE;
virtual views::Widget* GetWidget() OVERRIDE;
private:
friend class LockWindow;
LockWindowAura();
virtual ~LockWindowAura();
// Initialize the Aura lock window.
void Init();
DISALLOW_COPY_AND_ASSIGN(LockWindowAura);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_AURA_H_
// 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/lock_window_gtk.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/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/webui_login_display.h"
#include "chrome/browser/ui/views/dom_view.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/render_widget_host_view.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/native_widget_gtk.h"
#include "ui/views/widget/widget.h"
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;
// 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";
}
} // namespace
namespace chromeos {
LockWindow* LockWindow::Create() {
return new LockWindowGtk();
}
////////////////////////////////////////////////////////////////////////////////
// LockWindowGtk implementation.
void LockWindowGtk::Grab(DOMView* dom_view) {
// Grab on the RenderWidgetHostView hosting the WebUI login screen.
grab_widget_ = dom_view->dom_contents()->tab_contents()->
GetRenderWidgetHostView()->GetNativeView();
ClearGtkGrab();
// Call this after lock_window_->Show(); otherwise the 1st invocation
// of gdk_xxx_grab() will always fail.
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);
}
views::Widget* LockWindowGtk::GetWidget() {
return views::NativeWidgetGtk::GetWidget();
}
gboolean LockWindowGtk::OnButtonPress(GtkWidget* widget,
GdkEventButton* event) {
// Never propagate mouse events to parent.
return true;
}
void LockWindowGtk::OnDestroy(GtkWidget* object) {
VLOG(1) << "OnDestroy: LockWindow destroyed";
views::NativeWidgetGtk::OnDestroy(object);
}
void LockWindowGtk::ClearNativeFocus() {
gtk_widget_grab_focus(window_contents());
}
////////////////////////////////////////////////////////////////////////////////
// LockWindowGtk private:
LockWindowGtk::LockWindowGtk()
: views::NativeWidgetGtk(new views::Widget),
lock_window_(NULL),
grab_widget_(NULL),
drawn_(false),
input_grabbed_(false),
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) {
Init();
}
LockWindowGtk::~LockWindowGtk() {
}
void LockWindowGtk::Init() {
static const GdkColor kGdkBlack = {0, 0, 0, 0};
EnableDoubleBuffer(true);
gfx::Rect bounds(gfx::Screen::GetMonitorAreaNearestWindow(NULL));
lock_window_ = GetWidget();
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = bounds;
params.native_widget = this;
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);
DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
WmIpc::instance()->SetWindowType(
lock_window_->GetNativeView(),
WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
NULL);
}
void LockWindowGtk::OnGrabInputs() {
DVLOG(1) << "OnGrabInputs";
input_grabbed_ = true;
if (drawn_ && observer_)
observer_->OnLockWindowReady();
}
void LockWindowGtk::OnWindowManagerReady() {
DVLOG(1) << "OnClientEvent: drawn for lock";
drawn_ = true;
if (input_grabbed_ && observer_)
observer_->OnLockWindowReady();
}
void LockWindowGtk::ClearGtkGrab() {
GtkWidget* current_grab_window;
// Grab gtk input first so that the menu holding gtk grab will
// close itself.
gtk_grab_add(grab_widget_);
// Make sure there is no gtk grab widget so that gtk simply propagates
// an event. 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);
}
void LockWindowGtk::TryGrabAllInputs() {
// Grab x server so that we can atomically grab and take
// action when grab fails.
gdk_x11_grab_server();
gtk_grab_add(grab_widget_);
if (kbd_grab_status_ != GDK_GRAB_SUCCESS)
kbd_grab_status_ = gdk_keyboard_grab(grab_widget_->window,
FALSE,
GDK_CURRENT_TIME);
if (mouse_grab_status_ != GDK_GRAB_SUCCESS) {
mouse_grab_status_ =
gdk_pointer_grab(grab_widget_->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(&LockWindowGtk::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";
OnGrabInputs();
}
}
void LockWindowGtk::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);
}
}
void LockWindowGtk::HandleGtkGrabBroke() {
// 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_);
}
void LockWindowGtk::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();
}
} // namespace chromeos
// 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_LOCK_WINDOW_GTK_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_GTK_H_
#pragma once
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/login/lock_window.h"
#include "chrome/browser/chromeos/login/screen_locker_delegate.h"
#include "ui/views/widget/native_widget_gtk.h"
namespace chromeos {
// A ScreenLock window that covers entire screen to keep the keyboard
// focus/events inside the grab widget.
class LockWindowGtk : public views::NativeWidgetGtk,
public LockWindow {
public:
// LockWindow implementation:
virtual void Grab(DOMView* dom_view) OVERRIDE;
virtual views::Widget* GetWidget() OVERRIDE;
protected:
// NativeWidgetGtk overrides:
virtual gboolean OnButtonPress(GtkWidget* widget,
GdkEventButton* event) OVERRIDE;
virtual void OnDestroy(GtkWidget* object) OVERRIDE;
virtual void ClearNativeFocus() OVERRIDE;
virtual void HandleGtkGrabBroke() OVERRIDE;
private:
friend class LockWindow;
LockWindowGtk();
virtual ~LockWindowGtk();
// Initialize the lock window.
void Init();
// Called when the window manager is ready to handle locked state.
void OnWindowManagerReady();
// Called when the all inputs are grabbed.
void OnGrabInputs();
// Clear current GTK grab.
void ClearGtkGrab();
// 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();
// Event handler for client-event.
CHROMEGTK_CALLBACK_1(LockWindowGtk, void, OnClientEvent, GdkEventClient*)
// The screen locker window.
views::Widget* lock_window_;
// The widget to grab inputs on. This is initialized by Grab to be the
// RenderHostView displaying the WebUI.
GtkWidget* grab_widget_;
// 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_;
base::WeakPtrFactory<LockWindowGtk> 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_;
DISALLOW_COPY_AND_ASSIGN(LockWindowGtk);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_WINDOW_GTK_H_
......@@ -27,7 +27,6 @@
#include "chrome/browser/chromeos/login/authenticator.h"
#include "chrome/browser/chromeos/login/login_performer.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/screen_locker_views.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/webui_screen_locker.h"
#include "chrome/browser/profiles/profile.h"
......@@ -50,6 +49,10 @@
#include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h"
#endif
#if !defined(USE_AURA)
#include "chrome/browser/chromeos/login/screen_locker_views.h"
#endif
using content::BrowserThread;
namespace {
......@@ -201,10 +204,14 @@ ScreenLocker::ScreenLocker(const User& user)
void ScreenLocker::Init() {
authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
#if defined(USE_AURA)
delegate_.reset(new WebUIScreenLocker(this));
#else
if (UseWebUILockScreen())
delegate_.reset(new WebUIScreenLocker(this));
else
delegate_.reset(new ScreenLockerViews(this));
#endif
delegate_->LockScreen(unlock_on_input_);
}
......@@ -392,11 +399,13 @@ void ScreenLocker::UnlockScreenFailed() {
}
}
#if !defined(USE_AURA)
// static
bool ScreenLocker::UseWebUILockScreen() {
return !CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableWebUILockScreen);
}
#endif
// static
void ScreenLocker::InitClass() {
......
......@@ -34,12 +34,7 @@ class ScreenLocker : public LoginStatusConsumer {
// Returns the default instance if it has been created.
static ScreenLocker* default_screen_locker() {
#if defined(TOOLKIT_USES_GTK)
return screen_locker_;
#else
NOTIMPLEMENTED();
return NULL;
#endif
}
// Initialize and show the screen locker.
......@@ -125,10 +120,8 @@ class ScreenLocker : public LoginStatusConsumer {
// Logged in user.
const User& user_;
#if defined(TOOLKIT_USES_GTK)
// Used to authenticate the user to unlock.
scoped_refptr<Authenticator> authenticator_;
#endif
// Unlock the screen when it detects key/mouse event without asking
// password. True when chrome is in BWSI or auto login mode.
......
......@@ -9,7 +9,6 @@
#include "base/values.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/user_manager.h"
......@@ -24,97 +23,12 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/native_widget_gtk.h"
// These conflict with some of the Chrome headers, so must be included last.
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
namespace {
// URL which corresponds to the login WebUI.
const char kLoginURL[] = "chrome://oobe/login";
// 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 ScreenLock window that covers entire screen to keep the keyboard
// focus/events inside the grab widget.
class LockWindow : public views::NativeWidgetGtk {
public:
explicit LockWindow(chromeos::WebUIScreenLocker* webui_screen_locker)
: views::NativeWidgetGtk(new views::Widget),
toplevel_focus_widget_(NULL),
webui_screen_locker_(webui_screen_locker) {
EnableDoubleBuffer(true);
}
virtual gboolean OnButtonPress(GtkWidget* widget,
GdkEventButton* event) OVERRIDE {
// Never propagate mouse events to parent.
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_);
}
virtual void HandleGtkGrabBroke() OVERRIDE {
webui_screen_locker_->HandleGtkGrabBroke();
}
// 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. Gdk input is grabbed in WebUIScreenLocker, and resetting the focus
// by using gtk_window_set_focus seems to confuse gtk and doesn't let focus
// move to native widget under this.
GtkWidget* toplevel_focus_widget_;
// The WebUI screen locker.
chromeos::WebUIScreenLocker* webui_screen_locker_;
DISALLOW_COPY_AND_ASSIGN(LockWindow);
};
// 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";
}
} // namespace
namespace chromeos {
......@@ -124,51 +38,27 @@ namespace chromeos {
WebUIScreenLocker::WebUIScreenLocker(ScreenLocker* screen_locker)
: ScreenLockerDelegate(screen_locker),
drawn_(false),
input_grabbed_(false),
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) {
lock_ready_(false),
webui_ready_(false) {
}
void WebUIScreenLocker::LockScreen(bool unlock_on_input) {
static const GdkColor kGdkBlack = {0, 0, 0, 0};
gfx::Rect bounds(gfx::Screen::GetMonitorAreaNearestWindow(NULL));
LockWindow* lock_window = new LockWindow(this);
LockWindow* lock_window = LockWindow::Create();
lock_window->set_observer(this);
lock_window_ = lock_window->GetWidget();
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = 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);
// TODO(flackr): Obscure the screen with black / a screensaver if
// unlock_on_input.
DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
WmIpc::instance()->SetWindowType(
lock_window_->GetNativeView(),
WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
NULL);
WebUILoginView::Init(lock_window_);
lock_window_->SetContentsView(this);
lock_window_->Show();
OnWindowCreated();
LoadURL(GURL(kLoginURL));
lock_window->Grab(webui_login_);
// User list consisting of a single logged-in user.
UserList users(1, &chromeos::UserManager::Get()->logged_in_user());
login_display_.reset(new WebUILoginDisplay(this));
login_display_->set_background_bounds(bounds);
login_display_->set_parent_window(
GTK_WINDOW(lock_window_->GetNativeView()));
login_display_->Init(users, false, false);
static_cast<OobeUI*>(GetWebUI())->ShowSigninScreen(login_display_.get());
......@@ -176,38 +66,6 @@ void WebUIScreenLocker::LockScreen(bool unlock_on_input) {
registrar_.Add(this,
chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
content::NotificationService::AllSources());
ClearGtkGrab();
// Call this after lock_window_->Show(); otherwise the 1st invocation
// of gdk_xxx_grab() will always fail.
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(lock_window->window_contents());
}
void WebUIScreenLocker::OnGrabInputs() {
DVLOG(1) << "OnGrabInputs";
input_grabbed_ = true;
if (drawn_)
ScreenLockReady();
}
void WebUIScreenLocker::OnWindowManagerReady() {
DVLOG(1) << "OnClientEvent: drawn for lock";
drawn_ = true;
if (input_grabbed_)
ScreenLockReady();
}
void WebUIScreenLocker::ScreenLockReady() {
......@@ -215,142 +73,6 @@ void WebUIScreenLocker::ScreenLockReady() {
SetInputEnabled(true);
}
void WebUIScreenLocker::ClearGtkGrab() {
GtkWidget* current_grab_window;
// Grab gtk input first so that the menu holding gtk grab will
// close itself.
gtk_grab_add(webui_login_->native_view());
// Make sure there is no gtk grab widget so that gtk simply propagates
// an event. 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);
}
void WebUIScreenLocker::TryGrabAllInputs() {
// Grab on the RenderWidgetHostView hosting the WebUI login screen.
GdkWindow* grab_widget = webui_login_->dom_contents()->tab_contents()->
GetRenderWidgetHostView()->GetNativeView()->window;
// Grab x server so that we can atomically grab and take
// action when grab fails.
gdk_x11_grab_server();
gtk_grab_add(webui_login_->native_view());
if (kbd_grab_status_ != GDK_GRAB_SUCCESS)
kbd_grab_status_ = gdk_keyboard_grab(grab_widget, FALSE, GDK_CURRENT_TIME);
if (mouse_grab_status_ != GDK_GRAB_SUCCESS) {
mouse_grab_status_ =
gdk_pointer_grab(grab_widget,
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(&WebUIScreenLocker::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";
OnGrabInputs();
}
}
void WebUIScreenLocker::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(
static_cast<LockWindow*>(lock_window_->native_widget())->
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);
}
}
void WebUIScreenLocker::HandleGtkGrabBroke() {
// 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_);
}
void WebUIScreenLocker::OnAuthenticate() {
}
......@@ -394,13 +116,6 @@ WebUIScreenLocker::~WebUIScreenLocker() {
lock_window_->Close();
}
void WebUIScreenLocker::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();
}
////////////////////////////////////////////////////////////////////////////////
// WebUIScreenLocker, content::NotificationObserver implementation:
......@@ -454,4 +169,23 @@ void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
NOTREACHED();
}
////////////////////////////////////////////////////////////////////////////////
// LockWindow::Observer implementation:
void WebUIScreenLocker::OnLockWindowReady() {
lock_ready_ = true;
if (webui_ready_)
ScreenLockReady();
}
////////////////////////////////////////////////////////////////////////////////
// Overridden from WebUILoginView:
void WebUIScreenLocker::OnTabMainFrameFirstRender() {
WebUILoginView::OnTabMainFrameFirstRender();
webui_ready_ = true;
if (lock_ready_)
ScreenLockReady();
}
} // namespace chromeos
......@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/login/lock_window.h"
#include "chrome/browser/chromeos/login/login_display.h"
#include "chrome/browser/chromeos/login/screen_locker_delegate.h"
#include "chrome/browser/chromeos/login/webui_login_view.h"
......@@ -17,10 +18,6 @@
#include "content/public/browser/notification_registrar.h"
#include "ui/views/widget/widget.h"
#if defined(TOOLKIT_USES_GTK)
#include "ui/views/widget/native_widget_gtk.h"
#endif
namespace chromeos {
class ScreenLocker;
......@@ -31,13 +28,11 @@ class WebUILoginDisplay;
class WebUIScreenLocker : public WebUILoginView,
public LoginDisplay::Delegate,
public content::NotificationObserver,
public ScreenLockerDelegate {
public ScreenLockerDelegate,
public LockWindow::Observer {
public:
explicit WebUIScreenLocker(ScreenLocker* screen_locker);
// Called when the GTK grab breaks.
void HandleGtkGrabBroke();
// ScreenLockerDelegate implementation:
virtual void LockScreen(bool unlock_on_input) OVERRIDE;
virtual void ScreenLockReady() OVERRIDE;
......@@ -67,29 +62,14 @@ class WebUIScreenLocker : public WebUILoginView,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
private:
virtual ~WebUIScreenLocker();
// Called when the window manager is ready to handle locked state.
void OnWindowManagerReady();
// Called when the all inputs are grabbed.
void OnGrabInputs();
// Clear current GTK grab.
void ClearGtkGrab();
// LockWindow::Observer implementation.
virtual void OnLockWindowReady() OVERRIDE;
// 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();
// Overridden from WebUILoginView.
virtual void OnTabMainFrameFirstRender() OVERRIDE;
// 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();
// Event handler for client-event.
CHROMEGTK_CALLBACK_1(WebUIScreenLocker, void, OnClientEvent, GdkEventClient*)
private:
virtual ~WebUIScreenLocker();
// The screen locker window.
views::Widget* lock_window_;
......@@ -100,20 +80,11 @@ class WebUIScreenLocker : public WebUILoginView,
// Used for user image changed notifications.
content::NotificationRegistrar registrar_;
// 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_;
base::WeakPtrFactory<WebUIScreenLocker> weak_factory_;
// The number times the widget tried to grab all focus.
int grab_failure_count_;
// Tracks when the lock window is displayed and ready.
bool lock_ready_;
// Status of keyboard and mouse grab.
GdkGrabStatus kbd_grab_status_;
GdkGrabStatus mouse_grab_status_;
// Tracks when the WebUI finishes loading.
bool webui_ready_;
DISALLOW_COPY_AND_ASSIGN(WebUIScreenLocker);
};
......
......@@ -582,6 +582,12 @@
'browser/chromeos/login/language_switch_menu.h',
'browser/chromeos/login/session_manager_observer.cc',
'browser/chromeos/login/session_manager_observer.h',
'browser/chromeos/login/lock_window.cc',
'browser/chromeos/login/lock_window.h',
'browser/chromeos/login/lock_window_aura.cc',
'browser/chromeos/login/lock_window_aura.h',
'browser/chromeos/login/lock_window_gtk.cc',
'browser/chromeos/login/lock_window_gtk.h',
'browser/chromeos/login/login_display.cc',
'browser/chromeos/login/login_display.h',
'browser/chromeos/login/login_display_host.h',
......@@ -5114,21 +5120,18 @@
# Build Aura with ChromeOS.
['use_aura==1 and chromeos==1', {
'sources/': [
['exclude', '^browser/chromeos/browser/chromeos/login/screen_locker.cc'],
['exclude', '^browser/chromeos/frame/'],
['exclude', '^browser/chromeos/legacy_window_manager/wm_ipc.cc'],
['exclude', '^browser/chromeos/legacy_window_manager/wm_message_listener.cc'],
['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.cc'],
['exclude', '^browser/chromeos/login/screen_lock_view.cc'],
['exclude', '^browser/chromeos/login/shutdown_button.cc'],
['exclude', '^browser/chromeos/login/username_view.cc'],
['exclude', '^browser/chromeos/login/user_view.cc'],
['exclude', '^browser/chromeos/login/views_login_display.cc'],
['exclude', '^browser/chromeos/login/views_login_display_host.cc'],
['exclude', '^browser/chromeos/login/webui_screen_locker.cc'],
['exclude', '^browser/chromeos/login/wizard_in_process_browser_test.cc'],
['exclude', '^browser/chromeos/notifications/'],
['include', '^browser/ui/views/handle_web_keyboard_event_aura.cc'],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment