Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: chrome/browser/chromeos/login/lock_window_gtk.cc

Issue 9664072: Removing WmIpc and related files from ChromeOS (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Copyright Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/login/lock_window_gtk.h"
6
7 #include <X11/extensions/XTest.h>
8 #include <X11/keysym.h>
9 #include <gdk/gdkkeysyms.h>
10 #include <gdk/gdkx.h>
11
12 // Evil hack to undo X11 evil #define.
13 #undef None
14 #undef Status
15
16 #include "base/command_line.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/browser/chromeos/cros/cros_library.h"
20 #include "chrome/browser/chromeos/cros/network_library.h"
21 #include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h"
22 #include "chrome/browser/chromeos/login/helper.h"
23 #include "chrome/browser/chromeos/login/screen_locker.h"
24 #include "chrome/browser/chromeos/login/user_manager.h"
25 #include "chrome/browser/chromeos/login/webui_login_display.h"
26 #include "chrome/browser/ui/views/dom_view.h"
27 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
28 #include "chrome/common/chrome_notification_types.h"
29 #include "chrome/common/url_constants.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_types.h"
32 #include "content/public/browser/render_widget_host_view.h"
33 #include "content/public/browser/web_contents.h"
34 #include "ui/base/l10n/l10n_util.h"
35 #include "ui/base/x/x11_util.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/views/widget/native_widget_gtk.h"
38 #include "ui/views/widget/widget.h"
39
40 namespace {
41
42 // The maximum duration for which locker should try to grab the keyboard and
43 // mouse and its interval for regrabbing on failure.
44 const int kMaxGrabFailureSec = 30;
45 const int64 kRetryGrabIntervalMs = 500;
46
47 // Maximum number of times we'll try to grab the keyboard and mouse before
48 // giving up. If we hit the limit, Chrome exits and the session is terminated.
49 const int kMaxGrabFailures = kMaxGrabFailureSec * 1000 / kRetryGrabIntervalMs;
50
51 // Define separate methods for each error code so that stack trace
52 // will tell which error the grab failed with.
53 void FailedWithGrabAlreadyGrabbed() {
54 LOG(FATAL) << "Grab already grabbed";
55 }
56 void FailedWithGrabInvalidTime() {
57 LOG(FATAL) << "Grab invalid time";
58 }
59 void FailedWithGrabNotViewable() {
60 LOG(FATAL) << "Grab not viewable";
61 }
62 void FailedWithGrabFrozen() {
63 LOG(FATAL) << "Grab frozen";
64 }
65 void FailedWithUnknownError() {
66 LOG(FATAL) << "Grab uknown";
67 }
68
69 } // namespace
70
71 namespace chromeos {
72
73 LockWindow* LockWindow::Create() {
74 return new LockWindowGtk();
75 }
76
77 ////////////////////////////////////////////////////////////////////////////////
78 // LockWindowGtk implementation.
79
80 void LockWindowGtk::Grab(DOMView* dom_view) {
81 // Grab on the RenderWidgetHostView hosting the WebUI login screen.
82 grab_widget_ = dom_view->dom_contents()->web_contents()->
83 GetRenderWidgetHostView()->GetNativeView();
84 ClearGtkGrab();
85
86 // Call this after lock_window_->Show(); otherwise the 1st invocation
87 // of gdk_xxx_grab() will always fail.
88 TryGrabAllInputs();
89
90 // Add the window to its own group so that its grab won't be stolen if
91 // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g.
92 // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this
93 // after calling ClearGtkGrab(), as want to be in the default window group
94 // then so we can break any existing GTK grabs.
95 GtkWindowGroup* window_group = gtk_window_group_new();
96 gtk_window_group_add_window(window_group,
97 GTK_WINDOW(lock_window_->GetNativeView()));
98 g_object_unref(window_group);
99 }
100
101 views::Widget* LockWindowGtk::GetWidget() {
102 return views::NativeWidgetGtk::GetWidget();
103 }
104
105 gboolean LockWindowGtk::OnButtonPress(GtkWidget* widget,
106 GdkEventButton* event) {
107 // Never propagate mouse events to parent.
108 return true;
109 }
110
111 void LockWindowGtk::OnDestroy(GtkWidget* object) {
112 VLOG(1) << "OnDestroy: LockWindow destroyed";
113 views::NativeWidgetGtk::OnDestroy(object);
114 }
115
116 void LockWindowGtk::ClearNativeFocus() {
117 gtk_widget_grab_focus(window_contents());
118 }
119
120 ////////////////////////////////////////////////////////////////////////////////
121 // LockWindowGtk private:
122
123 LockWindowGtk::LockWindowGtk()
124 : views::NativeWidgetGtk(new views::Widget),
125 lock_window_(NULL),
126 grab_widget_(NULL),
127 drawn_(false),
128 input_grabbed_(false),
129 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
130 grab_failure_count_(0),
131 kbd_grab_status_(GDK_GRAB_INVALID_TIME),
132 mouse_grab_status_(GDK_GRAB_INVALID_TIME) {
133 Init();
134 }
135
136 LockWindowGtk::~LockWindowGtk() {
137 }
138
139 void LockWindowGtk::Init() {
140 static const GdkColor kGdkBlack = {0, 0, 0, 0};
141 EnableDoubleBuffer(true);
142
143 gfx::Rect bounds(gfx::Screen::GetMonitorAreaNearestWindow(NULL));
144
145 lock_window_ = GetWidget();
146 views::Widget::InitParams params(
147 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
148 params.bounds = bounds;
149 params.native_widget = this;
150 lock_window_->Init(params);
151 gtk_widget_modify_bg(
152 lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack);
153
154 g_signal_connect(lock_window_->GetNativeView(), "client-event",
155 G_CALLBACK(OnClientEventThunk), this);
156
157 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
158 WmIpc::instance()->SetWindowType(
159 lock_window_->GetNativeView(),
160 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
161 NULL);
162 }
163
164 void LockWindowGtk::OnGrabInputs() {
165 DVLOG(1) << "OnGrabInputs";
166 input_grabbed_ = true;
167 if (drawn_ && observer_)
168 observer_->OnLockWindowReady();
169 }
170
171 void LockWindowGtk::OnWindowManagerReady() {
172 DVLOG(1) << "OnClientEvent: drawn for lock";
173 drawn_ = true;
174 if (input_grabbed_ && observer_)
175 observer_->OnLockWindowReady();
176 }
177
178 void LockWindowGtk::ClearGtkGrab() {
179 GtkWidget* current_grab_window;
180 // Grab gtk input first so that the menu holding gtk grab will
181 // close itself.
182 gtk_grab_add(grab_widget_);
183
184 // Make sure there is no gtk grab widget so that gtk simply propagates
185 // an event. GTK maintains grab widgets in a linked-list, so we need to
186 // remove until it's empty.
187 while ((current_grab_window = gtk_grab_get_current()) != NULL)
188 gtk_grab_remove(current_grab_window);
189 }
190
191 void LockWindowGtk::TryGrabAllInputs() {
192 // Grab x server so that we can atomically grab and take
193 // action when grab fails.
194 gdk_x11_grab_server();
195 gtk_grab_add(grab_widget_);
196 if (kbd_grab_status_ != GDK_GRAB_SUCCESS)
197 kbd_grab_status_ = gdk_keyboard_grab(grab_widget_->window,
198 FALSE,
199 GDK_CURRENT_TIME);
200 if (mouse_grab_status_ != GDK_GRAB_SUCCESS) {
201 mouse_grab_status_ =
202 gdk_pointer_grab(grab_widget_->window,
203 FALSE,
204 static_cast<GdkEventMask>(
205 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
206 GDK_POINTER_MOTION_MASK),
207 NULL,
208 NULL,
209 GDK_CURRENT_TIME);
210 }
211 if ((kbd_grab_status_ != GDK_GRAB_SUCCESS ||
212 mouse_grab_status_ != GDK_GRAB_SUCCESS) &&
213 grab_failure_count_++ < kMaxGrabFailures) {
214 LOG(WARNING) << "Failed to grab inputs. Trying again in "
215 << kRetryGrabIntervalMs << " ms: kbd="
216 << kbd_grab_status_ << ", mouse=" << mouse_grab_status_;
217 TryUngrabOtherClients();
218 gdk_x11_ungrab_server();
219 MessageLoop::current()->PostDelayedTask(
220 FROM_HERE,
221 base::Bind(&LockWindowGtk::TryGrabAllInputs,
222 weak_factory_.GetWeakPtr()),
223 kRetryGrabIntervalMs);
224 } else {
225 gdk_x11_ungrab_server();
226 GdkGrabStatus status = kbd_grab_status_;
227 if (status == GDK_GRAB_SUCCESS) {
228 status = mouse_grab_status_;
229 }
230 switch (status) {
231 case GDK_GRAB_SUCCESS:
232 break;
233 case GDK_GRAB_ALREADY_GRABBED:
234 FailedWithGrabAlreadyGrabbed();
235 break;
236 case GDK_GRAB_INVALID_TIME:
237 FailedWithGrabInvalidTime();
238 break;
239 case GDK_GRAB_NOT_VIEWABLE:
240 FailedWithGrabNotViewable();
241 break;
242 case GDK_GRAB_FROZEN:
243 FailedWithGrabFrozen();
244 break;
245 default:
246 FailedWithUnknownError();
247 break;
248 }
249 DVLOG(1) << "Grab Success";
250 OnGrabInputs();
251 }
252 }
253
254 void LockWindowGtk::TryUngrabOtherClients() {
255 #if !defined(NDEBUG)
256 {
257 int event_base, error_base;
258 int major, minor;
259 // Make sure we have XTest extension.
260 DCHECK(XTestQueryExtension(ui::GetXDisplay(),
261 &event_base, &error_base,
262 &major, &minor));
263 }
264 #endif
265
266 // The following code is an attempt to grab inputs by closing
267 // supposedly opened menu. This happens when a plugin has a menu
268 // opened.
269 if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
270 mouse_grab_status_ == GDK_GRAB_FROZEN) {
271 // Successfully grabbed the keyboard, but pointer is still
272 // grabbed by other client. Another attempt to close supposedly
273 // opened menu by emulating keypress at the left top corner.
274 Display* display = ui::GetXDisplay();
275 Window root, child;
276 int root_x, root_y, win_x, winy;
277 unsigned int mask;
278 XQueryPointer(display,
279 ui::GetX11WindowFromGtkWidget(
280 window_contents()),
281 &root, &child, &root_x, &root_y,
282 &win_x, &winy, &mask);
283 XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime);
284 XTestFakeButtonEvent(display, 1, True, CurrentTime);
285 XTestFakeButtonEvent(display, 1, False, CurrentTime);
286 // Move the pointer back.
287 XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime);
288 XFlush(display);
289 } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
290 kbd_grab_status_ == GDK_GRAB_FROZEN) {
291 // Successfully grabbed the pointer, but keyboard is still grabbed
292 // by other client. Another attempt to close supposedly opened
293 // menu by emulating escape key. Such situation must be very
294 // rare, but handling this just in case
295 Display* display = ui::GetXDisplay();
296 KeyCode escape = XKeysymToKeycode(display, XK_Escape);
297 XTestFakeKeyEvent(display, escape, True, CurrentTime);
298 XTestFakeKeyEvent(display, escape, False, CurrentTime);
299 XFlush(display);
300 }
301 }
302
303 void LockWindowGtk::HandleGtkGrabBroke() {
304 // Input should never be stolen from ScreenLocker once it's
305 // grabbed. If this happens, it's a bug and has to be fixed. We
306 // let chrome crash to get a crash report and dump, and
307 // SessionManager will terminate the session to logout.
308 CHECK_NE(GDK_GRAB_SUCCESS, kbd_grab_status_);
309 CHECK_NE(GDK_GRAB_SUCCESS, mouse_grab_status_);
310 }
311
312 void LockWindowGtk::OnClientEvent(GtkWidget* widge, GdkEventClient* event) {
313 WmIpc::Message msg;
314 WmIpc::instance()->DecodeMessage(*event, &msg);
315 if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK)
316 OnWindowManagerReady();
317 }
318
319 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/login/lock_window_gtk.h ('k') | chrome/browser/chromeos/login/screen_locker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698