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