1// Copyright (c) 2012 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/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/screen_locker.h"
19#include "chrome/browser/chromeos/login/user_manager.h"
20#include "chrome/browser/chromeos/login/webui_login_display.h"
21#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
22#include "chrome/common/url_constants.h"
23#include "chromeos/dbus/dbus_thread_manager.h"
24#include "content/public/browser/browser_thread.h"
25#include "content/public/browser/notification_service.h"
26#include "content/public/browser/notification_types.h"
27#include "content/public/browser/render_widget_host_view.h"
28#include "content/public/browser/web_ui.h"
29#include "ui/aura/client/capture_client.h"
30#include "ui/base/l10n/l10n_util.h"
31#include "ui/base/x/x11_util.h"
32#include "ui/gfx/screen.h"
33#include "ui/views/controls/webview/webview.h"
34
35namespace {
36
37// URL which corresponds to the login WebUI.
38const char kLoginURL[] = "chrome://oobe/login#lock";
39
40}  // namespace
41
42namespace chromeos {
43
44////////////////////////////////////////////////////////////////////////////////
45// WebUIScreenLocker implementation.
46
47WebUIScreenLocker::WebUIScreenLocker(ScreenLocker* screen_locker)
48    : ScreenLockerDelegate(screen_locker),
49      lock_ready_(false),
50      webui_ready_(false),
51      network_state_helper_(new login::NetworkStateHelper),
52      weak_factory_(this) {
53  set_should_emit_login_prompt_visible(false);
54  ash::Shell::GetInstance()->lock_state_controller()->AddObserver(this);
55  DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
56}
57
58void WebUIScreenLocker::LockScreen() {
59  gfx::Rect bounds(ash::Shell::GetScreen()->GetPrimaryDisplay().bounds());
60
61  lock_time_ = base::TimeTicks::Now();
62  LockWindow* lock_window = LockWindow::Create();
63  lock_window->set_observer(this);
64  lock_window_ = lock_window->GetWidget();
65  lock_window_->AddObserver(this);
66  WebUILoginView::Init(lock_window_);
67  lock_window_->SetContentsView(this);
68  lock_window_->Show();
69  OnWindowCreated();
70  LoadURL(GURL(kLoginURL));
71  lock_window->Grab();
72
73  // Subscribe to crash events.
74  content::WebContentsObserver::Observe(GetWebContents());
75
76  login_display_.reset(new WebUILoginDisplay(this));
77  login_display_->set_background_bounds(bounds);
78  login_display_->set_parent_window(GetNativeWindow());
79  login_display_->Init(screen_locker()->users(), false, true, false);
80
81  static_cast<OobeUI*>(GetWebUI()->GetController())->ShowSigninScreen(
82      login_display_.get(), login_display_.get());
83
84  registrar_.Add(this,
85                 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
86                 content::NotificationService::AllSources());
87  registrar_.Add(this,
88                 chrome::NOTIFICATION_LOCK_WEBUI_READY,
89                 content::NotificationService::AllSources());
90  registrar_.Add(this,
91                 chrome::NOTIFICATION_LOCK_BACKGROUND_DISPLAYED,
92                 content::NotificationService::AllSources());
93}
94
95void WebUIScreenLocker::ScreenLockReady() {
96  UMA_HISTOGRAM_TIMES("LockScreen.LockReady",
97                      base::TimeTicks::Now() - lock_time_);
98  ScreenLockerDelegate::ScreenLockReady();
99  SetInputEnabled(true);
100}
101
102void WebUIScreenLocker::OnAuthenticate() {
103}
104
105void WebUIScreenLocker::SetInputEnabled(bool enabled) {
106  login_display_->SetUIEnabled(enabled);
107}
108
109void WebUIScreenLocker::ShowErrorMessage(
110    int error_msg_id,
111    HelpAppLauncher::HelpTopic help_topic_id) {
112  login_display_->ShowError(error_msg_id,
113                  0 /* login_attempts */,
114                  help_topic_id);
115}
116
117void WebUIScreenLocker::AnimateAuthenticationSuccess() {
118  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateAuthenticationSuccess");
119}
120
121void WebUIScreenLocker::ClearErrors() {
122  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
123}
124
125gfx::NativeWindow WebUIScreenLocker::GetNativeWindow() const {
126  return lock_window_->GetNativeWindow();
127}
128
129content::WebUI* WebUIScreenLocker::GetAssociatedWebUI() {
130  return GetWebUI();
131}
132
133void WebUIScreenLocker::FocusUserPod() {
134  if (!webui_ready_)
135    return;
136  webui_login_->RequestFocus();
137  GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.forceLockedUserPodFocus");
138}
139
140WebUIScreenLocker::~WebUIScreenLocker() {
141  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
142  ash::Shell::GetInstance()->
143      lock_state_controller()->RemoveObserver(this);
144  // In case of shutdown, lock_window_ may be deleted before WebUIScreenLocker.
145  if (lock_window_) {
146    lock_window_->RemoveObserver(this);
147    lock_window_->Close();
148  }
149  // If LockScreen() was called, we need to clear the signin screen handler
150  // delegate set in ShowSigninScreen so that it no longer points to us.
151  if (login_display_.get()) {
152    static_cast<OobeUI*>(GetWebUI()->GetController())->
153        ResetSigninScreenHandlerDelegate();
154  }
155}
156
157////////////////////////////////////////////////////////////////////////////////
158// WebUIScreenLocker, content::NotificationObserver implementation:
159
160void WebUIScreenLocker::Observe(
161    int type,
162    const content::NotificationSource& source,
163    const content::NotificationDetails& details) {
164  switch (type) {
165    case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
166      const User& user = *content::Details<User>(details).ptr();
167      login_display_->OnUserImageChanged(user);
168      break;
169    }
170    case chrome::NOTIFICATION_LOCK_WEBUI_READY: {
171      VLOG(1) << "WebUI ready; lock window is "
172              << (lock_ready_ ? "too" : "not");
173      webui_ready_ = true;
174      if (lock_ready_)
175        ScreenLockReady();
176      break;
177    }
178    case chrome::NOTIFICATION_LOCK_BACKGROUND_DISPLAYED: {
179      UMA_HISTOGRAM_TIMES("LockScreen.BackgroundReady",
180                          base::TimeTicks::Now() - lock_time_);
181      break;
182    }
183    default:
184      WebUILoginView::Observe(type, source, details);
185  }
186}
187
188////////////////////////////////////////////////////////////////////////////////
189// WebUIScreenLocker, LoginDisplay::Delegate implementation:
190
191void WebUIScreenLocker::CancelPasswordChangedFlow()  {
192  NOTREACHED();
193}
194
195void WebUIScreenLocker::CreateAccount() {
196  NOTREACHED();
197}
198
199void WebUIScreenLocker::CompleteLogin(const UserContext& user_context) {
200  NOTREACHED();
201}
202
203string16 WebUIScreenLocker::GetConnectedNetworkName() {
204  return network_state_helper_->GetCurrentNetworkName();
205}
206
207bool WebUIScreenLocker::IsSigninInProgress() const {
208  // The way how screen locker is implemented right now there's no
209  // GAIA sign in in progress in any case.
210  return false;
211}
212
213void WebUIScreenLocker::Login(const UserContext& user_context) {
214  chromeos::ScreenLocker::default_screen_locker()->Authenticate(user_context);
215}
216
217void WebUIScreenLocker::LoginAsRetailModeUser() {
218  NOTREACHED();
219}
220
221void WebUIScreenLocker::LoginAsGuest() {
222  NOTREACHED();
223}
224
225void WebUIScreenLocker::MigrateUserData(const std::string& old_password) {
226  NOTREACHED();
227}
228
229void WebUIScreenLocker::LoginAsPublicAccount(const std::string& username) {
230  NOTREACHED();
231}
232
233void WebUIScreenLocker::OnSigninScreenReady() {
234}
235
236void WebUIScreenLocker::OnUserSelected(const std::string& username) {
237}
238
239void WebUIScreenLocker::OnStartEnterpriseEnrollment() {
240  NOTREACHED();
241}
242
243void WebUIScreenLocker::OnStartKioskEnableScreen() {
244  NOTREACHED();
245}
246
247void WebUIScreenLocker::OnStartDeviceReset() {
248  NOTREACHED();
249}
250
251void WebUIScreenLocker::OnStartKioskAutolaunchScreen() {
252  NOTREACHED();
253}
254
255void WebUIScreenLocker::ShowWrongHWIDScreen() {
256  NOTREACHED();
257}
258
259void WebUIScreenLocker::ResetPublicSessionAutoLoginTimer() {
260}
261
262void WebUIScreenLocker::ResyncUserData() {
263  NOTREACHED();
264}
265
266void WebUIScreenLocker::SetDisplayEmail(const std::string& email) {
267  NOTREACHED();
268}
269
270void WebUIScreenLocker::Signout() {
271  chromeos::ScreenLocker::default_screen_locker()->Signout();
272}
273
274////////////////////////////////////////////////////////////////////////////////
275// LockWindow::Observer implementation:
276
277void WebUIScreenLocker::OnLockWindowReady() {
278  VLOG(1) << "Lock window ready; WebUI is " << (webui_ready_ ? "too" : "not");
279  lock_ready_ = true;
280  if (webui_ready_)
281    ScreenLockReady();
282}
283
284////////////////////////////////////////////////////////////////////////////////
285// SessionLockStateObserver override.
286
287void WebUIScreenLocker::OnLockStateEvent(
288    ash::LockStateObserver::EventType event) {
289  if (event == ash::LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED) {
290    // Release capture if any.
291    aura::client::GetCaptureClient(GetNativeWindow()->GetRootWindow())->
292        SetCapture(NULL);
293    GetWebUI()->CallJavascriptFunction("cr.ui.Oobe.animateOnceFullyDisplayed");
294  }
295}
296
297////////////////////////////////////////////////////////////////////////////////
298// WidgetObserver override.
299
300void WebUIScreenLocker::OnWidgetDestroying(views::Widget* widget) {
301  lock_window_->RemoveObserver(this);
302  lock_window_ = NULL;
303}
304
305////////////////////////////////////////////////////////////////////////////////
306// PowerManagerClient::Observer overrides.
307
308void WebUIScreenLocker::LidEventReceived(bool open,
309                                         const base::TimeTicks& time) {
310  content::BrowserThread::PostTask(
311      content::BrowserThread::UI,
312      FROM_HERE,
313      base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
314}
315
316void WebUIScreenLocker::SystemResumed(const base::TimeDelta& sleep_duration) {
317  content::BrowserThread::PostTask(
318      content::BrowserThread::UI,
319      FROM_HERE,
320      base::Bind(&WebUIScreenLocker::FocusUserPod, weak_factory_.GetWeakPtr()));
321}
322
323void WebUIScreenLocker::RenderProcessGone(base::TerminationStatus status) {
324  if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID &&
325      status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
326    LOG(ERROR) << "Renderer crash on lock screen";
327    Signout();
328  }
329}
330
331}  // namespace chromeos
332