blob: f83940a8eeb060eafe31b39d26f7a80828e07b85 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_chromeos.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
#include "chrome/browser/chromeos/login/hwid_checker.h"
#include "chrome/browser/chromeos/login/login_display_host_impl.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/webui_login_display.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/net/network_portal_detector.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/native_window_delegate.h"
#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/ime/xkeyboard.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(USE_AURA)
#include "ash/shell.h"
#include "ash/wm/lock_state_controller.h"
#endif
using content::BrowserThread;
using content::RenderViewHost;
namespace {
// User dictionary keys.
const char kKeyUsername[] = "username";
const char kKeyDisplayName[] = "displayName";
const char kKeyEmailAddress[] = "emailAddress";
const char kKeyEnterpriseDomain[] = "enterpriseDomain";
const char kKeyPublicAccount[] = "publicAccount";
const char kKeyLocallyManagedUser[] = "locallyManagedUser";
const char kKeySignedIn[] = "signedIn";
const char kKeyCanRemove[] = "canRemove";
const char kKeyIsOwner[] = "isOwner";
const char kKeyOauthTokenStatus[] = "oauthTokenStatus";
// Max number of users to show.
const size_t kMaxUsers = 18;
// Timeout to delay first notification about offline state for a
// current network.
const int kOfflineTimeoutSec = 5;
// Timeout used to prevent infinite connecting to a flaky network.
const int kConnectingTimeoutSec = 60;
// Type of the login screen UI that is currently presented to user.
const char kSourceGaiaSignin[] = "gaia-signin";
const char kSourceAccountPicker[] = "account-picker";
// The Task posted to PostTaskAndReply in StartClearingDnsCache on the IO
// thread.
void ClearDnsCache(IOThread* io_thread) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (browser_shutdown::IsTryingToQuit())
return;
io_thread->ClearHostCache();
}
static bool Contains(const std::vector<std::string>& container,
const std::string& value) {
return std::find(container.begin(), container.end(), value) !=
container.end();
}
} // namespace
namespace chromeos {
namespace {
const char kNetworkStateOffline[] = "offline";
const char kNetworkStateOnline[] = "online";
const char kNetworkStateCaptivePortal[] = "behind captive portal";
const char kNetworkStateConnecting[] = "connecting";
const char kNetworkStateProxyAuthRequired[] = "proxy auth required";
const char kErrorReasonProxyAuthCancelled[] = "proxy auth cancelled";
const char kErrorReasonProxyAuthSupplied[] = "proxy auth supplied";
const char kErrorReasonProxyConnectionFailed[] = "proxy connection failed";
const char kErrorReasonProxyConfigChanged[] = "proxy config changed";
const char kErrorReasonLoadingTimeout[] = "loading timeout";
const char kErrorReasonPortalDetected[] = "portal detected";
const char kErrorReasonNetworkStateChanged[] = "network state changed";
const char kErrorReasonUpdate[] = "update";
const char kErrorReasonFrameError[] = "frame error";
const char* NetworkStateStatusString(NetworkStateInformer::State state) {
switch (state) {
case NetworkStateInformer::OFFLINE:
return kNetworkStateOffline;
case NetworkStateInformer::ONLINE:
return kNetworkStateOnline;
case NetworkStateInformer::CAPTIVE_PORTAL:
return kNetworkStateCaptivePortal;
case NetworkStateInformer::CONNECTING:
return kNetworkStateConnecting;
case NetworkStateInformer::PROXY_AUTH_REQUIRED:
return kNetworkStateProxyAuthRequired;
default:
NOTREACHED();
return NULL;
}
}
const char* ErrorReasonString(ErrorScreenActor::ErrorReason reason) {
switch (reason) {
case ErrorScreenActor::ERROR_REASON_PROXY_AUTH_CANCELLED:
return kErrorReasonProxyAuthCancelled;
case ErrorScreenActor::ERROR_REASON_PROXY_AUTH_SUPPLIED:
return kErrorReasonProxyAuthSupplied;
case ErrorScreenActor::ERROR_REASON_PROXY_CONNECTION_FAILED:
return kErrorReasonProxyConnectionFailed;
case ErrorScreenActor::ERROR_REASON_PROXY_CONFIG_CHANGED:
return kErrorReasonProxyConfigChanged;
case ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT:
return kErrorReasonLoadingTimeout;
case ErrorScreenActor::ERROR_REASON_PORTAL_DETECTED:
return kErrorReasonPortalDetected;
case ErrorScreenActor::ERROR_REASON_NETWORK_STATE_CHANGED:
return kErrorReasonNetworkStateChanged;
case ErrorScreenActor::ERROR_REASON_UPDATE:
return kErrorReasonUpdate;
case ErrorScreenActor::ERROR_REASON_FRAME_ERROR:
return kErrorReasonFrameError;
default:
NOTREACHED();
return NULL;
}
}
// Updates params dictionary passed to the auth extension with related
// preferences from CrosSettings.
void UpdateAuthParamsFromSettings(DictionaryValue* params,
const CrosSettings* cros_settings) {
bool allow_new_user = true;
cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
bool allow_guest = true;
cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
// Account creation depends on Guest sign-in (http://crosbug.com/24570).
params->SetBoolean("createAccount", allow_new_user && allow_guest);
params->SetBoolean("guestSignin", allow_guest);
}
bool IsOnline(NetworkStateInformer::State state,
ErrorScreenActor::ErrorReason reason) {
return state == NetworkStateInformer::ONLINE &&
reason != ErrorScreenActor::ERROR_REASON_PORTAL_DETECTED &&
reason != ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT;
}
bool IsUnderCaptivePortal(NetworkStateInformer::State state,
ErrorScreenActor::ErrorReason reason) {
return state == NetworkStateInformer::CAPTIVE_PORTAL ||
reason == ErrorScreenActor::ERROR_REASON_PORTAL_DETECTED;
}
bool IsProxyError(NetworkStateInformer::State state,
ErrorScreenActor::ErrorReason reason,
net::Error frame_error) {
return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
reason == ErrorScreenActor::ERROR_REASON_PROXY_AUTH_CANCELLED ||
reason == ErrorScreenActor::ERROR_REASON_PROXY_CONNECTION_FAILED ||
(reason == ErrorScreenActor::ERROR_REASON_FRAME_ERROR &&
(frame_error == net::ERR_PROXY_CONNECTION_FAILED ||
frame_error == net::ERR_TUNNEL_CONNECTION_FAILED));
}
bool IsSigninScreen(const OobeUI::Screen screen) {
return screen == OobeUI::SCREEN_GAIA_SIGNIN ||
screen == OobeUI::SCREEN_ACCOUNT_PICKER;
}
bool IsSigninScreenError(ErrorScreen::ErrorState error_state) {
return error_state == ErrorScreen::ERROR_STATE_PORTAL ||
error_state == ErrorScreen::ERROR_STATE_OFFLINE ||
error_state == ErrorScreen::ERROR_STATE_PROXY ||
error_state == ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT;
}
// Returns network name by service path.
std::string GetNetworkName(const std::string& service_path) {
const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
GetNetworkState(service_path);
if (!network)
return std::string();
return network->name();
}
// Returns captive portal state for a network by its service path.
NetworkPortalDetector::CaptivePortalState GetCaptivePortalState(
const std::string& service_path) {
NetworkPortalDetector* detector = NetworkPortalDetector::Get();
const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
GetNetworkState(service_path);
if (!detector || !network)
return NetworkPortalDetector::CaptivePortalState();
return detector->GetCaptivePortalState(network);
}
void RecordDiscrepancyWithShill(
const NetworkState* network,
const NetworkPortalDetector::CaptivePortalStatus status) {
if (network->connection_state() == shill::kStateOnline) {
UMA_HISTOGRAM_ENUMERATION(
"CaptivePortal.OOBE.DiscrepancyWithShill_Online",
status,
NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
} else if (network->connection_state() == shill::kStatePortal) {
UMA_HISTOGRAM_ENUMERATION(
"CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool",
status,
NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
} else {
UMA_HISTOGRAM_ENUMERATION(
"CaptivePortal.OOBE.DiscrepancyWithShill_Offline",
status,
NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
}
}
// Record state and descripancies with shill (e.g. shill thinks that
// network is online but NetworkPortalDetector claims that it's behind
// portal) for the network identified by |service_path|.
void RecordNetworkPortalDetectorStats(const std::string& service_path) {
const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
GetNetworkState(service_path);
if (!network)
return;
NetworkPortalDetector::CaptivePortalState state =
GetCaptivePortalState(service_path);
if (state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN)
return;
UMA_HISTOGRAM_ENUMERATION("CaptivePortal.OOBE.DetectionResult",
state.status,
NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
switch (state.status) {
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
NOTREACHED();
break;
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
if (network->connection_state() == shill::kStateOnline ||
network->connection_state() == shill::kStatePortal)
RecordDiscrepancyWithShill(network, state.status);
break;
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
if (network->connection_state() != shill::kStateOnline)
RecordDiscrepancyWithShill(network, state.status);
break;
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
if (network->connection_state() != shill::kStatePortal)
RecordDiscrepancyWithShill(network, state.status);
break;
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
if (network->connection_state() != shill::kStateOnline)
RecordDiscrepancyWithShill(network, state.status);
break;
case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
NOTREACHED();
break;
}
}
static bool SetUserInputMethodImpl(
const std::string& username,
chromeos::input_method::InputMethodManager* manager) {
PrefService* const local_state = g_browser_process->local_state();
const base::DictionaryValue* users_lru_input_methods =
local_state->GetDictionary(prefs::kUsersLRUInputMethod);
if (users_lru_input_methods == NULL) {
DLOG(WARNING) << "SetUserInputMethod('" << username
<< "'): no kUsersLRUInputMethod";
return false;
}
std::string input_method;
if (!users_lru_input_methods->GetStringWithoutPathExpansion(username,
&input_method)) {
DLOG(INFO) << "SetUserInputMethod('" << username
<< "'): no input method for this user";
return false;
}
if (input_method.empty())
return false;
if (!manager->IsLoginKeyboard(input_method)) {
LOG(WARNING) << "SetUserInputMethod('" << username
<< "'): stored user LRU input method '" << input_method
<< "' is no longer Full Latin Keyboard Language"
<< " (entry dropped). Use hardware default instead.";
DictionaryPrefUpdate updater(local_state, prefs::kUsersLRUInputMethod);
base::DictionaryValue* const users_lru_input_methods = updater.Get();
if (users_lru_input_methods != NULL) {
users_lru_input_methods->SetStringWithoutPathExpansion(username, "");
}
return false;
}
if (!Contains(manager->GetActiveInputMethodIds(), input_method)) {
if (!manager->EnableInputMethod(input_method)) {
DLOG(ERROR) << "SigninScreenHandler::SetUserInputMethod('" << username
<< "'): user input method '" << input_method
<< "' is not enabled and enabling failed (ignored!).";
}
}
manager->ChangeInputMethod(input_method);
return true;
}
} // namespace
// SigninScreenHandler implementation ------------------------------------------
SigninScreenHandler::SigninScreenHandler(
const scoped_refptr<NetworkStateInformer>& network_state_informer,
ErrorScreenActor* error_screen_actor,
CoreOobeActor* core_oobe_actor)
: ui_state_(UI_STATE_UNKNOWN),
frame_state_(FRAME_STATE_UNKNOWN),
frame_error_(net::OK),
delegate_(NULL),
native_window_delegate_(NULL),
show_on_init_(false),
oobe_ui_(false),
focus_stolen_(false),
gaia_silent_load_(false),
is_account_picker_showing_first_time_(false),
dns_cleared_(false),
dns_clear_task_running_(false),
cookies_cleared_(false),
network_state_informer_(network_state_informer),
test_expects_complete_login_(false),
weak_factory_(this),
webui_visible_(false),
preferences_changed_delayed_(false),
error_screen_actor_(error_screen_actor),
core_oobe_actor_(core_oobe_actor),
is_first_update_state_call_(true),
offline_login_active_(false),
last_network_state_(NetworkStateInformer::UNKNOWN),
has_pending_auth_ui_(false),
wait_for_auto_enrollment_check_(false) {
DCHECK(network_state_informer_.get());
DCHECK(error_screen_actor_);
DCHECK(core_oobe_actor_);
network_state_informer_->AddObserver(this);
allow_new_user_subscription_ = CrosSettings::Get()->AddSettingsObserver(
kAccountsPrefAllowNewUser,
base::Bind(&SigninScreenHandler::UserSettingsChanged,
base::Unretained(this)));
allow_guest_subscription_ = CrosSettings::Get()->AddSettingsObserver(
kAccountsPrefAllowGuest,
base::Bind(&SigninScreenHandler::UserSettingsChanged,
base::Unretained(this)));
registrar_.Add(this,
chrome::NOTIFICATION_AUTH_NEEDED,
content::NotificationService::AllSources());
registrar_.Add(this,
chrome::NOTIFICATION_AUTH_SUPPLIED,
content::NotificationService::AllSources());
registrar_.Add(this,
chrome::NOTIFICATION_AUTH_CANCELLED,
content::NotificationService::AllSources());
}
SigninScreenHandler::~SigninScreenHandler() {
weak_factory_.InvalidateWeakPtrs();
SystemKeyEventListener* key_event_listener =
SystemKeyEventListener::GetInstance();
if (key_event_listener)
key_event_listener->RemoveCapsLockObserver(this);
if (delegate_)
delegate_->SetWebUIHandler(NULL);
network_state_informer_->RemoveObserver(this);
}
void SigninScreenHandler::DeclareLocalizedValues(
LocalizedValuesBuilder* builder) {
builder->Add("signinScreenTitle", IDS_SIGNIN_SCREEN_TITLE);
builder->Add("signinScreenPasswordChanged",
IDS_SIGNIN_SCREEN_PASSWORD_CHANGED);
builder->Add("passwordHint", IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT);
builder->Add("podMenuButtonAccessibleName",
IDS_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME);
builder->Add("podMenuRemoveItemAccessibleName",
IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME);
builder->Add("passwordFieldAccessibleName",
IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME);
builder->Add("signedIn", IDS_SCREEN_LOCK_ACTIVE_USER);
builder->Add("signinButton", IDS_LOGIN_BUTTON);
builder->Add("shutDown", IDS_SHUTDOWN_BUTTON);
builder->Add("addUser", IDS_ADD_USER_BUTTON);
builder->Add("browseAsGuest", IDS_GO_INCOGNITO_BUTTON);
builder->Add("cancel", IDS_CANCEL);
builder->Add("signOutUser", IDS_SCREEN_LOCK_SIGN_OUT);
builder->Add("createAccount", IDS_CREATE_ACCOUNT_HTML);
builder->Add("guestSignin", IDS_BROWSE_WITHOUT_SIGNING_IN_HTML);
builder->Add("createLocallyManagedUser",
IDS_CREATE_LOCALLY_MANAGED_USER_HTML);
builder->Add("createManagedUserFeatureName",
IDS_CREATE_LOCALLY_MANAGED_USER_FEATURE_NAME);
builder->Add("offlineLogin", IDS_OFFLINE_LOGIN_HTML);
builder->Add("ownerUserPattern", IDS_LOGIN_POD_OWNER_USER);
builder->Add("removeUser", IDS_LOGIN_POD_REMOVE_USER);
builder->Add("errorTpmFailureTitle", IDS_LOGIN_ERROR_TPM_FAILURE_TITLE);
builder->Add("errorTpmFailureReboot", IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT);
builder->Add("errorTpmFailureRebootButton",
IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT_BUTTON);
builder->Add(
"disabledAddUserTooltip",
g_browser_process->browser_policy_connector()->IsEnterpriseManaged() ?
IDS_DISABLED_ADD_USER_TOOLTIP_ENTERPRISE :
IDS_DISABLED_ADD_USER_TOOLTIP);
builder->Add("supervisedUserExpiredTokenWarning",
IDS_SUPERVISED_USER_EXPIRED_TOKEN_WARNING);
builder->Add("multiple-signin-banner-text", IDS_LOGIN_USER_ADDING_BANNER);
// Strings used by password changed dialog.
builder->Add("passwordChangedTitle", IDS_LOGIN_PASSWORD_CHANGED_TITLE);
builder->Add("passwordChangedDesc", IDS_LOGIN_PASSWORD_CHANGED_DESC);
builder->AddF("passwordChangedMoreInfo",
IDS_LOGIN_PASSWORD_CHANGED_MORE_INFO,
IDS_SHORT_PRODUCT_OS_NAME);
builder->Add("oldPasswordHint", IDS_LOGIN_PASSWORD_CHANGED_OLD_PASSWORD_HINT);
builder->Add("oldPasswordIncorrect",
IDS_LOGIN_PASSWORD_CHANGED_INCORRECT_OLD_PASSWORD);
builder->Add("passwordChangedCantRemember",
IDS_LOGIN_PASSWORD_CHANGED_CANT_REMEMBER);
builder->Add("passwordChangedBackButton",
IDS_LOGIN_PASSWORD_CHANGED_BACK_BUTTON);
builder->Add("passwordChangedsOkButton", IDS_OK);
builder->Add("passwordChangedProceedAnyway",
IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY);
builder->Add("proceedAnywayButton",
IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY_BUTTON);
builder->Add("publicAccountInfoFormat", IDS_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT);
builder->Add("publicAccountReminder",
IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER);
builder->Add("publicAccountEnter", IDS_LOGIN_PUBLIC_ACCOUNT_ENTER);
builder->Add("publicAccountEnterAccessibleName",
IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME);
builder->AddF("removeUserWarningText",
IDS_LOGIN_POD_USER_REMOVE_WARNING,
UTF8ToUTF16(chrome::kSupervisedUserManagementDisplayURL));
builder->Add("removeUserWarningButtonTitle",
IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON);
// Strings used by confirm password dialog.
builder->Add("confirmPasswordTitle", IDS_LOGIN_CONFIRM_PASSWORD_TITLE);
builder->Add("confirmPasswordHint", IDS_LOGIN_CONFIRM_PASSWORD_HINT);
builder->Add("confirmPasswordConfirmButton",
IDS_LOGIN_CONFIRM_PASSWORD_CONFIRM_BUTTON);
// Strings used by no password warning dialog.
builder->Add("noPasswordWarningTitle", IDS_LOGIN_NO_PASSWORD_WARNING_TITLE);
builder->Add("noPasswordWarningBody", IDS_LOGIN_NO_PASSWORD_WARNING);
builder->Add("noPasswordWarningOkButton",
IDS_LOGIN_NO_PASSWORD_WARNING_DISMISS_BUTTON);
if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
builder->Add("demoLoginMessage", IDS_KIOSK_MODE_LOGIN_MESSAGE);
}
void SigninScreenHandler::Show(bool oobe_ui) {
CHECK(delegate_);
oobe_ui_ = oobe_ui;
if (!page_is_ready()) {
show_on_init_ = true;
return;
}
if (oobe_ui) {
// Shows new user sign-in for OOBE.
HandleShowAddUser(NULL);
} else {
// Populates account picker. Animation is turned off for now until we
// figure out how to make it fast enough.
SendUserList(false);
// Reset Caps Lock state when login screen is shown.
input_method::InputMethodManager::Get()->GetXKeyboard()->
SetCapsLockEnabled(false);
DictionaryValue params;
params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent());
UpdateUIState(UI_STATE_ACCOUNT_PICKER, &params);
}
}
void SigninScreenHandler::ShowRetailModeLoginSpinner() {
CallJS("showLoginSpinner");
}
void SigninScreenHandler::SetDelegate(SigninScreenHandlerDelegate* delegate) {
delegate_ = delegate;
if (delegate_)
delegate_->SetWebUIHandler(this);
}
void SigninScreenHandler::SetNativeWindowDelegate(
NativeWindowDelegate* native_window_delegate) {
native_window_delegate_ = native_window_delegate;
}
void SigninScreenHandler::OnNetworkReady() {
MaybePreloadAuthExtension();
}
void SigninScreenHandler::UpdateState(ErrorScreenActor::ErrorReason reason) {
UpdateStateInternal(reason, false);
}
// SigninScreenHandler, private: -----------------------------------------------
void SigninScreenHandler::UpdateUIState(UIState ui_state,
DictionaryValue* params) {
switch (ui_state) {
case UI_STATE_GAIA_SIGNIN:
ui_state_ = UI_STATE_GAIA_SIGNIN;
ShowScreen(OobeUI::kScreenGaiaSignin, params);
break;
case UI_STATE_ACCOUNT_PICKER:
ui_state_ = UI_STATE_ACCOUNT_PICKER;
ShowScreen(OobeUI::kScreenAccountPicker, params);
break;
default:
NOTREACHED();
break;
}
}
// TODO (ygorshenin@): split this method into small parts.
void SigninScreenHandler::UpdateStateInternal(
ErrorScreenActor::ErrorReason reason,
bool force_update) {
// Do nothing once user has signed in or sign in is in progress.
// TODO(ygorshenin): We will end up here when processing network state
// notification but no ShowSigninScreen() was called so delegate_ will be
// NULL. Network state processing logic does not belong here.
if (delegate_ &&
(delegate_->IsUserSigninCompleted() || delegate_->IsSigninInProgress())) {
return;
}
NetworkStateInformer::State state = network_state_informer_->state();
const std::string network_path = network_state_informer_->network_path();
const std::string network_name = GetNetworkName(network_path);
// Skip "update" notification about OFFLINE state from
// NetworkStateInformer if previous notification already was
// delayed.
if ((state == NetworkStateInformer::OFFLINE || has_pending_auth_ui_) &&
!force_update &&
!update_state_closure_.IsCancelled()) {
return;
}
// TODO (ygorshenin@): switch log level to INFO once signin screen
// will be tested well.
LOG(WARNING) << "SigninScreenHandler::UpdateStateInternal(): "
<< "state=" << NetworkStateStatusString(state) << ", "
<< "network_name=" << network_name << ", "
<< "reason=" << ErrorReasonString(reason) << ", "
<< "force_update=" << force_update;
update_state_closure_.Cancel();
if ((state == NetworkStateInformer::OFFLINE && !force_update) ||
has_pending_auth_ui_) {
update_state_closure_.Reset(
base::Bind(
&SigninScreenHandler::UpdateStateInternal,
weak_factory_.GetWeakPtr(), reason, true));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
update_state_closure_.callback(),
base::TimeDelta::FromSeconds(kOfflineTimeoutSec));
return;
}
// Don't show or hide error screen if we're in connecting state.
if (state == NetworkStateInformer::CONNECTING && !force_update) {
if (connecting_closure_.IsCancelled()) {
// First notification about CONNECTING state.
connecting_closure_.Reset(
base::Bind(&SigninScreenHandler::UpdateStateInternal,
weak_factory_.GetWeakPtr(), reason, true));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
connecting_closure_.callback(),
base::TimeDelta::FromSeconds(kConnectingTimeoutSec));
}
return;
}
connecting_closure_.Cancel();
const bool is_online = IsOnline(state, reason);
const bool is_under_captive_portal = IsUnderCaptivePortal(state, reason);
const bool is_gaia_loading_timeout =
(reason == ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
const bool is_gaia_error = frame_error_ != net::OK &&
frame_error_ != net::ERR_NETWORK_CHANGED;
const bool is_gaia_signin = IsGaiaVisible() || IsGaiaHiddenByError();
const bool error_screen_should_overlay =
!offline_login_active_ && IsGaiaVisible();
const bool from_not_online_to_online_transition =
is_online && last_network_state_ != NetworkStateInformer::ONLINE;
last_network_state_ = state;
if (is_online || !is_under_captive_portal)
error_screen_actor_->HideCaptivePortal();
// Hide offline message (if needed) and return if current screen is
// not a Gaia frame.
if (!is_gaia_signin) {
if (!IsSigninScreenHiddenByError())
HideOfflineMessage(state, reason);
return;
}
// Reload frame if network state is changed from {!ONLINE} -> ONLINE state.
if (reason == ErrorScreenActor::ERROR_REASON_NETWORK_STATE_CHANGED &&
from_not_online_to_online_transition) {
// Schedules a immediate retry.
LOG(WARNING) << "Retry page load since network has been changed.";
ReloadGaiaScreen();
}
if (reason == ErrorScreenActor::ERROR_REASON_PROXY_CONFIG_CHANGED &&
error_screen_should_overlay) {
// Schedules a immediate retry.
LOG(WARNING) << "Retry page load since proxy settings has been changed.";
ReloadGaiaScreen();
}
if (reason == ErrorScreenActor::ERROR_REASON_FRAME_ERROR &&
!IsProxyError(state, reason, frame_error_)) {
LOG(WARNING) << "Retry page load due to reason: "
<< ErrorReasonString(reason);
ReloadGaiaScreen();
}
if ((!is_online || is_gaia_loading_timeout || is_gaia_error) &&
!offline_login_active_) {
SetupAndShowOfflineMessage(state, reason);
} else {
HideOfflineMessage(state, reason);
}
}
void SigninScreenHandler::SetupAndShowOfflineMessage(
NetworkStateInformer:: State state,
ErrorScreenActor::ErrorReason reason) {
const std::string network_path = network_state_informer_->network_path();
const bool is_under_captive_portal = IsUnderCaptivePortal(state, reason);
const bool is_proxy_error = IsProxyError(state, reason, frame_error_);
const bool is_gaia_loading_timeout =
(reason == ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
// Record portal detection stats only if we're going to show or
// change state of the error screen.
RecordNetworkPortalDetectorStats(network_path);
if (is_proxy_error) {
error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
std::string());
} else if (is_under_captive_portal) {
// Do not bother a user with obsessive captive portal showing. This
// check makes captive portal being shown only once: either when error
// screen is shown for the first time or when switching from another
// error screen (offline, proxy).
if (IsGaiaVisible() ||
(error_screen_actor_->error_state() !=
ErrorScreen::ERROR_STATE_PORTAL)) {
error_screen_actor_->FixCaptivePortal();
}
const std::string network_name = GetNetworkName(network_path);
error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
network_name);
} else if (is_gaia_loading_timeout) {
error_screen_actor_->SetErrorState(
ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT, std::string());
} else {
error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
std::string());
}
const bool guest_signin_allowed = IsGuestSigninAllowed() &&
IsSigninScreenError(error_screen_actor_->error_state());
error_screen_actor_->AllowGuestSignin(guest_signin_allowed);
const bool offline_login_allowed = IsOfflineLoginAllowed() &&
IsSigninScreenError(error_screen_actor_->error_state()) &&
error_screen_actor_->error_state() !=
ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT;
error_screen_actor_->AllowOfflineLogin(offline_login_allowed);
if (GetCurrentScreen() != OobeUI::SCREEN_ERROR_MESSAGE) {
DictionaryValue params;
const std::string network_type = network_state_informer_->network_type();
params.SetString("lastNetworkType", network_type);
error_screen_actor_->SetUIState(ErrorScreen::UI_STATE_SIGNIN);
error_screen_actor_->Show(OobeUI::SCREEN_GAIA_SIGNIN, &params);
}
}
void SigninScreenHandler::HideOfflineMessage(
NetworkStateInformer::State state,
ErrorScreenActor::ErrorReason reason) {
if (!IsSigninScreenHiddenByError())
return;
error_screen_actor_->Hide();
// Forces a reload for Gaia screen on hiding error message.
if (IsGaiaVisible() || IsGaiaHiddenByError())
ReloadGaiaScreen();
}
void SigninScreenHandler::ReloadGaiaScreen() {
if (frame_state_ == FRAME_STATE_LOADING)
return;
NetworkStateInformer::State state = network_state_informer_->state();
if (state != NetworkStateInformer::ONLINE) {
LOG(WARNING) << "Skipping reload of auth extension frame since "
<< "network state=" << NetworkStateStatusString(state);
return;
}
LOG(WARNING) << "Reload auth extension frame.";
frame_state_ = FRAME_STATE_LOADING;
CallJS("login.GaiaSigninScreen.doReload");
}
void SigninScreenHandler::Initialize() {
// If delegate_ is NULL here (e.g. WebUIScreenLocker has been destroyed),
// don't do anything, just return.
if (!delegate_)
return;
// Register for Caps Lock state change notifications;
SystemKeyEventListener* key_event_listener =
SystemKeyEventListener::GetInstance();
if (key_event_listener)
key_event_listener->AddCapsLockObserver(this);
if (show_on_init_) {
show_on_init_ = false;
Show(oobe_ui_);
}
}
gfx::NativeWindow SigninScreenHandler::GetNativeWindow() {
if (native_window_delegate_)
return native_window_delegate_->GetNativeWindow();
return NULL;
}
void SigninScreenHandler::RegisterMessages() {
AddCallback("authenticateUser", &SigninScreenHandler::HandleAuthenticateUser);
AddCallback("completeLogin", &SigninScreenHandler::HandleCompleteLogin);
AddCallback("completeAuthentication",
&SigninScreenHandler::HandleCompleteAuthentication);
AddCallback("getUsers", &SigninScreenHandler::HandleGetUsers);
AddCallback("launchDemoUser", &SigninScreenHandler::HandleLaunchDemoUser);
AddCallback("launchIncognito", &SigninScreenHandler::HandleLaunchIncognito);
AddCallback("showLocallyManagedUserCreationScreen",
&SigninScreenHandler::HandleShowLocallyManagedUserCreationScreen);
AddCallback("launchPublicAccount",
&SigninScreenHandler::HandleLaunchPublicAccount);
AddRawCallback("offlineLogin", &SigninScreenHandler::HandleOfflineLogin);
AddCallback("rebootSystem", &SigninScreenHandler::HandleRebootSystem);
AddRawCallback("showAddUser", &SigninScreenHandler::HandleShowAddUser);
AddCallback("shutdownSystem", &SigninScreenHandler::HandleShutdownSystem);
AddCallback("loadWallpaper", &SigninScreenHandler::HandleLoadWallpaper);
AddCallback("removeUser", &SigninScreenHandler::HandleRemoveUser);
AddCallback("toggleEnrollmentScreen",
&SigninScreenHandler::HandleToggleEnrollmentScreen);
AddCallback("toggleKioskEnableScreen",
&SigninScreenHandler::HandleToggleKioskEnableScreen);
AddCallback("toggleResetScreen",
&SigninScreenHandler::HandleToggleResetScreen);
AddCallback("launchHelpApp", &SigninScreenHandler::HandleLaunchHelpApp);
AddCallback("createAccount", &SigninScreenHandler::HandleCreateAccount);
AddCallback("accountPickerReady",
&SigninScreenHandler::HandleAccountPickerReady);
AddCallback("wallpaperReady", &SigninScreenHandler::HandleWallpaperReady);
AddCallback("loginWebuiReady", &SigninScreenHandler::HandleLoginWebuiReady);
AddCallback("signOutUser", &SigninScreenHandler::HandleSignOutUser);
AddCallback("networkErrorShown",
&SigninScreenHandler::HandleNetworkErrorShown);
AddCallback("openProxySettings",
&SigninScreenHandler::HandleOpenProxySettings);
AddCallback("loginVisible", &SigninScreenHandler::HandleLoginVisible);
AddCallback("cancelPasswordChangedFlow",
&SigninScreenHandler::HandleCancelPasswordChangedFlow);
AddCallback("cancelUserAdding",
&SigninScreenHandler::HandleCancelUserAdding);
AddCallback("migrateUserData", &SigninScreenHandler::HandleMigrateUserData);
AddCallback("resyncUserData", &SigninScreenHandler::HandleResyncUserData);
AddCallback("loginUIStateChanged",
&SigninScreenHandler::HandleLoginUIStateChanged);
AddCallback("unlockOnLoginSuccess",
&SigninScreenHandler::HandleUnlockOnLoginSuccess);
AddCallback("frameLoadingCompleted",
&SigninScreenHandler::HandleFrameLoadingCompleted);
AddCallback("showLoadingTimeoutError",
&SigninScreenHandler::HandleShowLoadingTimeoutError);
AddCallback("updateOfflineLogin",
&SigninScreenHandler::HandleUpdateOfflineLogin);
AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
// This message is sent by the kiosk app menu, but is handled here
// so we can tell the delegate to launch the app.
AddCallback("launchKioskApp", &SigninScreenHandler::HandleLaunchKioskApp);
}
void SigninScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(prefs::kUsersLRUInputMethod);
}
void SigninScreenHandler::HandleGetUsers() {
SendUserList(false);
}
void SigninScreenHandler::ClearAndEnablePassword() {
core_oobe_actor_->ResetSignInUI(false);
}
void SigninScreenHandler::ClearUserPodPassword() {
core_oobe_actor_->ClearUserPodPassword();
}
void SigninScreenHandler::RefocusCurrentPod() {
core_oobe_actor_->RefocusCurrentPod();
}
void SigninScreenHandler::OnLoginSuccess(const std::string& username) {
core_oobe_actor_->OnLoginSuccess(username);
}
void SigninScreenHandler::OnUserRemoved(const std::string& username) {
SendUserList(false);
}
void SigninScreenHandler::OnUserImageChanged(const User& user) {
if (page_is_ready())
CallJS("login.AccountPickerScreen.updateUserImage", user.email());
}
void SigninScreenHandler::OnPreferencesChanged() {
// Make sure that one of the login UI is fully functional now, otherwise
// preferences update would be picked up next time it will be shown.
if (!webui_visible_) {
LOG(WARNING) << "Login UI is not active - postponed prefs change.";
preferences_changed_delayed_ = true;
return;
}
if (delegate_ && !delegate_->IsShowUsers()) {
HandleShowAddUser(NULL);
} else {
SendUserList(false);
UpdateUIState(UI_STATE_ACCOUNT_PICKER, NULL);
}
preferences_changed_delayed_ = false;
}
void SigninScreenHandler::ResetSigninScreenHandlerDelegate() {
SetDelegate(NULL);
}
void SigninScreenHandler::ShowError(int login_attempts,
const std::string& error_text,
const std::string& help_link_text,
HelpAppLauncher::HelpTopic help_topic_id) {
core_oobe_actor_->ShowSignInError(login_attempts, error_text, help_link_text,
help_topic_id);
}
void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) {
switch (error_id) {
case LoginDisplay::TPM_ERROR:
core_oobe_actor_->ShowTpmError();
break;
default:
NOTREACHED() << "Unknown sign in error";
break;
}
}
void SigninScreenHandler::ShowSigninUI(const std::string& email) {
core_oobe_actor_->ShowSignInUI(email);
}
void SigninScreenHandler::ShowGaiaPasswordChanged(const std::string& username) {
email_ = username;
password_changed_for_.insert(email_);
core_oobe_actor_->ShowSignInUI(email_);
CallJS("login.AccountPickerScreen.updateUserGaiaNeeded", email_);
}
void SigninScreenHandler::ShowPasswordChangedDialog(bool show_password_error) {
core_oobe_actor_->ShowPasswordChangedScreen(show_password_error);
}
void SigninScreenHandler::ShowSigninScreenForCreds(
const std::string& username,
const std::string& password) {
VLOG(2) << "ShowSigninScreenForCreds for user " << username
<< ", frame_state=" << frame_state_;
test_user_ = username;
test_pass_ = password;
test_expects_complete_login_ = true;
// Submit login form for test if gaia is ready. If gaia is loading, login
// will be attempted in HandleLoginWebuiReady after gaia is ready. Otherwise,
// reload gaia then follow the loading case.
if (frame_state_ == FRAME_STATE_LOADED)
SubmitLoginFormForTest();
else if (frame_state_ != FRAME_STATE_LOADING)
HandleShowAddUser(NULL);
}
void SigninScreenHandler::OnCookiesCleared(base::Closure on_clear_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
cookies_cleared_ = true;
on_clear_callback.Run();
}
void SigninScreenHandler::OnCapsLockChange(bool enabled) {
if (page_is_ready())
CallJS("login.AccountPickerScreen.setCapsLockState", enabled);
}
void SigninScreenHandler::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_AUTH_NEEDED: {
has_pending_auth_ui_ = true;
break;
}
case chrome::NOTIFICATION_AUTH_SUPPLIED:
has_pending_auth_ui_ = false;
if (IsSigninScreenHiddenByError()) {
// Hide error screen and reload auth extension.
HideOfflineMessage(network_state_informer_->state(),
ErrorScreenActor::ERROR_REASON_PROXY_AUTH_SUPPLIED);
} else if (ui_state_ == UI_STATE_GAIA_SIGNIN) {
// Reload auth extension as proxy credentials are supplied.
ReloadGaiaScreen();
}
break;
case chrome::NOTIFICATION_AUTH_CANCELLED: {
// Don't reload auth extension if proxy auth dialog was cancelled.
has_pending_auth_ui_ = false;
break;
}
default:
NOTREACHED() << "Unexpected notification " << type;
}
}
void SigninScreenHandler::OnDnsCleared() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
dns_clear_task_running_ = false;
dns_cleared_ = true;
ShowSigninScreenIfReady();
}
// Update keyboard layout to least recently used by the user.
void SigninScreenHandler::SetUserInputMethod(const std::string& username) {
UserManager* user_manager = UserManager::Get();
if (user_manager->IsUserLoggedIn()) {
// We are on sign-in screen inside user session (adding new user to
// the session or on lock screen), don't switch input methods in this case.
// TODO(dpolukhin): adding user and sign-in should be consistent
// crbug.com/292774
return;
}
chromeos::input_method::InputMethodManager* const manager =
chromeos::input_method::InputMethodManager::Get();
const bool succeed = SetUserInputMethodImpl(username, manager);
// This is also a case when LRU layout is set only for a few local users,
// thus others need to be switched to default locale.
// Otherwise they will end up using another user's locale to log in.
if (!succeed) {
DLOG(INFO) << "SetUserInputMethod('" << username
<< "'): failed to set user layout. Switching to default.";
manager->SetInputMethodDefault();
}
}
void SigninScreenHandler::ShowSigninScreenIfReady() {
if (!dns_cleared_ || !cookies_cleared_ || !delegate_)
return;
std::string active_network_path = network_state_informer_->network_path();
if (gaia_silent_load_ &&
(network_state_informer_->state() != NetworkStateInformer::ONLINE ||
gaia_silent_load_network_ != active_network_path)) {
// Network has changed. Force Gaia reload.
gaia_silent_load_ = false;
// Gaia page will be realoded, so focus isn't stolen anymore.
focus_stolen_ = false;
}
// Note that LoadAuthExtension clears |email_|.
if (email_.empty())
delegate_->LoadSigninWallpaper();
else
delegate_->LoadWallpaper(email_);
// Set Least Recently Used input method for the user.
if (!email_.empty())
SetUserInputMethod(email_);
LoadAuthExtension(!gaia_silent_load_, false, false);
UpdateUIState(UI_STATE_GAIA_SIGNIN, NULL);
if (gaia_silent_load_) {
// The variable is assigned to false because silently loaded Gaia page was
// used.
gaia_silent_load_ = false;
if (focus_stolen_)
HandleLoginWebuiReady();
}
UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
}
void SigninScreenHandler::UpdateAuthParams(DictionaryValue* params) {
if (!delegate_)
return;
UpdateAuthParamsFromSettings(params, CrosSettings::Get());
// Allow locally managed user creation only if:
// 1. Enterprise managed device > is allowed by policy.
// 2. Consumer device > owner exists.
// 3. New users are allowed by owner.
CrosSettings* cros_settings = CrosSettings::Get();
bool allow_new_user = false;
cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
bool managed_users_allowed =
UserManager::Get()->AreLocallyManagedUsersAllowed();
bool managed_users_can_create = true;
int message_id = -1;
if (delegate_->GetUsers().size() == 0) {
managed_users_can_create = false;
message_id = IDS_CREATE_LOCALLY_MANAGED_USER_NO_MANAGER_TEXT;
}
if (!allow_new_user) {
managed_users_can_create = false;
message_id = IDS_CREATE_LOCALLY_MANAGED_USER_CREATION_RESTRICTED_TEXT;
}
params->SetBoolean("managedUsersEnabled", managed_users_allowed);
params->SetBoolean("managedUsersCanCreate", managed_users_can_create);
if (!managed_users_can_create) {
params->SetString("managedUsersRestrictionReason",
l10n_util::GetStringUTF16(message_id));
}
}
void SigninScreenHandler::LoadAuthExtension(
bool force, bool silent_load, bool offline) {
DictionaryValue params;
params.SetBoolean("forceReload", force);
params.SetBoolean("silentLoad", silent_load);
params.SetBoolean("isLocal", offline);
params.SetBoolean("passwordChanged",
!email_.empty() && password_changed_for_.count(email_));
if (delegate_)
params.SetBoolean("isShowUsers", delegate_->IsShowUsers());
params.SetBoolean("useOffline", offline);
params.SetString("email", email_);
email_.clear();
UpdateAuthParams(&params);
if (!offline) {
const std::string app_locale = g_browser_process->GetApplicationLocale();
if (!app_locale.empty())
params.SetString("hl", app_locale);
} else {
base::DictionaryValue *localized_strings = new base::DictionaryValue();
localized_strings->SetString("stringEmail",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMAIL));
localized_strings->SetString("stringPassword",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_PASSWORD));
localized_strings->SetString("stringSignIn",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_SIGNIN));
localized_strings->SetString("stringEmptyEmail",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMPTY_EMAIL));
localized_strings->SetString("stringEmptyPassword",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMPTY_PASSWORD));
localized_strings->SetString("stringError",
l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_ERROR));
params.Set("localizedStrings", localized_strings);
}
const GURL gaia_url =
CommandLine::ForCurrentProcess()->HasSwitch(::switches::kGaiaUrl) ?
GURL(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
::switches::kGaiaUrl)) :
GaiaUrls::GetInstance()->gaia_url();
params.SetString("gaiaUrl", gaia_url.spec());
frame_state_ = FRAME_STATE_LOADING;
CallJS("login.GaiaSigninScreen.loadAuthExtension", params);
}
void SigninScreenHandler::UserSettingsChanged() {
UpdateAuthExtension();
UpdateAddButtonStatus();
}
void SigninScreenHandler::UpdateAuthExtension() {
DictionaryValue params;
UpdateAuthParams(&params);
CallJS("login.GaiaSigninScreen.updateAuthExtension", params);
}
void SigninScreenHandler::UpdateAddButtonStatus() {
CallJS("cr.ui.login.DisplayManager.updateAddUserButtonStatus",
AllWhitelistedUsersPresent());
}
void SigninScreenHandler::HandleCompleteLogin(const std::string& typed_email,
const std::string& password) {
if (!delegate_)
return;
const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
delegate_->SetDisplayEmail(sanitized_email);
delegate_->CompleteLogin(UserContext(sanitized_email,
password,
std::string())); // auth_code
if (test_expects_complete_login_) {
VLOG(2) << "Complete test login for " << typed_email
<< ", requested=" << test_user_;
test_expects_complete_login_ = false;
test_user_.clear();
test_pass_.clear();
}
}
void SigninScreenHandler::HandleCompleteAuthentication(
const std::string& email,
const std::string& password,
const std::string& auth_code) {
if (!delegate_)
return;
const std::string sanitized_email = gaia::SanitizeEmail(email);
delegate_->SetDisplayEmail(sanitized_email);
delegate_->CompleteLogin(UserContext(sanitized_email, password, auth_code));
}
void SigninScreenHandler::HandleAuthenticateUser(const std::string& username,
const std::string& password) {
if (!delegate_)
return;
delegate_->Login(UserContext(gaia::SanitizeEmail(username),
password,
std::string())); // auth_code
}
void SigninScreenHandler::HandleLaunchDemoUser() {
if (delegate_)
delegate_->LoginAsRetailModeUser();
}
void SigninScreenHandler::HandleLaunchIncognito() {
if (delegate_)
delegate_->LoginAsGuest();
}
void SigninScreenHandler::HandleShowLocallyManagedUserCreationScreen() {
if (!UserManager::Get()->AreLocallyManagedUsersAllowed()) {
LOG(ERROR) << "Managed users not allowed.";
return;
}
scoped_ptr<DictionaryValue> params(new DictionaryValue());
LoginDisplayHostImpl::default_host()->
StartWizard(WizardController::kLocallyManagedUserCreationScreenName,
params.Pass());
}
void SigninScreenHandler::HandleLaunchPublicAccount(
const std::string& username) {
if (delegate_)
delegate_->LoginAsPublicAccount(username);
}
void SigninScreenHandler::HandleOfflineLogin(const base::ListValue* args) {
if (!delegate_ || delegate_->IsShowUsers()) {
NOTREACHED();
return;
}
if (!args->GetString(0, &email_))
email_.clear();
// Load auth extension. Parameters are: force reload, do not load extension in
// background, use offline version.
LoadAuthExtension(true, false, true);
UpdateUIState(UI_STATE_GAIA_SIGNIN, NULL);
}
void SigninScreenHandler::HandleShutdownSystem() {
ash::Shell::GetInstance()->lock_state_controller()->RequestShutdown();
}
void SigninScreenHandler::HandleLoadWallpaper(const std::string& email) {
if (delegate_)
delegate_->LoadWallpaper(email);
}
void SigninScreenHandler::HandleRebootSystem() {
chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
}
void SigninScreenHandler::HandleRemoveUser(const std::string& email) {
if (!delegate_)
return;
delegate_->RemoveUser(email);
UpdateAddButtonStatus();
}
void SigninScreenHandler::HandleShowAddUser(const base::ListValue* args) {
email_.clear();
// |args| can be null if it's OOBE.
if (args)
args->GetString(0, &email_);
is_account_picker_showing_first_time_ = false;
if (gaia_silent_load_ && email_.empty()) {
dns_cleared_ = true;
cookies_cleared_ = true;
ShowSigninScreenIfReady();
} else {
StartClearingDnsCache();
StartClearingCookies(base::Bind(
&SigninScreenHandler::ShowSigninScreenIfReady,
weak_factory_.GetWeakPtr()));
}
}
void SigninScreenHandler::HandleToggleEnrollmentScreen() {
if (delegate_)
delegate_->ShowEnterpriseEnrollmentScreen();
}
void SigninScreenHandler::HandleToggleKioskEnableScreen() {
if (delegate_ &&
!wait_for_auto_enrollment_check_ &&
!g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
wait_for_auto_enrollment_check_ = true;
LoginDisplayHostImpl::default_host()->GetAutoEnrollmentCheckResult(
base::Bind(&SigninScreenHandler::ContinueKioskEnableFlow,
weak_factory_.GetWeakPtr()));
}
}
void SigninScreenHandler::HandleToggleResetScreen() {
if (delegate_ &&
!g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
delegate_->ShowResetScreen();
}
}
void SigninScreenHandler::HandleToggleKioskAutolaunchScreen() {
if (delegate_ &&
!g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
delegate_->ShowKioskAutolaunchScreen();
}
}
void SigninScreenHandler::HandleLaunchHelpApp(double help_topic_id) {
if (!delegate_)
return;
if (!help_app_.get())
help_app_ = new HelpAppLauncher(GetNativeWindow());
help_app_->ShowHelpTopic(
static_cast<HelpAppLauncher::HelpTopic>(help_topic_id));
}
void SigninScreenHandler::FillUserDictionary(User* user,
bool is_owner,
DictionaryValue* user_dict) {
const std::string& email = user->email();
bool is_public_account =
user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT;
bool is_locally_managed_user =
user->GetType() == User::USER_TYPE_LOCALLY_MANAGED;
User::OAuthTokenStatus token_status = user->oauth_token_status();
// If supervised user has unknown token status consider that as valid token.
// It will be invalidated inside session in case it has been revoked.
if (is_locally_managed_user &&
token_status == User::OAUTH_TOKEN_STATUS_UNKNOWN) {
token_status = User::OAUTH2_TOKEN_STATUS_VALID;
}
user_dict->SetString(kKeyUsername, email);
user_dict->SetString(kKeyEmailAddress, user->display_email());
user_dict->SetString(kKeyDisplayName, user->GetDisplayName());
user_dict->SetBoolean(kKeyPublicAccount, is_public_account);
user_dict->SetBoolean(kKeyLocallyManagedUser, is_locally_managed_user);
user_dict->SetInteger(kKeyOauthTokenStatus, token_status);
user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
user_dict->SetBoolean(kKeyIsOwner, is_owner);
if (is_public_account) {
policy::BrowserPolicyConnector* policy_connector =
g_browser_process->browser_policy_connector();
if (policy_connector->IsEnterpriseManaged()) {
user_dict->SetString(kKeyEnterpriseDomain,
policy_connector->GetEnterpriseDomain());
}
}
}
void SigninScreenHandler::SendUserList(bool animated) {
if (!delegate_)
return;
size_t max_non_owner_users = kMaxUsers - 1;
size_t non_owner_count = 0;
ListValue users_list;
const UserList& users = delegate_->GetUsers();
// TODO(nkostylev): Show optional intro dialog about multi-profiles feature
// based on user preferences. http://crbug.com/230862
// TODO(nkostylev): Move to a separate method in UserManager.
// http://crbug.com/230852
bool is_signin_to_add = LoginDisplayHostImpl::default_host() &&
UserManager::Get()->IsUserLoggedIn();
bool single_user = users.size() == 1;
for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
const std::string& email = (*it)->email();
std::string owner;
chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
bool is_owner = (email == owner);
if (non_owner_count < max_non_owner_users || is_owner) {
DictionaryValue* user_dict = new DictionaryValue();
FillUserDictionary(*it, is_owner, user_dict);
bool is_public_account =
((*it)->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT);
bool signed_in = (*it)->is_logged_in();
// Single user check here is necessary because owner info might not be
// available when running into login screen on first boot.
// See http://crosbug.com/12723
user_dict->SetBoolean(kKeyCanRemove,
!single_user &&
!email.empty() &&
!is_owner &&
!is_public_account &&
!signed_in &&
!is_signin_to_add);
users_list.Append(user_dict);
if (!is_owner)
++non_owner_count;
}
}
CallJS("login.AccountPickerScreen.loadUsers", users_list, animated,
delegate_->IsShowGuest());
}
void SigninScreenHandler::HandleAccountPickerReady() {
LOG(INFO) << "Login WebUI >> AccountPickerReady";
if (delegate_ && !ScreenLocker::default_screen_locker() &&
!chromeos::IsMachineHWIDCorrect() &&
!oobe_ui_) {
delegate_->ShowWrongHWIDScreen();
return;
}
PrefService* prefs = g_browser_process->local_state();
if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
prefs->SetBoolean(prefs::kFactoryResetRequested, false);
prefs->CommitPendingWrite();
HandleToggleResetScreen();
return;
}
is_account_picker_showing_first_time_ = true;
MaybePreloadAuthExtension();
if (ScreenLocker::default_screen_locker())
ScreenLocker::default_screen_locker()->delegate()->OnLockWebUIReady();
if (delegate_)
delegate_->OnSigninScreenReady();
}
void SigninScreenHandler::HandleWallpaperReady() {
if (ScreenLocker::default_screen_locker()) {
ScreenLocker::default_screen_locker()->delegate()->
OnLockBackgroundDisplayed();
}
}
void SigninScreenHandler::HandleLoginWebuiReady() {
if (focus_stolen_) {
// Set focus to the Gaia page.
// TODO(altimofeev): temporary solution, until focus parameters are
// implemented on the Gaia side.
// Do this only once. Any subsequent call would relod GAIA frame.
focus_stolen_ = false;
const char code[] = "gWindowOnLoad();";
RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
rvh->ExecuteJavascriptInWebFrame(
ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
ASCIIToUTF16(code));
}
if (!gaia_silent_load_) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_LOGIN_WEBUI_LOADED,
content::NotificationService::AllSources(),
content::NotificationService::NoDetails());
} else {
focus_stolen_ = true;
// Prevent focus stealing by the Gaia page.
// TODO(altimofeev): temporary solution, until focus parameters are
// implemented on the Gaia side.
const char code[] = "var gWindowOnLoad = window.onload; "
"window.onload=function() {};";
RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
rvh->ExecuteJavascriptInWebFrame(
ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
ASCIIToUTF16(code));
// As we could miss and window.onload could already be called, restore
// focus to current pod (see crbug/175243).
RefocusCurrentPod();
}
HandleFrameLoadingCompleted(0);
if (test_expects_complete_login_)
SubmitLoginFormForTest();
}
void SigninScreenHandler::HandleSignOutUser() {
if (delegate_)
delegate_->Signout();
}
void SigninScreenHandler::HandleNetworkErrorShown() {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
content::NotificationService::AllSources(),
content::NotificationService::NoDetails());
}
void SigninScreenHandler::HandleCreateAccount() {
if (delegate_)
delegate_->CreateAccount();
}
void SigninScreenHandler::HandleOpenProxySettings() {
LoginDisplayHostImpl::default_host()->OpenProxySettings();
}
void SigninScreenHandler::HandleLoginVisible(const std::string& source) {
LOG(WARNING) << "Login WebUI >> loginVisible, src: " << source << ", "
<< "webui_visible_: " << webui_visible_;
if (!webui_visible_) {
// There might be multiple messages from OOBE UI so send notifications after
// the first one only.
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
content::NotificationService::AllSources(),
content::NotificationService::NoDetails());
TRACE_EVENT_ASYNC_END0(
"ui", "ShowLoginWebUI", LoginDisplayHostImpl::kShowLoginWebUIid);
}
webui_visible_ = true;
if (preferences_changed_delayed_)
OnPreferencesChanged();
}
void SigninScreenHandler::HandleCancelPasswordChangedFlow() {
StartClearingCookies(base::Bind(
&SigninScreenHandler::CancelPasswordChangedFlowInternal,
weak_factory_.GetWeakPtr()));
}
void SigninScreenHandler::HandleCancelUserAdding() {
if (delegate_)
delegate_->CancelUserAdding();
}
void SigninScreenHandler::HandleMigrateUserData(
const std::string& old_password) {
if (delegate_)
delegate_->MigrateUserData(old_password);
}
void SigninScreenHandler::HandleResyncUserData() {
if (delegate_)
delegate_->ResyncUserData();
}
void SigninScreenHandler::HandleLoginUIStateChanged(const std::string& source,
bool new_value) {
LOG(INFO) << "Login WebUI >> active: " << new_value << ", "
<< "source: " << source;
if (!KioskAppManager::Get()->GetAutoLaunchApp().empty() &&
KioskAppManager::Get()->IsAutoLaunchRequested()) {
LOG(INFO) << "Showing auto-launch warning";
// On slow devices, the wallpaper animation is not shown initially, so we
// must explicitly load the wallpaper. This is also the case for the
// account-picker and gaia-signin UI states.
delegate_->LoadSigninWallpaper();
HandleToggleKioskAutolaunchScreen();
return;
}
if (source == kSourceGaiaSignin) {
ui_state_ = UI_STATE_GAIA_SIGNIN;
} else if (source == kSourceAccountPicker) {
ui_state_ = UI_STATE_ACCOUNT_PICKER;
} else {
NOTREACHED();
return;
}
}
void SigninScreenHandler::HandleUnlockOnLoginSuccess() {
DCHECK(UserManager::Get()->IsUserLoggedIn());
if (ScreenLocker::default_screen_locker())
ScreenLocker::default_screen_locker()->UnlockOnLoginSuccess();
}
void SigninScreenHandler::HandleFrameLoadingCompleted(int status) {
const net::Error frame_error = static_cast<net::Error>(-status);
if (frame_error == net::ERR_ABORTED) {
LOG(WARNING) << "Ignore gaia frame error: " << frame_error;
return;
}
frame_error_ = frame_error;
if (frame_error == net::OK) {
LOG(INFO) << "Gaia frame is loaded";
frame_state_ = FRAME_STATE_LOADED;
} else {
LOG(WARNING) << "Gaia frame error: " << frame_error_;
frame_state_ = FRAME_STATE_ERROR;
}
if (network_state_informer_->state() != NetworkStateInformer::ONLINE)
return;
if (frame_state_ == FRAME_STATE_LOADED)
UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
else if (frame_state_ == FRAME_STATE_ERROR)
UpdateState(ErrorScreenActor::ERROR_REASON_FRAME_ERROR);
}
void SigninScreenHandler::HandleShowLoadingTimeoutError() {
UpdateState(ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
}
void SigninScreenHandler::HandleUpdateOfflineLogin(bool offline_login_active) {
offline_login_active_ = offline_login_active;
}
void SigninScreenHandler::HandleFocusPod(const std::string& user_id) {
SetUserInputMethod(user_id);
}
void SigninScreenHandler::HandleLaunchKioskApp(const std::string& app_id) {
delegate_->LoginAsKioskApp(app_id);
}
void SigninScreenHandler::StartClearingDnsCache() {
if (dns_clear_task_running_ || !g_browser_process->io_thread())
return;
dns_cleared_ = false;
BrowserThread::PostTaskAndReply(
BrowserThread::IO, FROM_HERE,
base::Bind(&ClearDnsCache, g_browser_process->io_thread()),
base::Bind(&SigninScreenHandler::OnDnsCleared,
weak_factory_.GetWeakPtr()));
dns_clear_task_running_ = true;
}
void SigninScreenHandler::StartClearingCookies(
const base::Closure& on_clear_callback) {
cookies_cleared_ = false;
ProfileHelper* profile_helper =
g_browser_process->platform_part()->profile_helper();
LOG_ASSERT(
Profile::FromWebUI(web_ui()) == profile_helper->GetSigninProfile());
profile_helper->ClearSigninProfile(base::Bind(
&SigninScreenHandler::OnCookiesCleared,
weak_factory_.GetWeakPtr(), on_clear_callback));
}
void SigninScreenHandler::MaybePreloadAuthExtension() {
// Fetching of the extension is not started before account picker page is
// loaded because it can affect the loading speed. Also if cookies clearing
// was initiated or |dns_clear_task_running_| then auth extension showing has
// already been initiated and preloading is senseless.
// Do not load the extension for the screen locker, see crosbug.com/25018.
if (is_account_picker_showing_first_time_ &&
!gaia_silent_load_ &&
!ScreenLocker::default_screen_locker() &&
!cookies_cleared_ &&
!dns_clear_task_running_ &&
network_state_informer_->state() == NetworkStateInformer::ONLINE) {
gaia_silent_load_ = true;
gaia_silent_load_network_ = network_state_informer_->network_path();
LoadAuthExtension(true, true, false);
}
}
bool SigninScreenHandler::AllWhitelistedUsersPresent() {
CrosSettings* cros_settings = CrosSettings::Get();
bool allow_new_user = false;
cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
if (allow_new_user)
return false;
UserManager* user_manager = UserManager::Get();
const UserList& users = user_manager->GetUsers();
if (!delegate_ || users.size() > kMaxUsers) {
return false;
}
const base::ListValue* whitelist = NULL;
if (!cros_settings->GetList(kAccountsPrefUsers, &whitelist) || !whitelist)
return false;
for (size_t i = 0; i < whitelist->GetSize(); ++i) {
std::string whitelisted_user;
// NB: Wildcards in the whitelist are also detected as not present here.
if (!whitelist->GetString(i, &whitelisted_user) ||
!user_manager->IsKnownUser(whitelisted_user)) {
return false;
}
}
return true;
}
void SigninScreenHandler::CancelPasswordChangedFlowInternal() {
if (delegate_) {
Show(oobe_ui_);
delegate_->CancelPasswordChangedFlow();
}
}
OobeUI::Screen SigninScreenHandler::GetCurrentScreen() const {
OobeUI::Screen screen = OobeUI::SCREEN_UNKNOWN;
OobeUI* oobe_ui = static_cast<OobeUI*>(web_ui()->GetController());
if (oobe_ui)
screen = oobe_ui->current_screen();
return screen;
}
bool SigninScreenHandler::IsGaiaVisible() const {
return IsSigninScreen(GetCurrentScreen()) &&
ui_state_ == UI_STATE_GAIA_SIGNIN;
}
bool SigninScreenHandler::IsGaiaHiddenByError() const {
return IsSigninScreenHiddenByError() &&
ui_state_ == UI_STATE_GAIA_SIGNIN;
}
bool SigninScreenHandler::IsSigninScreenHiddenByError() const {
return (GetCurrentScreen() == OobeUI::SCREEN_ERROR_MESSAGE) &&
(IsSigninScreen(error_screen_actor_->parent_screen()));
}
bool SigninScreenHandler::IsGuestSigninAllowed() const {
CrosSettings* cros_settings = CrosSettings::Get();
if (!cros_settings)
return false;
bool allow_guest;
cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
return allow_guest;
}
bool SigninScreenHandler::IsOfflineLoginAllowed() const {
CrosSettings* cros_settings = CrosSettings::Get();
if (!cros_settings)
return false;
// Offline login is allowed only when user pods are hidden.
bool show_pods;
cros_settings->GetBoolean(kAccountsPrefShowUserNamesOnSignIn, &show_pods);
return !show_pods;
}
void SigninScreenHandler::SubmitLoginFormForTest() {
VLOG(2) << "Submit login form for test, user=" << test_user_;
std::string code;
code += "document.getElementById('Email').value = '" + test_user_ + "';";
code += "document.getElementById('Passwd').value = '" + test_pass_ + "';";
code += "document.getElementById('signIn').click();";
RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
rvh->ExecuteJavascriptInWebFrame(
ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
ASCIIToUTF16(code));
// Test properties are cleared in HandleCompleteLogin because the form
// submission might fail and login will not be attempted after reloading
// if they are cleared here.
}
void SigninScreenHandler::ContinueKioskEnableFlow(bool should_auto_enroll) {
wait_for_auto_enrollment_check_ = false;
// Do not proceed with kiosk enable when auto enroll will be enforced.
// TODO(xiyuan): Add an error UI feedkback so user knows what happens.
if (should_auto_enroll) {
LOG(WARNING) << "Kiosk enable flow aborted because auto enrollment is "
"going to be enforced.";
if (!kiosk_enable_flow_aborted_callback_for_test_.is_null())
kiosk_enable_flow_aborted_callback_for_test_.Run();
return;
}
if (delegate_)
delegate_->ShowKioskEnableScreen();
}
} // namespace chromeos