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

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

Issue 8711003: Enable WebUI ScreenLocker for aura by abstracting lock window. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reviewer comments. Created 9 years 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/chromeos/login/webui_screen_locker.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/login/webui_screen_locker.h" 5 #include "chrome/browser/chromeos/login/webui_screen_locker.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/utf_string_conversions.h" 8 #include "base/utf_string_conversions.h"
9 #include "base/values.h" 9 #include "base/values.h"
10 #include "chrome/browser/chromeos/cros/cros_library.h" 10 #include "chrome/browser/chromeos/cros/cros_library.h"
11 #include "chrome/browser/chromeos/cros/network_library.h" 11 #include "chrome/browser/chromeos/cros/network_library.h"
12 #include "chrome/browser/chromeos/legacy_window_manager/wm_ipc.h"
13 #include "chrome/browser/chromeos/login/helper.h" 12 #include "chrome/browser/chromeos/login/helper.h"
14 #include "chrome/browser/chromeos/login/screen_locker.h" 13 #include "chrome/browser/chromeos/login/screen_locker.h"
15 #include "chrome/browser/chromeos/login/user_manager.h" 14 #include "chrome/browser/chromeos/login/user_manager.h"
16 #include "chrome/browser/chromeos/login/webui_login_display.h" 15 #include "chrome/browser/chromeos/login/webui_login_display.h"
17 #include "chrome/browser/ui/views/dom_view.h" 16 #include "chrome/browser/ui/views/dom_view.h"
18 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 17 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
19 #include "chrome/common/chrome_notification_types.h" 18 #include "chrome/common/chrome_notification_types.h"
20 #include "chrome/common/url_constants.h" 19 #include "chrome/common/url_constants.h"
21 #include "content/browser/renderer_host/render_widget_host_view.h" 20 #include "content/browser/renderer_host/render_widget_host_view.h"
22 #include "content/public/browser/notification_service.h" 21 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_types.h" 22 #include "content/public/browser/notification_types.h"
24 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/x/x11_util.h" 24 #include "ui/base/x/x11_util.h"
26 #include "ui/gfx/screen.h" 25 #include "ui/gfx/screen.h"
27 #include "ui/views/widget/native_widget_gtk.h"
28
29 // These conflict with some of the Chrome headers, so must be included last.
30 #include <X11/extensions/XTest.h>
31 #include <X11/keysym.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gdk/gdkx.h>
34 26
35 namespace { 27 namespace {
36 28
37 // URL which corresponds to the login WebUI. 29 // URL which corresponds to the login WebUI.
38 const char kLoginURL[] = "chrome://oobe/login"; 30 const char kLoginURL[] = "chrome://oobe/login";
39 31
40 // The maximum duration for which locker should try to grab the keyboard and
41 // mouse and its interval for regrabbing on failure.
42 const int kMaxGrabFailureSec = 30;
43 const int64 kRetryGrabIntervalMs = 500;
44
45 // Maximum number of times we'll try to grab the keyboard and mouse before
46 // giving up. If we hit the limit, Chrome exits and the session is terminated.
47 const int kMaxGrabFailures = kMaxGrabFailureSec * 1000 / kRetryGrabIntervalMs;
48
49 // A ScreenLock window that covers entire screen to keep the keyboard
50 // focus/events inside the grab widget.
51 class LockWindow : public views::NativeWidgetGtk {
52 public:
53 explicit LockWindow(chromeos::WebUIScreenLocker* webui_screen_locker)
54 : views::NativeWidgetGtk(new views::Widget),
55 toplevel_focus_widget_(NULL),
56 webui_screen_locker_(webui_screen_locker) {
57 EnableDoubleBuffer(true);
58 }
59
60 virtual gboolean OnButtonPress(GtkWidget* widget,
61 GdkEventButton* event) OVERRIDE {
62 // Never propagate mouse events to parent.
63 return true;
64 }
65
66 virtual void OnDestroy(GtkWidget* object) OVERRIDE {
67 VLOG(1) << "OnDestroy: LockWindow destroyed";
68 views::NativeWidgetGtk::OnDestroy(object);
69 }
70
71 virtual void ClearNativeFocus() OVERRIDE {
72 DCHECK(toplevel_focus_widget_);
73 gtk_widget_grab_focus(toplevel_focus_widget_);
74 }
75
76 virtual void HandleGtkGrabBroke() OVERRIDE {
77 webui_screen_locker_->HandleGtkGrabBroke();
78 }
79
80 // Sets the widget to move the focus to when clearning the native
81 // widget's focus.
82 void set_toplevel_focus_widget(GtkWidget* widget) {
83 gtk_widget_set_can_focus(widget, TRUE);
84 toplevel_focus_widget_ = widget;
85 }
86
87 private:
88 // The widget we set focus to when clearning the focus on native
89 // widget. Gdk input is grabbed in WebUIScreenLocker, and resetting the focus
90 // by using gtk_window_set_focus seems to confuse gtk and doesn't let focus
91 // move to native widget under this.
92 GtkWidget* toplevel_focus_widget_;
93
94 // The WebUI screen locker.
95 chromeos::WebUIScreenLocker* webui_screen_locker_;
96
97 DISALLOW_COPY_AND_ASSIGN(LockWindow);
98 };
99
100 // Define separate methods for each error code so that stack trace
101 // will tell which error the grab failed with.
102 void FailedWithGrabAlreadyGrabbed() {
103 LOG(FATAL) << "Grab already grabbed";
104 }
105 void FailedWithGrabInvalidTime() {
106 LOG(FATAL) << "Grab invalid time";
107 }
108 void FailedWithGrabNotViewable() {
109 LOG(FATAL) << "Grab not viewable";
110 }
111 void FailedWithGrabFrozen() {
112 LOG(FATAL) << "Grab frozen";
113 }
114 void FailedWithUnknownError() {
115 LOG(FATAL) << "Grab uknown";
116 }
117
118 } // namespace 32 } // namespace
119 33
120 namespace chromeos { 34 namespace chromeos {
121 35
122 //////////////////////////////////////////////////////////////////////////////// 36 ////////////////////////////////////////////////////////////////////////////////
123 // WebUIScreenLocker implementation. 37 // WebUIScreenLocker implementation.
124 38
125 WebUIScreenLocker::WebUIScreenLocker(ScreenLocker* screen_locker) 39 WebUIScreenLocker::WebUIScreenLocker(ScreenLocker* screen_locker)
126 : ScreenLockerDelegate(screen_locker), 40 : ScreenLockerDelegate(screen_locker),
127 drawn_(false), 41 lock_ready_(false),
128 input_grabbed_(false), 42 webui_ready_(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 } 43 }
134 44
135 void WebUIScreenLocker::LockScreen(bool unlock_on_input) { 45 void WebUIScreenLocker::LockScreen(bool unlock_on_input) {
136 static const GdkColor kGdkBlack = {0, 0, 0, 0};
137
138 gfx::Rect bounds(gfx::Screen::GetMonitorAreaNearestWindow(NULL)); 46 gfx::Rect bounds(gfx::Screen::GetMonitorAreaNearestWindow(NULL));
139 47
140 LockWindow* lock_window = new LockWindow(this); 48 LockWindow* lock_window = LockWindow::Create();
49 lock_window->set_observer(this);
141 lock_window_ = lock_window->GetWidget(); 50 lock_window_ = lock_window->GetWidget();
142 views::Widget::InitParams params(
143 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
144 params.bounds = bounds;
145 params.native_widget = lock_window;
146 lock_window_->Init(params);
147 gtk_widget_modify_bg(
148 lock_window_->GetNativeView(), GTK_STATE_NORMAL, &kGdkBlack);
149
150 g_signal_connect(lock_window_->GetNativeView(), "client-event",
151 G_CALLBACK(OnClientEventThunk), this);
152
153 // TODO(flackr): Obscure the screen with black / a screensaver if
154 // unlock_on_input.
155 DCHECK(GTK_WIDGET_REALIZED(lock_window_->GetNativeView()));
156 WmIpc::instance()->SetWindowType(
157 lock_window_->GetNativeView(),
158 WM_IPC_WINDOW_CHROME_SCREEN_LOCKER,
159 NULL);
160
161 WebUILoginView::Init(lock_window_); 51 WebUILoginView::Init(lock_window_);
162 lock_window_->SetContentsView(this); 52 lock_window_->SetContentsView(this);
163 lock_window_->Show(); 53 lock_window_->Show();
164 OnWindowCreated(); 54 OnWindowCreated();
165 LoadURL(GURL(kLoginURL)); 55 LoadURL(GURL(kLoginURL));
56 lock_window->Grab(webui_login_);
57
166 // User list consisting of a single logged-in user. 58 // User list consisting of a single logged-in user.
167 UserList users(1, &chromeos::UserManager::Get()->logged_in_user()); 59 UserList users(1, &chromeos::UserManager::Get()->logged_in_user());
168 login_display_.reset(new WebUILoginDisplay(this)); 60 login_display_.reset(new WebUILoginDisplay(this));
169 login_display_->set_background_bounds(bounds); 61 login_display_->set_background_bounds(bounds);
170 login_display_->set_parent_window(
171 GTK_WINDOW(lock_window_->GetNativeView()));
172 login_display_->Init(users, false, false); 62 login_display_->Init(users, false, false);
173 63
174 static_cast<OobeUI*>(GetWebUI())->ShowSigninScreen(login_display_.get()); 64 static_cast<OobeUI*>(GetWebUI())->ShowSigninScreen(login_display_.get());
175 65
176 registrar_.Add(this, 66 registrar_.Add(this,
177 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 67 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
178 content::NotificationService::AllSources()); 68 content::NotificationService::AllSources());
179
180 ClearGtkGrab();
181
182 // Call this after lock_window_->Show(); otherwise the 1st invocation
183 // of gdk_xxx_grab() will always fail.
184 TryGrabAllInputs();
185
186 // Add the window to its own group so that its grab won't be stolen if
187 // gtk_grab_add() gets called on behalf on a non-screen-locker widget (e.g.
188 // a modal dialog) -- see http://crosbug.com/8999. We intentionally do this
189 // after calling ClearGtkGrab(), as want to be in the default window group
190 // then so we can break any existing GTK grabs.
191 GtkWindowGroup* window_group = gtk_window_group_new();
192 gtk_window_group_add_window(window_group,
193 GTK_WINDOW(lock_window_->GetNativeView()));
194 g_object_unref(window_group);
195
196 lock_window->set_toplevel_focus_widget(lock_window->window_contents());
197 }
198
199 void WebUIScreenLocker::OnGrabInputs() {
200 DVLOG(1) << "OnGrabInputs";
201 input_grabbed_ = true;
202 if (drawn_)
203 ScreenLockReady();
204 }
205
206 void WebUIScreenLocker::OnWindowManagerReady() {
207 DVLOG(1) << "OnClientEvent: drawn for lock";
208 drawn_ = true;
209 if (input_grabbed_)
210 ScreenLockReady();
211 } 69 }
212 70
213 void WebUIScreenLocker::ScreenLockReady() { 71 void WebUIScreenLocker::ScreenLockReady() {
214 ScreenLockerDelegate::ScreenLockReady(); 72 ScreenLockerDelegate::ScreenLockReady();
215 SetInputEnabled(true); 73 SetInputEnabled(true);
216 } 74 }
217 75
218 void WebUIScreenLocker::ClearGtkGrab() {
219 GtkWidget* current_grab_window;
220 // Grab gtk input first so that the menu holding gtk grab will
221 // close itself.
222 gtk_grab_add(webui_login_->native_view());
223
224 // Make sure there is no gtk grab widget so that gtk simply propagates
225 // an event. GTK maintains grab widgets in a linked-list, so we need to
226 // remove until it's empty.
227 while ((current_grab_window = gtk_grab_get_current()) != NULL)
228 gtk_grab_remove(current_grab_window);
229 }
230
231 void WebUIScreenLocker::TryGrabAllInputs() {
232 // Grab on the RenderWidgetHostView hosting the WebUI login screen.
233 GdkWindow* grab_widget = webui_login_->dom_contents()->tab_contents()->
234 GetRenderWidgetHostView()->GetNativeView()->window;
235 // Grab x server so that we can atomically grab and take
236 // action when grab fails.
237 gdk_x11_grab_server();
238 gtk_grab_add(webui_login_->native_view());
239 if (kbd_grab_status_ != GDK_GRAB_SUCCESS)
240 kbd_grab_status_ = gdk_keyboard_grab(grab_widget, FALSE, GDK_CURRENT_TIME);
241 if (mouse_grab_status_ != GDK_GRAB_SUCCESS) {
242 mouse_grab_status_ =
243 gdk_pointer_grab(grab_widget,
244 FALSE,
245 static_cast<GdkEventMask>(
246 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
247 GDK_POINTER_MOTION_MASK),
248 NULL,
249 NULL,
250 GDK_CURRENT_TIME);
251 }
252 if ((kbd_grab_status_ != GDK_GRAB_SUCCESS ||
253 mouse_grab_status_ != GDK_GRAB_SUCCESS) &&
254 grab_failure_count_++ < kMaxGrabFailures) {
255 LOG(WARNING) << "Failed to grab inputs. Trying again in "
256 << kRetryGrabIntervalMs << " ms: kbd="
257 << kbd_grab_status_ << ", mouse=" << mouse_grab_status_;
258 TryUngrabOtherClients();
259 gdk_x11_ungrab_server();
260 MessageLoop::current()->PostDelayedTask(
261 FROM_HERE,
262 base::Bind(&WebUIScreenLocker::TryGrabAllInputs,
263 weak_factory_.GetWeakPtr()),
264 kRetryGrabIntervalMs);
265 } else {
266 gdk_x11_ungrab_server();
267 GdkGrabStatus status = kbd_grab_status_;
268 if (status == GDK_GRAB_SUCCESS) {
269 status = mouse_grab_status_;
270 }
271 switch (status) {
272 case GDK_GRAB_SUCCESS:
273 break;
274 case GDK_GRAB_ALREADY_GRABBED:
275 FailedWithGrabAlreadyGrabbed();
276 break;
277 case GDK_GRAB_INVALID_TIME:
278 FailedWithGrabInvalidTime();
279 break;
280 case GDK_GRAB_NOT_VIEWABLE:
281 FailedWithGrabNotViewable();
282 break;
283 case GDK_GRAB_FROZEN:
284 FailedWithGrabFrozen();
285 break;
286 default:
287 FailedWithUnknownError();
288 break;
289 }
290 DVLOG(1) << "Grab Success";
291 OnGrabInputs();
292 }
293 }
294
295 void WebUIScreenLocker::TryUngrabOtherClients() {
296 #if !defined(NDEBUG)
297 {
298 int event_base, error_base;
299 int major, minor;
300 // Make sure we have XTest extension.
301 DCHECK(XTestQueryExtension(ui::GetXDisplay(),
302 &event_base, &error_base,
303 &major, &minor));
304 }
305 #endif
306
307 // The following code is an attempt to grab inputs by closing
308 // supposedly opened menu. This happens when a plugin has a menu
309 // opened.
310 if (mouse_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
311 mouse_grab_status_ == GDK_GRAB_FROZEN) {
312 // Successfully grabbed the keyboard, but pointer is still
313 // grabbed by other client. Another attempt to close supposedly
314 // opened menu by emulating keypress at the left top corner.
315 Display* display = ui::GetXDisplay();
316 Window root, child;
317 int root_x, root_y, win_x, winy;
318 unsigned int mask;
319 XQueryPointer(display,
320 ui::GetX11WindowFromGtkWidget(
321 static_cast<LockWindow*>(lock_window_->native_widget())->
322 window_contents()),
323 &root, &child, &root_x, &root_y,
324 &win_x, &winy, &mask);
325 XTestFakeMotionEvent(display, -1, -10000, -10000, CurrentTime);
326 XTestFakeButtonEvent(display, 1, True, CurrentTime);
327 XTestFakeButtonEvent(display, 1, False, CurrentTime);
328 // Move the pointer back.
329 XTestFakeMotionEvent(display, -1, root_x, root_y, CurrentTime);
330 XFlush(display);
331 } else if (kbd_grab_status_ == GDK_GRAB_ALREADY_GRABBED ||
332 kbd_grab_status_ == GDK_GRAB_FROZEN) {
333 // Successfully grabbed the pointer, but keyboard is still grabbed
334 // by other client. Another attempt to close supposedly opened
335 // menu by emulating escape key. Such situation must be very
336 // rare, but handling this just in case
337 Display* display = ui::GetXDisplay();
338 KeyCode escape = XKeysymToKeycode(display, XK_Escape);
339 XTestFakeKeyEvent(display, escape, True, CurrentTime);
340 XTestFakeKeyEvent(display, escape, False, CurrentTime);
341 XFlush(display);
342 }
343 }
344
345 void WebUIScreenLocker::HandleGtkGrabBroke() {
346 // Input should never be stolen from ScreenLocker once it's
347 // grabbed. If this happens, it's a bug and has to be fixed. We
348 // let chrome crash to get a crash report and dump, and
349 // SessionManager will terminate the session to logout.
350 CHECK_NE(GDK_GRAB_SUCCESS, kbd_grab_status_);
351 CHECK_NE(GDK_GRAB_SUCCESS, mouse_grab_status_);
352 }
353
354 void WebUIScreenLocker::OnAuthenticate() { 76 void WebUIScreenLocker::OnAuthenticate() {
355 } 77 }
356 78
357 void WebUIScreenLocker::SetInputEnabled(bool enabled) { 79 void WebUIScreenLocker::SetInputEnabled(bool enabled) {
358 login_display_->SetUIEnabled(enabled); 80 login_display_->SetUIEnabled(enabled);
359 SetStatusAreaEnabled(enabled); 81 SetStatusAreaEnabled(enabled);
360 } 82 }
361 83
362 void WebUIScreenLocker::SetSignoutEnabled(bool enabled) { 84 void WebUIScreenLocker::SetSignoutEnabled(bool enabled) {
363 // TODO(flackr): Implement (crbug.com/105267). 85 // TODO(flackr): Implement (crbug.com/105267).
(...skipping 23 matching lines...) Expand all
387 109
388 void WebUIScreenLocker::ClearErrors() { 110 void WebUIScreenLocker::ClearErrors() {
389 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors"); 111 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
390 } 112 }
391 113
392 WebUIScreenLocker::~WebUIScreenLocker() { 114 WebUIScreenLocker::~WebUIScreenLocker() {
393 DCHECK(lock_window_); 115 DCHECK(lock_window_);
394 lock_window_->Close(); 116 lock_window_->Close();
395 } 117 }
396 118
397 void WebUIScreenLocker::OnClientEvent(GtkWidget* widge, GdkEventClient* event) {
398 WmIpc::Message msg;
399 WmIpc::instance()->DecodeMessage(*event, &msg);
400 if (msg.type() == WM_IPC_MESSAGE_CHROME_NOTIFY_SCREEN_REDRAWN_FOR_LOCK)
401 OnWindowManagerReady();
402 }
403
404 //////////////////////////////////////////////////////////////////////////////// 119 ////////////////////////////////////////////////////////////////////////////////
405 // WebUIScreenLocker, content::NotificationObserver implementation: 120 // WebUIScreenLocker, content::NotificationObserver implementation:
406 121
407 void WebUIScreenLocker::Observe( 122 void WebUIScreenLocker::Observe(
408 int type, 123 int type,
409 const content::NotificationSource& source, 124 const content::NotificationSource& source,
410 const content::NotificationDetails& details) { 125 const content::NotificationDetails& details) {
411 if (type != chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED) 126 if (type != chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED)
412 return; 127 return;
413 128
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 NOTREACHED(); 162 NOTREACHED();
448 } 163 }
449 164
450 void WebUIScreenLocker::OnUserSelected(const std::string& username) { 165 void WebUIScreenLocker::OnUserSelected(const std::string& username) {
451 } 166 }
452 167
453 void WebUIScreenLocker::OnStartEnterpriseEnrollment() { 168 void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
454 NOTREACHED(); 169 NOTREACHED();
455 } 170 }
456 171
172 ////////////////////////////////////////////////////////////////////////////////
173 // LockWindow::Observer implementation:
174
175 void WebUIScreenLocker::OnLockWindowReady() {
176 lock_ready_ = true;
177 if (webui_ready_)
178 ScreenLockReady();
179 }
180
181 ////////////////////////////////////////////////////////////////////////////////
182 // Overridden from WebUILoginView:
183
184 void WebUIScreenLocker::OnTabMainFrameFirstRender() {
185 WebUILoginView::OnTabMainFrameFirstRender();
186 webui_ready_ = true;
187 if (lock_ready_)
188 ScreenLockReady();
189 }
190
457 } // namespace chromeos 191 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/login/webui_screen_locker.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698