Index: chrome/browser/chromeos/login/webui_screen_locker.cc |
diff --git a/chrome/browser/chromeos/login/webui_screen_locker.cc b/chrome/browser/chromeos/login/webui_screen_locker.cc |
index a6798c7e1c246c550d6f49ddfbee7a4c7761e05c..8f328b55c5fb67f4a7a2fa719eb7ea5f73fab201 100644 |
--- a/chrome/browser/chromeos/login/webui_screen_locker.cc |
+++ b/chrome/browser/chromeos/login/webui_screen_locker.cc |
@@ -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 |