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/screens/chrome_user_selection_screen.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/browser_process_platform_part.h"
16#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
17#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
18#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
19#include "components/policy/core/common/cloud/cloud_policy_core.h"
20#include "components/policy/core/common/cloud/cloud_policy_store.h"
21#include "components/policy/core/common/policy_map.h"
22#include "components/policy/core/common/policy_types.h"
23#include "components/user_manager/user.h"
24#include "components/user_manager/user_manager.h"
25#include "components/user_manager/user_type.h"
26#include "policy/policy_constants.h"
27
28namespace chromeos {
29
30ChromeUserSelectionScreen::ChromeUserSelectionScreen()
31    : handler_initialized_(false),
32      weak_factory_(this) {
33  device_local_account_policy_service_ = g_browser_process->platform_part()->
34      browser_policy_connector_chromeos()->GetDeviceLocalAccountPolicyService();
35  device_local_account_policy_service_->AddObserver(this);
36}
37
38ChromeUserSelectionScreen::~ChromeUserSelectionScreen() {
39  device_local_account_policy_service_->RemoveObserver(this);
40}
41
42void ChromeUserSelectionScreen::Init(const user_manager::UserList& users,
43                                     bool show_guest) {
44  UserSelectionScreen::Init(users, show_guest);
45
46  // Retrieve the current policy for all users.
47  for (user_manager::UserList::const_iterator it = users.begin();
48       it != users.end(); ++it) {
49    if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT)
50      OnPolicyUpdated((*it)->GetUserID());
51  }
52}
53
54void ChromeUserSelectionScreen::SendUserList() {
55  UserSelectionScreen::SendUserList();
56  handler_initialized_ = true;
57}
58
59void ChromeUserSelectionScreen::OnPolicyUpdated(const std::string& user_id) {
60  policy::DeviceLocalAccountPolicyBroker* broker =
61      device_local_account_policy_service_->GetBrokerForUser(user_id);
62  if (!broker)
63    return;
64
65  CheckForPublicSessionDisplayNameChange(broker);
66  CheckForPublicSessionLocalePolicyChange(broker);
67}
68
69void ChromeUserSelectionScreen::OnDeviceLocalAccountsChanged() {
70  // Nothing to do here. When the list of device-local accounts changes, the
71  // entire UI is reloaded.
72}
73
74void ChromeUserSelectionScreen::CheckForPublicSessionDisplayNameChange(
75    policy::DeviceLocalAccountPolicyBroker* broker) {
76  const std::string& user_id = broker->user_id();
77  const std::string& display_name = broker->GetDisplayName();
78  if (display_name == public_session_display_names_[user_id])
79    return;
80
81  public_session_display_names_[user_id] = display_name;
82
83  if (!handler_initialized_)
84    return;
85
86  if (!display_name.empty()) {
87    // If a new display name was set by policy, notify the UI about it.
88    handler_->SetPublicSessionDisplayName(user_id, display_name);
89    return;
90  }
91
92  // When no display name is set by policy, the |User|, owned by |UserManager|,
93  // decides what display name to use. However, the order in which |UserManager|
94  // and |this| are informed of the display name change is undefined. Post a
95  // task that will update the UI after the UserManager is guaranteed to have
96  // been informed of the change.
97  base::MessageLoop::current()->PostTask(
98      FROM_HERE,
99      base::Bind(&ChromeUserSelectionScreen::SetPublicSessionDisplayName,
100                 weak_factory_.GetWeakPtr(),
101                 user_id));
102}
103
104void ChromeUserSelectionScreen::CheckForPublicSessionLocalePolicyChange(
105    policy::DeviceLocalAccountPolicyBroker* broker) {
106  const std::string& user_id = broker->user_id();
107  const policy::PolicyMap::Entry* entry =
108      broker->core()->store()->policy_map().Get(policy::key::kSessionLocales);
109
110  // Parse the list of recommended locales set by policy.
111  std::vector<std::string> new_recommended_locales;
112  base::ListValue const* list = NULL;
113  if (entry &&
114      entry->level == policy::POLICY_LEVEL_RECOMMENDED &&
115      entry->value &&
116      entry->value->GetAsList(&list)) {
117    for (base::ListValue::const_iterator it = list->begin(); it != list->end();
118         ++it) {
119      std::string locale;
120      if (!(*it)->GetAsString(&locale)) {
121        NOTREACHED();
122        new_recommended_locales.clear();
123        break;
124      }
125      new_recommended_locales.push_back(locale);
126    }
127  }
128
129  std::vector<std::string>& recommended_locales =
130      public_session_recommended_locales_[user_id];
131
132  if (new_recommended_locales != recommended_locales)
133    SetPublicSessionLocales(user_id, new_recommended_locales);
134
135  if (new_recommended_locales.empty())
136    public_session_recommended_locales_.erase(user_id);
137  else
138    recommended_locales = new_recommended_locales;
139}
140
141void ChromeUserSelectionScreen::SetPublicSessionDisplayName(
142    const std::string& user_id) {
143  const user_manager::User* user =
144      user_manager::UserManager::Get()->FindUser(user_id);
145  if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
146    return;
147
148  handler_->SetPublicSessionDisplayName(
149      user_id,
150      base::UTF16ToUTF8(user->GetDisplayName()));
151}
152
153void ChromeUserSelectionScreen::SetPublicSessionLocales(
154    const std::string& user_id,
155    const std::vector<std::string>& recommended_locales) {
156  if (!handler_initialized_)
157    return;
158
159  // Construct the list of available locales. This list consists of the
160  // recommended locales, followed by all others.
161  scoped_ptr<base::ListValue> available_locales =
162      GetUILanguageList(&recommended_locales, std::string());
163
164  // Set the initially selected locale to the first recommended locale that is
165  // actually available or the current UI locale if none of them are available.
166  const std::string default_locale = FindMostRelevantLocale(
167      recommended_locales,
168      *available_locales.get(),
169      g_browser_process->GetApplicationLocale());
170
171  // Set a flag to indicate whether the list of recommended locales contains at
172  // least two entries. This is used to decide whether the public session pod
173  // expands to its basic form (for zero or one recommended locales) or the
174  // advanced form (two or more recommended locales).
175  const bool two_or_more_recommended_locales = recommended_locales.size() >= 2;
176
177  // Notify the UI.
178  handler_->SetPublicSessionLocales(user_id,
179                                    available_locales.Pass(),
180                                    default_locale,
181                                    two_or_more_recommended_locales);
182}
183
184}  // namespace chromeos
185