webui_screen_locker.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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->set_initially_focused_view(this);
90  lock_window_ = lock_window->GetWidget();
91  lock_window_->AddObserver(this);
92  WebUILoginView::Init();
93  lock_window_->SetContentsView(this);
94  lock_window_->Show();
95  LoadURL(GURL(kLoginURL));
96  lock_window->Grab();
97
98  login_display_.reset(new WebUILoginDisplay(this));
99  login_display_->set_background_bounds(bounds);
100  login_display_->set_parent_window(GetNativeWindow());
101  login_display_->Init(screen_locker()->users(), false, true, false);
102
103  GetOobeUI()->ShowSigninScreen(
104      LoginScreenContext(), login_display_.get(), login_display_.get());
105
106  registrar_.Add(this,
107                 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
108                 content::NotificationService::AllSources());
109
110  if (login::LoginScrollIntoViewEnabled())
111    DisableKeyboardOverscroll();
112}
113
114void WebUIScreenLocker::ScreenLockReady() {
115  UMA_HISTOGRAM_TIMES("LockScreen.LockReady",
116                      base::TimeTicks::Now() - lock_time_);
117  ScreenLockerDelegate::ScreenLockReady();
118  SetInputEnabled(true);
119}
120
121void WebUIScreenLocker::OnAuthenticate() {
122}
123
124void WebUIScreenLocker::SetInputEnabled(bool enabled) {
125  login_display_->SetUIEnabled(enabled);
126}
127
128void WebUIScreenLocker::ShowErrorMessage(
129    int error_msg_id,
130    HelpAppLauncher::HelpTopic help_topic_id) {
131  login_display_->ShowError(error_msg_id,
132                  0 /* login_attempts */,
133                  help_topic_id);
134}
135
136void WebUIScreenLocker::AnimateAuthenticationSuccess() {
137  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateAuthenticationSuccess");
138}
139
140void WebUIScreenLocker::ClearErrors() {
141  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
142}
143
144gfx::NativeWindow WebUIScreenLocker::GetNativeWindow() const {
145  return lock_window_->GetNativeWindow();
146}
147
148content::WebUI* WebUIScreenLocker::GetAssociatedWebUI() {
149  return GetWebUI();
150}
151
152void WebUIScreenLocker::FocusUserPod() {
153  if (!webui_ready_)
154    return;
155  webui_login_->RequestFocus();
156  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.forceLockedUserPodFocus");
157}
158
159WebUIScreenLocker::~WebUIScreenLocker() {
160  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
161  ash::Shell::GetInstance()->
162      lock_state_controller()->RemoveObserver(this);
163  // In case of shutdown, lock_window_ may be deleted before WebUIScreenLocker.
164  if (lock_window_) {
165    lock_window_->RemoveObserver(this);
166    lock_window_->Close();
167  }
168  // If LockScreen() was called, we need to clear the signin screen handler
169  // delegate set in ShowSigninScreen so that it no longer points to us.
170  if (login_display_.get()) {
171    static_cast<OobeUI*>(GetWebUI()->GetController())->
172        ResetSigninScreenHandlerDelegate();
173  }
174
175  if (keyboard::KeyboardController::GetInstance() && is_observing_keyboard_) {
176    keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
177    is_observing_keyboard_ = false;
178  }
179
180  ash::Shell::GetInstance()->delegate()->
181      RemoveVirtualKeyboardStateObserver(this);
182
183  if (login::LoginScrollIntoViewEnabled())
184    ResetKeyboardOverscrollOverride();
185}
186
187void WebUIScreenLocker::OnLockWebUIReady() {
188  VLOG(1) << "WebUI ready; lock window is "
189          << (lock_ready_ ? "too" : "not");
190  webui_ready_ = true;
191  if (lock_ready_)
192    ScreenLockReady();
193}
194
195void WebUIScreenLocker::OnLockBackgroundDisplayed() {
196  UMA_HISTOGRAM_TIMES("LockScreen.BackgroundReady",
197                      base::TimeTicks::Now() - lock_time_);
198}
199
200OobeUI* WebUIScreenLocker::GetOobeUI() {
201  return static_cast<OobeUI*>(GetWebUI()->GetController());
202}
203
204////////////////////////////////////////////////////////////////////////////////
205// WebUIScreenLocker, content::NotificationObserver implementation:
206
207void WebUIScreenLocker::Observe(
208    int type,
209    const content::NotificationSource& source,
210    const content::NotificationDetails& details) {
211  switch (type) {
212    case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
213      const User& user = *content::Details<User>(details).ptr();
214      login_display_->OnUserImageChanged(user);
215      break;
216    }
217    default:
218      WebUILoginView::Observe(type, source, details);
219  }
220}
221
222////////////////////////////////////////////////////////////////////////////////
223// WebUIScreenLocker, LoginDisplay::Delegate implementation:
224
225void WebUIScreenLocker::CancelPasswordChangedFlow()  {
226  NOTREACHED();
227}
228
229void WebUIScreenLocker::CreateAccount() {
230  NOTREACHED();
231}
232
233void WebUIScreenLocker::CompleteLogin(const UserContext& user_context) {
234  NOTREACHED();
235}
236
237base::string16 WebUIScreenLocker::GetConnectedNetworkName() {
238  return network_state_helper_->GetCurrentNetworkName();
239}
240
241bool WebUIScreenLocker::IsSigninInProgress() const {
242  // The way how screen locker is implemented right now there's no
243  // GAIA sign in in progress in any case.
244  return false;
245}
246
247void WebUIScreenLocker::Login(const UserContext& user_context,
248                              const SigninSpecifics& specifics) {
249  chromeos::ScreenLocker::default_screen_locker()->Authenticate(user_context);
250}
251
252void WebUIScreenLocker::MigrateUserData(const std::string& old_password) {
253  NOTREACHED();
254}
255
256void WebUIScreenLocker::OnSigninScreenReady() {
257}
258
259void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
260  NOTREACHED();
261}
262
263void WebUIScreenLocker::OnStartKioskEnableScreen() {
264  NOTREACHED();
265}
266
267void WebUIScreenLocker::OnStartKioskAutolaunchScreen() {
268  NOTREACHED();
269}
270
271void WebUIScreenLocker::ShowWrongHWIDScreen() {
272  NOTREACHED();
273}
274
275void WebUIScreenLocker::ResetPublicSessionAutoLoginTimer() {
276}
277
278void WebUIScreenLocker::ResyncUserData() {
279  NOTREACHED();
280}
281
282void WebUIScreenLocker::SetDisplayEmail(const std::string& email) {
283  NOTREACHED();
284}
285
286void WebUIScreenLocker::Signout() {
287  chromeos::ScreenLocker::default_screen_locker()->Signout();
288}
289
290////////////////////////////////////////////////////////////////////////////////
291// LockWindow::Observer implementation:
292
293void WebUIScreenLocker::OnLockWindowReady() {
294  VLOG(1) << "Lock window ready; WebUI is " << (webui_ready_ ? "too" : "not");
295  lock_ready_ = true;
296  if (webui_ready_)
297    ScreenLockReady();
298}
299
300////////////////////////////////////////////////////////////////////////////////
301// SessionLockStateObserver override.
302
303void WebUIScreenLocker::OnLockStateEvent(
304    ash::LockStateObserver::EventType event) {
305  if (event == ash::LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED) {
306    // Release capture if any.
307    aura::client::GetCaptureClient(GetNativeWindow()->GetRootWindow())->
308        SetCapture(NULL);
309    GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateOnceFullyDisplayed");
310  }
311}
312
313////////////////////////////////////////////////////////////////////////////////
314// WidgetObserver override.
315
316void WebUIScreenLocker::OnWidgetDestroying(views::Widget* widget) {
317  lock_window_->RemoveObserver(this);
318  lock_window_ = NULL;
319}
320
321////////////////////////////////////////////////////////////////////////////////
322// PowerManagerClient::Observer overrides.
323
324void WebUIScreenLocker::LidEventReceived(bool open,
325                                         const base::TimeTicks& time) {
326  content::BrowserThread::PostTask(
327      content::BrowserThread::UI,
328      FROM_HERE,
329      base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
330}
331
332void WebUIScreenLocker::SuspendDone(const base::TimeDelta& sleep_duration) {
333  content::BrowserThread::PostTask(
334      content::BrowserThread::UI,
335      FROM_HERE,
336      base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
337}
338
339void WebUIScreenLocker::RenderProcessGone(base::TerminationStatus status) {
340  if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID &&
341      status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
342    LOG(ERROR) << "Renderer crash on lock screen";
343    Signout();
344  }
345}
346
347////////////////////////////////////////////////////////////////////////////////
348// ash::KeyboardStateObserver overrides.
349
350void WebUIScreenLocker::OnVirtualKeyboardStateChanged(bool activated) {
351  if (keyboard::KeyboardController::GetInstance()) {
352    if (activated) {
353      if (!is_observing_keyboard_) {
354        keyboard::KeyboardController::GetInstance()->AddObserver(this);
355        is_observing_keyboard_ = true;
356      }
357    } else {
358      keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
359      is_observing_keyboard_ = false;
360    }
361  }
362}
363
364////////////////////////////////////////////////////////////////////////////////
365// keyboard::KeyboardControllerObserver overrides.
366
367void WebUIScreenLocker::OnKeyboardBoundsChanging(
368    const gfx::Rect& new_bounds) {
369  if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty()) {
370    // Keyboard has been hidden.
371    if (GetOobeUI()) {
372      GetOobeUI()->GetCoreOobeActor()->ShowControlBar(true);
373      if (login::LoginScrollIntoViewEnabled())
374        GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(false, new_bounds);
375    }
376  } else if (!new_bounds.IsEmpty() && keyboard_bounds_.IsEmpty()) {
377    // Keyboard has been shown.
378    if (GetOobeUI()) {
379      GetOobeUI()->GetCoreOobeActor()->ShowControlBar(false);
380      if (login::LoginScrollIntoViewEnabled())
381        GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(true, new_bounds);
382    }
383  }
384
385  keyboard_bounds_ = new_bounds;
386}
387
388}  // namespace chromeos
389