1 // Copyright 2014 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/webui_screen_locker.h"
6
7 #include "ash/shell.h"
8 #include "ash/wm/lock_state_controller.h"
9 #include "ash/wm/lock_state_observer.h"
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/browser_shutdown.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
17 #include "chrome/browser/chromeos/login/helper.h"
18 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
19 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
20 #include "chrome/browser/chromeos/login/users/user.h"
21 #include "chrome/browser/chromeos/login/users/user_manager.h"
22 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
23 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
24 #include "chrome/common/url_constants.h"
25 #include "chromeos/dbus/dbus_thread_manager.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_widget_host_view.h"
30 #include "content/public/browser/web_ui.h"
31 #include "ui/aura/client/capture_client.h"
32 #include "ui/aura/window_event_dispatcher.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/x/x11_util.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/keyboard/keyboard_controller.h"
37 #include "ui/keyboard/keyboard_util.h"
38 #include "ui/views/controls/webview/webview.h"
39
40 namespace {
41
42 // URL which corresponds to the login WebUI.
43 const char kLoginURL[] = "chrome://oobe/lock";
44
45 // Disables virtual keyboard overscroll. Login UI will scroll user pods
46 // into view on JS side when virtual keyboard is shown.
DisableKeyboardOverscroll()47 void DisableKeyboardOverscroll() {
48 keyboard::SetKeyboardOverscrollOverride(
49 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
50 }
51
ResetKeyboardOverscrollOverride()52 void ResetKeyboardOverscrollOverride() {
53 keyboard::SetKeyboardOverscrollOverride(
54 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE);
55 }
56
57 } // namespace
58
59 namespace chromeos {
60
61 ////////////////////////////////////////////////////////////////////////////////
62 // WebUIScreenLocker implementation.
63
WebUIScreenLocker(ScreenLocker * screen_locker)64 WebUIScreenLocker::WebUIScreenLocker(ScreenLocker* screen_locker)
65 : ScreenLockerDelegate(screen_locker),
66 lock_ready_(false),
67 webui_ready_(false),
68 network_state_helper_(new login::NetworkStateHelper),
69 is_observing_keyboard_(false),
70 weak_factory_(this) {
71 set_should_emit_login_prompt_visible(false);
72 ash::Shell::GetInstance()->lock_state_controller()->AddObserver(this);
73 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
74
75 if (keyboard::KeyboardController::GetInstance()) {
76 keyboard::KeyboardController::GetInstance()->AddObserver(this);
77 is_observing_keyboard_ = true;
78 }
79
80 ash::Shell::GetInstance()->delegate()->AddVirtualKeyboardStateObserver(this);
81 }
82
LockScreen()83 void WebUIScreenLocker::LockScreen() {
84 gfx::Rect bounds(ash::Shell::GetScreen()->GetPrimaryDisplay().bounds());
85
86 lock_time_ = base::TimeTicks::Now();
87 LockWindow* lock_window = LockWindow::Create();
88 lock_window->set_observer(this);
89 lock_window_ = lock_window->GetWidget();
90 lock_window_->AddObserver(this);
91 WebUILoginView::Init();
92 lock_window_->SetContentsView(this);
93 lock_window_->Show();
94 LoadURL(GURL(kLoginURL));
95 lock_window->Grab();
96
97 login_display_.reset(new WebUILoginDisplay(this));
98 login_display_->set_background_bounds(bounds);
99 login_display_->set_parent_window(GetNativeWindow());
100 login_display_->Init(screen_locker()->users(), false, true, false);
101
102 GetOobeUI()->ShowSigninScreen(
103 LoginScreenContext(), login_display_.get(), login_display_.get());
104
105 registrar_.Add(this,
106 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
107 content::NotificationService::AllSources());
108
109 if (login::LoginScrollIntoViewEnabled())
110 DisableKeyboardOverscroll();
111 }
112
ScreenLockReady()113 void WebUIScreenLocker::ScreenLockReady() {
114 UMA_HISTOGRAM_TIMES("LockScreen.LockReady",
115 base::TimeTicks::Now() - lock_time_);
116 ScreenLockerDelegate::ScreenLockReady();
117 SetInputEnabled(true);
118 }
119
OnAuthenticate()120 void WebUIScreenLocker::OnAuthenticate() {
121 }
122
SetInputEnabled(bool enabled)123 void WebUIScreenLocker::SetInputEnabled(bool enabled) {
124 login_display_->SetUIEnabled(enabled);
125 }
126
ShowErrorMessage(int error_msg_id,HelpAppLauncher::HelpTopic help_topic_id)127 void WebUIScreenLocker::ShowErrorMessage(
128 int error_msg_id,
129 HelpAppLauncher::HelpTopic help_topic_id) {
130 login_display_->ShowError(error_msg_id,
131 0 /* login_attempts */,
132 help_topic_id);
133 }
134
AnimateAuthenticationSuccess()135 void WebUIScreenLocker::AnimateAuthenticationSuccess() {
136 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateAuthenticationSuccess");
137 }
138
ClearErrors()139 void WebUIScreenLocker::ClearErrors() {
140 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
141 }
142
GetNativeWindow() const143 gfx::NativeWindow WebUIScreenLocker::GetNativeWindow() const {
144 return lock_window_->GetNativeWindow();
145 }
146
GetAssociatedWebUI()147 content::WebUI* WebUIScreenLocker::GetAssociatedWebUI() {
148 return GetWebUI();
149 }
150
FocusUserPod()151 void WebUIScreenLocker::FocusUserPod() {
152 if (!webui_ready_)
153 return;
154 webui_login_->RequestFocus();
155 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.forceLockedUserPodFocus");
156 }
157
~WebUIScreenLocker()158 WebUIScreenLocker::~WebUIScreenLocker() {
159 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
160 ash::Shell::GetInstance()->
161 lock_state_controller()->RemoveObserver(this);
162 // In case of shutdown, lock_window_ may be deleted before WebUIScreenLocker.
163 if (lock_window_) {
164 lock_window_->RemoveObserver(this);
165 lock_window_->Close();
166 }
167 // If LockScreen() was called, we need to clear the signin screen handler
168 // delegate set in ShowSigninScreen so that it no longer points to us.
169 if (login_display_.get()) {
170 static_cast<OobeUI*>(GetWebUI()->GetController())->
171 ResetSigninScreenHandlerDelegate();
172 }
173
174 if (keyboard::KeyboardController::GetInstance() && is_observing_keyboard_) {
175 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
176 is_observing_keyboard_ = false;
177 }
178
179 ash::Shell::GetInstance()->delegate()->
180 RemoveVirtualKeyboardStateObserver(this);
181
182 if (login::LoginScrollIntoViewEnabled())
183 ResetKeyboardOverscrollOverride();
184 }
185
OnLockWebUIReady()186 void WebUIScreenLocker::OnLockWebUIReady() {
187 VLOG(1) << "WebUI ready; lock window is "
188 << (lock_ready_ ? "too" : "not");
189 webui_ready_ = true;
190 if (lock_ready_)
191 ScreenLockReady();
192 }
193
OnLockBackgroundDisplayed()194 void WebUIScreenLocker::OnLockBackgroundDisplayed() {
195 UMA_HISTOGRAM_TIMES("LockScreen.BackgroundReady",
196 base::TimeTicks::Now() - lock_time_);
197 }
198
GetOobeUI()199 OobeUI* WebUIScreenLocker::GetOobeUI() {
200 return static_cast<OobeUI*>(GetWebUI()->GetController());
201 }
202
203 ////////////////////////////////////////////////////////////////////////////////
204 // WebUIScreenLocker, content::NotificationObserver implementation:
205
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)206 void WebUIScreenLocker::Observe(
207 int type,
208 const content::NotificationSource& source,
209 const content::NotificationDetails& details) {
210 switch (type) {
211 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
212 const User& user = *content::Details<User>(details).ptr();
213 login_display_->OnUserImageChanged(user);
214 break;
215 }
216 default:
217 WebUILoginView::Observe(type, source, details);
218 }
219 }
220
221 ////////////////////////////////////////////////////////////////////////////////
222 // WebUIScreenLocker, LoginDisplay::Delegate implementation:
223
CancelPasswordChangedFlow()224 void WebUIScreenLocker::CancelPasswordChangedFlow() {
225 NOTREACHED();
226 }
227
CreateAccount()228 void WebUIScreenLocker::CreateAccount() {
229 NOTREACHED();
230 }
231
CompleteLogin(const UserContext & user_context)232 void WebUIScreenLocker::CompleteLogin(const UserContext& user_context) {
233 NOTREACHED();
234 }
235
GetConnectedNetworkName()236 base::string16 WebUIScreenLocker::GetConnectedNetworkName() {
237 return network_state_helper_->GetCurrentNetworkName();
238 }
239
IsSigninInProgress() const240 bool WebUIScreenLocker::IsSigninInProgress() const {
241 // The way how screen locker is implemented right now there's no
242 // GAIA sign in in progress in any case.
243 return false;
244 }
245
Login(const UserContext & user_context)246 void WebUIScreenLocker::Login(const UserContext& user_context) {
247 chromeos::ScreenLocker::default_screen_locker()->Authenticate(user_context);
248 }
249
LoginAsRetailModeUser()250 void WebUIScreenLocker::LoginAsRetailModeUser() {
251 NOTREACHED();
252 }
253
LoginAsGuest()254 void WebUIScreenLocker::LoginAsGuest() {
255 NOTREACHED();
256 }
257
MigrateUserData(const std::string & old_password)258 void WebUIScreenLocker::MigrateUserData(const std::string& old_password) {
259 NOTREACHED();
260 }
261
LoginAsPublicAccount(const std::string & username)262 void WebUIScreenLocker::LoginAsPublicAccount(const std::string& username) {
263 NOTREACHED();
264 }
265
OnSigninScreenReady()266 void WebUIScreenLocker::OnSigninScreenReady() {
267 }
268
OnUserSelected(const std::string & username)269 void WebUIScreenLocker::OnUserSelected(const std::string& username) {
270 }
271
OnStartEnterpriseEnrollment()272 void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
273 NOTREACHED();
274 }
275
OnStartKioskEnableScreen()276 void WebUIScreenLocker::OnStartKioskEnableScreen() {
277 NOTREACHED();
278 }
279
OnStartKioskAutolaunchScreen()280 void WebUIScreenLocker::OnStartKioskAutolaunchScreen() {
281 NOTREACHED();
282 }
283
ShowWrongHWIDScreen()284 void WebUIScreenLocker::ShowWrongHWIDScreen() {
285 NOTREACHED();
286 }
287
ResetPublicSessionAutoLoginTimer()288 void WebUIScreenLocker::ResetPublicSessionAutoLoginTimer() {
289 }
290
ResyncUserData()291 void WebUIScreenLocker::ResyncUserData() {
292 NOTREACHED();
293 }
294
SetDisplayEmail(const std::string & email)295 void WebUIScreenLocker::SetDisplayEmail(const std::string& email) {
296 NOTREACHED();
297 }
298
Signout()299 void WebUIScreenLocker::Signout() {
300 chromeos::ScreenLocker::default_screen_locker()->Signout();
301 }
302
LoginAsKioskApp(const std::string & app_id,bool diagnostic_mode)303 void WebUIScreenLocker::LoginAsKioskApp(const std::string& app_id,
304 bool diagnostic_mode) {
305 NOTREACHED();
306 }
307
308 ////////////////////////////////////////////////////////////////////////////////
309 // LockWindow::Observer implementation:
310
OnLockWindowReady()311 void WebUIScreenLocker::OnLockWindowReady() {
312 VLOG(1) << "Lock window ready; WebUI is " << (webui_ready_ ? "too" : "not");
313 lock_ready_ = true;
314 if (webui_ready_)
315 ScreenLockReady();
316 }
317
318 ////////////////////////////////////////////////////////////////////////////////
319 // SessionLockStateObserver override.
320
OnLockStateEvent(ash::LockStateObserver::EventType event)321 void WebUIScreenLocker::OnLockStateEvent(
322 ash::LockStateObserver::EventType event) {
323 if (event == ash::LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED) {
324 // Release capture if any.
325 aura::client::GetCaptureClient(GetNativeWindow()->GetRootWindow())->
326 SetCapture(NULL);
327 GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateOnceFullyDisplayed");
328 }
329 }
330
331 ////////////////////////////////////////////////////////////////////////////////
332 // WidgetObserver override.
333
OnWidgetDestroying(views::Widget * widget)334 void WebUIScreenLocker::OnWidgetDestroying(views::Widget* widget) {
335 lock_window_->RemoveObserver(this);
336 lock_window_ = NULL;
337 }
338
339 ////////////////////////////////////////////////////////////////////////////////
340 // PowerManagerClient::Observer overrides.
341
LidEventReceived(bool open,const base::TimeTicks & time)342 void WebUIScreenLocker::LidEventReceived(bool open,
343 const base::TimeTicks& time) {
344 content::BrowserThread::PostTask(
345 content::BrowserThread::UI,
346 FROM_HERE,
347 base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
348 }
349
SuspendDone(const base::TimeDelta & sleep_duration)350 void WebUIScreenLocker::SuspendDone(const base::TimeDelta& sleep_duration) {
351 content::BrowserThread::PostTask(
352 content::BrowserThread::UI,
353 FROM_HERE,
354 base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
355 }
356
RenderProcessGone(base::TerminationStatus status)357 void WebUIScreenLocker::RenderProcessGone(base::TerminationStatus status) {
358 if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID &&
359 status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
360 LOG(ERROR) << "Renderer crash on lock screen";
361 Signout();
362 }
363 }
364
365 ////////////////////////////////////////////////////////////////////////////////
366 // ash::KeyboardStateObserver overrides.
367
OnVirtualKeyboardStateChanged(bool activated)368 void WebUIScreenLocker::OnVirtualKeyboardStateChanged(bool activated) {
369 if (keyboard::KeyboardController::GetInstance()) {
370 if (activated) {
371 if (!is_observing_keyboard_) {
372 keyboard::KeyboardController::GetInstance()->AddObserver(this);
373 is_observing_keyboard_ = true;
374 }
375 } else {
376 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
377 is_observing_keyboard_ = false;
378 }
379 }
380 }
381
382 ////////////////////////////////////////////////////////////////////////////////
383 // keyboard::KeyboardControllerObserver overrides.
384
OnKeyboardBoundsChanging(const gfx::Rect & new_bounds)385 void WebUIScreenLocker::OnKeyboardBoundsChanging(
386 const gfx::Rect& new_bounds) {
387 if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty()) {
388 // Keyboard has been hidden.
389 if (GetOobeUI()) {
390 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(true);
391 if (login::LoginScrollIntoViewEnabled())
392 GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(false, new_bounds);
393 }
394 } else if (!new_bounds.IsEmpty() && keyboard_bounds_.IsEmpty()) {
395 // Keyboard has been shown.
396 if (GetOobeUI()) {
397 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(false);
398 if (login::LoginScrollIntoViewEnabled())
399 GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(true, new_bounds);
400 }
401 }
402
403 keyboard_bounds_ = new_bounds;
404 }
405
406 } // namespace chromeos
407