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