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
40namespace {
41
42// URL which corresponds to the login WebUI.
43const 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.
47void DisableKeyboardOverscroll() {
48  keyboard::SetKeyboardOverscrollOverride(
49      keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
50}
51
52void ResetKeyboardOverscrollOverride() {
53  keyboard::SetKeyboardOverscrollOverride(
54      keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE);
55}
56
57}  // namespace
58
59namespace chromeos {
60
61////////////////////////////////////////////////////////////////////////////////
62// WebUIScreenLocker implementation.
63
64WebUIScreenLocker::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
83void 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
113void WebUIScreenLocker::ScreenLockReady() {
114  UMA_HISTOGRAM_TIMES("LockScreen.LockReady",
115                      base::TimeTicks::Now() - lock_time_);
116  ScreenLockerDelegate::ScreenLockReady();
117  SetInputEnabled(true);
118}
119
120void WebUIScreenLocker::OnAuthenticate() {
121}
122
123void WebUIScreenLocker::SetInputEnabled(bool enabled) {
124  login_display_->SetUIEnabled(enabled);
125}
126
127void 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
135void WebUIScreenLocker::AnimateAuthenticationSuccess() {
136  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateAuthenticationSuccess");
137}
138
139void WebUIScreenLocker::ClearErrors() {
140  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
141}
142
143gfx::NativeWindow WebUIScreenLocker::GetNativeWindow() const {
144  return lock_window_->GetNativeWindow();
145}
146
147content::WebUI* WebUIScreenLocker::GetAssociatedWebUI() {
148  return GetWebUI();
149}
150
151void WebUIScreenLocker::FocusUserPod() {
152  if (!webui_ready_)
153    return;
154  webui_login_->RequestFocus();
155  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.forceLockedUserPodFocus");
156}
157
158WebUIScreenLocker::~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
186void 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
194void WebUIScreenLocker::OnLockBackgroundDisplayed() {
195  UMA_HISTOGRAM_TIMES("LockScreen.BackgroundReady",
196                      base::TimeTicks::Now() - lock_time_);
197}
198
199OobeUI* WebUIScreenLocker::GetOobeUI() {
200  return static_cast<OobeUI*>(GetWebUI()->GetController());
201}
202
203////////////////////////////////////////////////////////////////////////////////
204// WebUIScreenLocker, content::NotificationObserver implementation:
205
206void 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
224void WebUIScreenLocker::CancelPasswordChangedFlow()  {
225  NOTREACHED();
226}
227
228void WebUIScreenLocker::CreateAccount() {
229  NOTREACHED();
230}
231
232void WebUIScreenLocker::CompleteLogin(const UserContext& user_context) {
233  NOTREACHED();
234}
235
236base::string16 WebUIScreenLocker::GetConnectedNetworkName() {
237  return network_state_helper_->GetCurrentNetworkName();
238}
239
240bool 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
246void WebUIScreenLocker::Login(const UserContext& user_context) {
247  chromeos::ScreenLocker::default_screen_locker()->Authenticate(user_context);
248}
249
250void WebUIScreenLocker::LoginAsRetailModeUser() {
251  NOTREACHED();
252}
253
254void WebUIScreenLocker::LoginAsGuest() {
255  NOTREACHED();
256}
257
258void WebUIScreenLocker::MigrateUserData(const std::string& old_password) {
259  NOTREACHED();
260}
261
262void WebUIScreenLocker::LoginAsPublicAccount(const std::string& username) {
263  NOTREACHED();
264}
265
266void WebUIScreenLocker::OnSigninScreenReady() {
267}
268
269void WebUIScreenLocker::OnUserSelected(const std::string& username) {
270}
271
272void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
273  NOTREACHED();
274}
275
276void WebUIScreenLocker::OnStartKioskEnableScreen() {
277  NOTREACHED();
278}
279
280void WebUIScreenLocker::OnStartKioskAutolaunchScreen() {
281  NOTREACHED();
282}
283
284void WebUIScreenLocker::ShowWrongHWIDScreen() {
285  NOTREACHED();
286}
287
288void WebUIScreenLocker::ResetPublicSessionAutoLoginTimer() {
289}
290
291void WebUIScreenLocker::ResyncUserData() {
292  NOTREACHED();
293}
294
295void WebUIScreenLocker::SetDisplayEmail(const std::string& email) {
296  NOTREACHED();
297}
298
299void WebUIScreenLocker::Signout() {
300  chromeos::ScreenLocker::default_screen_locker()->Signout();
301}
302
303void WebUIScreenLocker::LoginAsKioskApp(const std::string& app_id,
304                                        bool diagnostic_mode) {
305  NOTREACHED();
306}
307
308////////////////////////////////////////////////////////////////////////////////
309// LockWindow::Observer implementation:
310
311void 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
321void 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
334void WebUIScreenLocker::OnWidgetDestroying(views::Widget* widget) {
335  lock_window_->RemoveObserver(this);
336  lock_window_ = NULL;
337}
338
339////////////////////////////////////////////////////////////////////////////////
340// PowerManagerClient::Observer overrides.
341
342void 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
350void 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
357void 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
368void 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
385void 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