Commit e627fd19 authored by tbarzic@chromium.org's avatar tbarzic@chromium.org

Easy Unlock: Add support for hard locking

When user clicks on the easy-unlock icon on "Click to enter" lock screen
(i.e. when lock screen auth type is USER_CLICK), the lock screen changes to
state in which password is required to unlock the screen.
This adds new auth state FORCE_OFFLINE_PASSWORD which, unlike OFFLINE_PASSWORD,
can't be changed to any other.

BUG=397363
TBR=asargent@chromium.org

Review URL: https://codereview.chromium.org/456943003

Cr-Commit-Position: refs/heads/master@{#290096}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290096 0039d316-1c4b-4281-b951-d872f2087c98
parent ecb4dc3a
...@@ -362,6 +362,9 @@ void UserSelectionScreen::HandleGetUsers() { ...@@ -362,6 +362,9 @@ void UserSelectionScreen::HandleGetUsers() {
void UserSelectionScreen::SetAuthType( void UserSelectionScreen::SetAuthType(
const std::string& username, const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type) { ScreenlockBridge::LockHandler::AuthType auth_type) {
DCHECK(GetAuthType(username) !=
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD ||
auth_type == ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD);
user_auth_type_map_[username] = auth_type; user_auth_type_map_[username] = auth_type;
} }
......
...@@ -57,6 +57,8 @@ screenlock::AuthType FromLockHandlerAuthType( ...@@ -57,6 +57,8 @@ screenlock::AuthType FromLockHandlerAuthType(
// locking. // locking.
NOTREACHED(); NOTREACHED();
return screenlock::AUTH_TYPE_NONE; return screenlock::AUTH_TYPE_NONE;
case ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD:
return screenlock::AUTH_TYPE_OFFLINEPASSWORD;
} }
NOTREACHED(); NOTREACHED();
return screenlock::AUTH_TYPE_OFFLINEPASSWORD; return screenlock::AUTH_TYPE_OFFLINEPASSWORD;
......
...@@ -49,6 +49,10 @@ bool HasAnimation(EasyUnlockScreenlockStateHandler::State state) { ...@@ -49,6 +49,10 @@ bool HasAnimation(EasyUnlockScreenlockStateHandler::State state) {
return state == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING; return state == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING;
} }
bool HardlockOnClick(EasyUnlockScreenlockStateHandler::State state) {
return state == EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED;
}
size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) { size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) {
switch (state) { switch (state) {
case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH: case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
...@@ -64,9 +68,7 @@ size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) { ...@@ -64,9 +68,7 @@ size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) {
case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY: case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_NEARBY; return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_NEARBY;
case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED: case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
// TODO(tbarzic): When hard lock is enabled change this to return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS;
// IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS.
return 0;
default: default:
return 0; return 0;
} }
...@@ -99,10 +101,13 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) { ...@@ -99,10 +101,13 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) {
state_ = new_state; state_ = new_state;
// If lock screen is not active, just cache the current state. // If lock screen is not active or it forces offline password, just cache the
// The screenlock state will get refreshed in |ScreenDidLock|. // current state. The screenlock state will get refreshed in |ScreenDidLock|.
if (!screenlock_bridge_->IsLocked()) if (!screenlock_bridge_->IsLocked() ||
screenlock_bridge_->lock_handler()->GetAuthType(user_email_) ==
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD) {
return; return;
}
UpdateScreenlockAuthType(); UpdateScreenlockAuthType();
...@@ -115,7 +120,9 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) { ...@@ -115,7 +120,9 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) {
} }
icon_options.SetIconAsResourceURL(icon_url); icon_options.SetIconAsResourceURL(icon_url);
UpdateTooltipOptions(&icon_options); bool trial_run = IsTrialRun();
UpdateTooltipOptions(trial_run, &icon_options);
if (UseOpaqueIcon(state_)) if (UseOpaqueIcon(state_))
icon_options.SetOpacity(kOpaqueIconOpacity); icon_options.SetOpacity(kOpaqueIconOpacity);
...@@ -125,6 +132,13 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) { ...@@ -125,6 +132,13 @@ void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) {
if (HasAnimation(state_)) if (HasAnimation(state_))
icon_options.SetAnimation(kSpinnerResourceWidth, kSpinnerIntervalMs); icon_options.SetAnimation(kSpinnerResourceWidth, kSpinnerIntervalMs);
// Hardlocking is disabled in trial run.
if (!trial_run && HardlockOnClick(state_))
icon_options.SetHardlockOnClick();
if (trial_run && state_ == STATE_AUTHENTICATED)
MarkTrialRunComplete();
screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_, screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
icon_options); icon_options);
} }
...@@ -140,12 +154,11 @@ void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock() { ...@@ -140,12 +154,11 @@ void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock() {
} }
void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions( void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
bool trial_run,
ScreenlockBridge::UserPodCustomIconOptions* icon_options) { ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
bool show_tutorial = ShouldShowTutorial();
size_t resource_id = 0; size_t resource_id = 0;
base::string16 device_name; base::string16 device_name;
if (show_tutorial) { if (trial_run && state_ == STATE_AUTHENTICATED) {
resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TUTORIAL; resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TUTORIAL;
} else { } else {
resource_id = GetTooltipResourceId(state_); resource_id = GetTooltipResourceId(state_);
...@@ -166,20 +179,17 @@ void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions( ...@@ -166,20 +179,17 @@ void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
if (tooltip.empty()) if (tooltip.empty())
return; return;
if (show_tutorial) icon_options->SetTooltip(
MarkTutorialShown(); tooltip,
state_ == STATE_AUTHENTICATED && trial_run /* autoshow tooltip */);
icon_options->SetTooltip(tooltip, show_tutorial /* autoshow tooltip */);
} }
bool EasyUnlockScreenlockStateHandler::ShouldShowTutorial() { bool EasyUnlockScreenlockStateHandler::IsTrialRun() {
if (state_ != STATE_AUTHENTICATED)
return false;
return pref_service_ && return pref_service_ &&
pref_service_->GetBoolean(prefs::kEasyUnlockShowTutorial); pref_service_->GetBoolean(prefs::kEasyUnlockShowTutorial);
} }
void EasyUnlockScreenlockStateHandler::MarkTutorialShown() { void EasyUnlockScreenlockStateHandler::MarkTrialRunComplete() {
if (!pref_service_) if (!pref_service_)
return; return;
pref_service_->SetBoolean(prefs::kEasyUnlockShowTutorial, false); pref_service_->SetBoolean(prefs::kEasyUnlockShowTutorial, false);
...@@ -195,6 +205,10 @@ base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() { ...@@ -195,6 +205,10 @@ base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() {
} }
void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() { void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
if (screenlock_bridge_->lock_handler()->GetAuthType(user_email_) ==
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
return;
if (state_ == STATE_AUTHENTICATED) { if (state_ == STATE_AUTHENTICATED) {
screenlock_bridge_->lock_handler()->SetAuthType( screenlock_bridge_->lock_handler()->SetAuthType(
user_email_, user_email_,
......
...@@ -61,17 +61,20 @@ class EasyUnlockScreenlockStateHandler : public ScreenlockBridge::Observer { ...@@ -61,17 +61,20 @@ class EasyUnlockScreenlockStateHandler : public ScreenlockBridge::Observer {
virtual void OnScreenDidLock() OVERRIDE; virtual void OnScreenDidLock() OVERRIDE;
virtual void OnScreenDidUnlock() OVERRIDE; virtual void OnScreenDidUnlock() OVERRIDE;
// Updates icon's tooltip options.
// |trial_run|: Whether the trial Easy Unlock run is in progress.
void UpdateTooltipOptions( void UpdateTooltipOptions(
bool trial_run,
ScreenlockBridge::UserPodCustomIconOptions* icon_options); ScreenlockBridge::UserPodCustomIconOptions* icon_options);
// Whether the tutorial message should be shown to the user. The message is // Whether this is the first, trial Easy Unlock run. If this is the case, a
// shown only once, when the user encounters STATE_AUTHENTICATED for the first // tutorial message should be shown and hard-locking be disabled in
// time (across sessions). After the tutorial message is shown, // Authenticated state. The trial run will be active if Easy Unlock never
// |MarkTutorialShown| should be called to prevent further tutorial message. // entered Authenticated state (across sessions).
bool ShouldShowTutorial(); bool IsTrialRun();
// Sets user preference that prevents showing of tutorial messages. // Sets user preference that marks trial run completed.
void MarkTutorialShown(); void MarkTrialRunComplete();
// Gets the name to be used for the device. The name depends on the device // Gets the name to be used for the device. The name depends on the device
// type (example values: Chromebook and Chromebox). // type (example values: Chromebook and Chromebox).
......
...@@ -37,7 +37,8 @@ ScreenlockBridge::UserPodCustomIconOptions::UserPodCustomIconOptions() ...@@ -37,7 +37,8 @@ ScreenlockBridge::UserPodCustomIconOptions::UserPodCustomIconOptions()
animation_resource_width_(0u), animation_resource_width_(0u),
animation_frame_length_ms_(0u), animation_frame_length_ms_(0u),
opacity_(100u), opacity_(100u),
autoshow_tooltip_(false) { autoshow_tooltip_(false),
hardlock_on_click_(false) {
} }
ScreenlockBridge::UserPodCustomIconOptions::~UserPodCustomIconOptions() {} ScreenlockBridge::UserPodCustomIconOptions::~UserPodCustomIconOptions() {}
...@@ -86,6 +87,10 @@ ScreenlockBridge::UserPodCustomIconOptions::ToDictionaryValue() const { ...@@ -86,6 +87,10 @@ ScreenlockBridge::UserPodCustomIconOptions::ToDictionaryValue() const {
animation_frame_length_ms_); animation_frame_length_ms_);
result->Set("animation", animation); result->Set("animation", animation);
} }
if (hardlock_on_click_)
result->SetBoolean("hardlockOnClick", true);
return result.Pass(); return result.Pass();
} }
...@@ -130,6 +135,10 @@ void ScreenlockBridge::UserPodCustomIconOptions::SetTooltip( ...@@ -130,6 +135,10 @@ void ScreenlockBridge::UserPodCustomIconOptions::SetTooltip(
autoshow_tooltip_ = autoshow; autoshow_tooltip_ = autoshow;
} }
void ScreenlockBridge::UserPodCustomIconOptions::SetHardlockOnClick() {
hardlock_on_click_ = true;
}
// static // static
std::string ScreenlockBridge::GetAuthenticatedUserEmail(Profile* profile) { std::string ScreenlockBridge::GetAuthenticatedUserEmail(Profile* profile) {
// |profile| has to be a signed-in profile with SigninManager already // |profile| has to be a signed-in profile with SigninManager already
......
...@@ -78,6 +78,10 @@ class ScreenlockBridge { ...@@ -78,6 +78,10 @@ class ScreenlockBridge {
// shown with the icon. // shown with the icon.
void SetTooltip(const base::string16& tooltip, bool autoshow); void SetTooltip(const base::string16& tooltip, bool autoshow);
// If hardlock on click is set, clicking the icon in the screenlock will
// go to state where password is required for unlock.
void SetHardlockOnClick();
private: private:
std::string icon_resource_url_; std::string icon_resource_url_;
scoped_ptr<gfx::Image> icon_image_; scoped_ptr<gfx::Image> icon_image_;
...@@ -95,6 +99,8 @@ class ScreenlockBridge { ...@@ -95,6 +99,8 @@ class ScreenlockBridge {
base::string16 tooltip_; base::string16 tooltip_;
bool autoshow_tooltip_; bool autoshow_tooltip_;
bool hardlock_on_click_;
DISALLOW_COPY_AND_ASSIGN(UserPodCustomIconOptions); DISALLOW_COPY_AND_ASSIGN(UserPodCustomIconOptions);
}; };
...@@ -108,6 +114,7 @@ class ScreenlockBridge { ...@@ -108,6 +114,7 @@ class ScreenlockBridge {
NUMERIC_PIN = 2, NUMERIC_PIN = 2,
USER_CLICK = 3, USER_CLICK = 3,
EXPAND_THEN_USER_CLICK = 4, EXPAND_THEN_USER_CLICK = 4,
FORCE_OFFLINE_PASSWORD = 5
}; };
// Displays |message| in a banner on the lock screen. // Displays |message| in a banner on the lock screen.
......
...@@ -764,6 +764,7 @@ void SigninScreenHandler::RegisterMessages() { ...@@ -764,6 +764,7 @@ void SigninScreenHandler::RegisterMessages() {
AddCallback("updateOfflineLogin", AddCallback("updateOfflineLogin",
&SigninScreenHandler::HandleUpdateOfflineLogin); &SigninScreenHandler::HandleUpdateOfflineLogin);
AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod); AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
AddCallback("hardlockPod", &SigninScreenHandler::HandleHardlockPod);
AddCallback("retrieveAuthenticatedUserEmail", AddCallback("retrieveAuthenticatedUserEmail",
&SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail); &SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail);
AddCallback("getPublicSessionKeyboardLayouts", AddCallback("getPublicSessionKeyboardLayouts",
...@@ -948,6 +949,10 @@ void SigninScreenHandler::SetAuthType( ...@@ -948,6 +949,10 @@ void SigninScreenHandler::SetAuthType(
const std::string& username, const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type, ScreenlockBridge::LockHandler::AuthType auth_type,
const base::string16& initial_value) { const base::string16& initial_value) {
if (delegate_->GetAuthType(username) ==
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
return;
delegate_->SetAuthType(username, auth_type); delegate_->SetAuthType(username, auth_type);
CallJS("login.AccountPickerScreen.setAuthType", CallJS("login.AccountPickerScreen.setAuthType",
...@@ -1304,6 +1309,13 @@ void SigninScreenHandler::HandleFocusPod(const std::string& user_id) { ...@@ -1304,6 +1309,13 @@ void SigninScreenHandler::HandleFocusPod(const std::string& user_id) {
WallpaperManager::Get()->SetUserWallpaperDelayed(user_id); WallpaperManager::Get()->SetUserWallpaperDelayed(user_id);
} }
void SigninScreenHandler::HandleHardlockPod(const std::string& user_id) {
SetAuthType(user_id,
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD,
base::string16());
HideUserPodCustomIcon(user_id);
}
void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail( void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail(
double attempt_token) { double attempt_token) {
// TODO(antrim) : move GaiaSigninScreen dependency to GaiaSigninScreen. // TODO(antrim) : move GaiaSigninScreen dependency to GaiaSigninScreen.
......
...@@ -370,6 +370,7 @@ class SigninScreenHandler ...@@ -370,6 +370,7 @@ class SigninScreenHandler
void HandleUpdateOfflineLogin(bool offline_login_active); void HandleUpdateOfflineLogin(bool offline_login_active);
void HandleShowSupervisedUserCreationScreen(); void HandleShowSupervisedUserCreationScreen();
void HandleFocusPod(const std::string& user_id); void HandleFocusPod(const std::string& user_id);
void HandleHardlockPod(const std::string& user_id);
void HandleLaunchKioskApp(const std::string& app_id, bool diagnostic_mode); void HandleLaunchKioskApp(const std::string& app_id, bool diagnostic_mode);
void HandleRetrieveAuthenticatedUserEmail(double attempt_token); void HandleRetrieveAuthenticatedUserEmail(double attempt_token);
void HandleGetPublicSessionKeyboardLayouts(const std::string& user_id, void HandleGetPublicSessionKeyboardLayouts(const std::string& user_id,
......
...@@ -257,6 +257,10 @@ void UserManagerScreenHandler::SetAuthType( ...@@ -257,6 +257,10 @@ void UserManagerScreenHandler::SetAuthType(
const std::string& user_email, const std::string& user_email,
ScreenlockBridge::LockHandler::AuthType auth_type, ScreenlockBridge::LockHandler::AuthType auth_type,
const base::string16& auth_value) { const base::string16& auth_value) {
if (GetAuthType(user_email) ==
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
return;
user_auth_type_map_[user_email] = auth_type; user_auth_type_map_[user_email] = auth_type;
web_ui()->CallJavascriptFunction( web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.setAuthType", "login.AccountPickerScreen.setAuthType",
...@@ -430,6 +434,16 @@ void UserManagerScreenHandler::HandleAttemptUnlock( ...@@ -430,6 +434,16 @@ void UserManagerScreenHandler::HandleAttemptUnlock(
GetScreenlockRouter(email)->OnAuthAttempted(GetAuthType(email), ""); GetScreenlockRouter(email)->OnAuthAttempted(GetAuthType(email), "");
} }
void UserManagerScreenHandler::HandleHardlockUserPod(
const base::ListValue* args) {
std::string email;
CHECK(args->GetString(0, &email));
SetAuthType(email,
ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD,
base::string16());
HideUserPodCustomIcon(email);
}
void UserManagerScreenHandler::OnClientLoginSuccess( void UserManagerScreenHandler::OnClientLoginSuccess(
const ClientLoginResult& result) { const ClientLoginResult& result) {
chrome::SetLocalAuthCredentials(authenticating_profile_index_, chrome::SetLocalAuthCredentials(authenticating_profile_index_,
......
...@@ -63,6 +63,7 @@ class UserManagerScreenHandler : public content::WebUIMessageHandler, ...@@ -63,6 +63,7 @@ class UserManagerScreenHandler : public content::WebUIMessageHandler,
void HandleLaunchUser(const base::ListValue* args); void HandleLaunchUser(const base::ListValue* args);
void HandleRemoveUser(const base::ListValue* args); void HandleRemoveUser(const base::ListValue* args);
void HandleAttemptUnlock(const base::ListValue* args); void HandleAttemptUnlock(const base::ListValue* args);
void HandleHardlockUserPod(const base::ListValue* args);
// Handle GAIA auth results. // Handle GAIA auth results.
virtual void OnClientLoginSuccess(const ClientLoginResult& result) OVERRIDE; virtual void OnClientLoginSuccess(const ClientLoginResult& result) OVERRIDE;
......
...@@ -171,7 +171,8 @@ html[dir=rtl] .main-pane { ...@@ -171,7 +171,8 @@ html[dir=rtl] .main-pane {
display: none; display: none;
} }
.pod[auth-type='offlinePassword'].focused .password-entry-container { .pod[auth-type='offlinePassword'].focused .password-entry-container,
.pod[auth-type='forceOfflinePassword'].focused .password-entry-container {
display: flex; display: flex;
flex: auto; flex: auto;
} }
...@@ -226,6 +227,11 @@ html[dir=rtl] .main-pane { ...@@ -226,6 +227,11 @@ html[dir=rtl] .main-pane {
flex: none; flex: none;
} }
.custom-icon.faded {
-webkit-transition: opacity 200ms ease-in-out,
visibility 200ms ease-in-out;
}
.custom-icon-container { .custom-icon-container {
display: flex; display: flex;
flex: none; flex: none;
......
...@@ -85,10 +85,11 @@ cr.define('login', function() { ...@@ -85,10 +85,11 @@ cr.define('login', function() {
* @const * @const
*/ */
var UserPodTabOrder = { var UserPodTabOrder = {
POD_INPUT: 1, // Password input fields (and whole pods themselves). POD_INPUT: 1, // Password input fields (and whole pods themselves).
HEADER_BAR: 2, // Buttons on the header bar (Shutdown, Add User). POD_CUSTOM_ICON: 2, // Pod custom icon next to passwrod input field.
ACTION_BOX: 3, // Action box buttons. HEADER_BAR: 3, // Buttons on the header bar (Shutdown, Add User).
PAD_MENU_ITEM: 4 // User pad menu items (Remove this user). ACTION_BOX: 4, // Action box buttons.
PAD_MENU_ITEM: 5 // User pad menu items (Remove this user).
}; };
/** /**
...@@ -103,6 +104,7 @@ cr.define('login', function() { ...@@ -103,6 +104,7 @@ cr.define('login', function() {
NUMERIC_PIN: 2, NUMERIC_PIN: 2,
USER_CLICK: 3, USER_CLICK: 3,
EXPAND_THEN_USER_CLICK: 4, EXPAND_THEN_USER_CLICK: 4,
FORCE_OFFLINE_PASSWORD: 5
}; };
/** /**
...@@ -114,6 +116,7 @@ cr.define('login', function() { ...@@ -114,6 +116,7 @@ cr.define('login', function() {
2: 'numericPin', 2: 'numericPin',
3: 'userClick', 3: 'userClick',
4: 'expandThenUserClick', 4: 'expandThenUserClick',
5: 'forceOfflinePassword'
}; };
// Focus and tab order are organized as follows: // Focus and tab order are organized as follows:
...@@ -121,9 +124,12 @@ cr.define('login', function() { ...@@ -121,9 +124,12 @@ cr.define('login', function() {
// (1) all user pods have tab index 1 so they are traversed first; // (1) all user pods have tab index 1 so they are traversed first;
// (2) when a user pod is activated, its tab index is set to -1 and its // (2) when a user pod is activated, its tab index is set to -1 and its
// main input field gets focus and tab index 1; // main input field gets focus and tab index 1;
// (3) buttons on the header bar have tab index 2 so they follow user pods; // (3) if user pod custom icon is interactive, it has tab index 2 so it
// (4) Action box buttons have tab index 3 and follow header bar buttons; // follows the input.
// (5) lastly, focus jumps to the Status Area and back to user pods. // (4) buttons on the header bar have tab index 3 so they follow the custom
// icon, or user pod if custom icon is not interactive;
// (5) Action box buttons have tab index 4 and follow header bar buttons;
// (6) lastly, focus jumps to the Status Area and back to user pods.
// //
// 'Focus' event is handled by a capture handler for the whole document // 'Focus' event is handled by a capture handler for the whole document
// and in some cases 'mousedown' event handlers are used instead of 'click' // and in some cases 'mousedown' event handlers are used instead of 'click'
...@@ -252,6 +258,23 @@ cr.define('login', function() { ...@@ -252,6 +258,23 @@ cr.define('login', function() {
*/ */
animationResourceSize_: 0, animationResourceSize_: 0,
/**
* When {@code fadeOut} is called, the element gets hidden using fadeout
* animation. This is reference to the listener for transition end added to
* the icon element while it's fading out.
* @type {?function(Event)}
* @private
*/
hideTransitionListener_: null,
/**
* Callback for click and 'Enter' key events that gets set if the icon is
* interactive.
* @type {?function()}
* @private
*/
actionHandler_: null,
/** @override */ /** @override */
decorate: function() { decorate: function() {
this.iconElement.addEventListener('mouseover', this.iconElement.addEventListener('mouseover',
...@@ -260,6 +283,16 @@ cr.define('login', function() { ...@@ -260,6 +283,16 @@ cr.define('login', function() {
this.hideTooltip_.bind(this, false)); this.hideTooltip_.bind(this, false));
this.iconElement.addEventListener('mousedown', this.iconElement.addEventListener('mousedown',
this.hideTooltip_.bind(this, false)); this.hideTooltip_.bind(this, false));
this.iconElement.addEventListener('click',
this.handleClick_.bind(this));
this.iconElement.addEventListener('keydown',
this.handleKeyDown_.bind(this));
// When the icon is focused using mouse, there should be no outline shown.
// Preventing default mousedown event accomplishes this.
this.iconElement.addEventListener('mousedown', function(e) {
e.preventDefault();
});
}, },
/** /**
...@@ -298,16 +331,24 @@ cr.define('login', function() { ...@@ -298,16 +331,24 @@ cr.define('login', function() {
* Shows the icon. * Shows the icon.
*/ */
show: function() { show: function() {
this.resetHideTransitionState_();
this.hidden = false; this.hidden = false;
}, },
/** /**
* Hides the icon. Makes sure the tooltip is hidden and animation reset. * Hides the icon using a fade-out animation.
*/ */
hide: function() { fadeOut: function() {
// The icon is already being hidden.
if (this.iconElement.classList.contains('faded'))
return;
this.hideTooltip_(true); this.hideTooltip_(true);
this.setAnimation(null); this.iconElement.classList.add('faded');
this.hidden = true; this.hideTransitionListener_ = this.hide_.bind(this);
this.iconElement.addEventListener('webkitTransitionEnd',
this.hideTransitionListener_);
ensureTransitionEndEvent(this.iconElement, 200);
}, },
/** /**
...@@ -402,6 +443,80 @@ cr.define('login', function() { ...@@ -402,6 +443,80 @@ cr.define('login', function() {
animation.frameLengthMs); animation.frameLengthMs);
}, },
/**
* Sets up icon tabIndex attribute and handler for click and 'Enter' key
* down events.
* @param {?function()} callback If icon should be interactive, the
* function to get called on click and 'Enter' key down events. Should
* be null to make the icon non interactive.
*/
setInteractive: function(callback) {
// Update tabIndex property if needed.
if (!!this.actionHandler_ != !!callback) {
if (callback) {
this.iconElement.setAttribute('tabIndex',
UserPodTabOrder.POD_CUSTOM_ICON);
} else {
this.iconElement.removeAttribute('tabIndex');
}
}
// Set the new action handler.
this.actionHandler_ = callback;
},
/**
* Hides the icon. Makes sure the tooltip is hidden and animation reset.
* @private
*/
hide_: function() {
this.hideTooltip_(true);
this.hidden = true;
this.setAnimation(null);
this.setInteractive(null);
this.resetHideTransitionState_();
},
/**
* Ensures the icon's transition state potentially set by a call to
* {@code fadeOut} is cleared.
* @private
*/
resetHideTransitionState_: function() {
if (this.hideTransitionListener_) {
this.iconElement.removeEventListener('webkitTransitionEnd',
this.hideTransitionListener_);
this.hideTransitionListener_ = null;
}
this.iconElement.classList.toggle('faded', false);
},
/**
* Handles click event on the icon element. No-op if
* {@code this.actionHandler_} is not set.
* @param {Event} e The click event.
* @private
*/
handleClick_: function(e) {
if (!this.actionHandler_)
return;
this.actionHandler_();
stopEventPropagation(e);
},
/**
* Handles key down event on the icon element. Only 'Enter' key is handled.
* No-op if {@code this.actionHandler_} is not set.
* @param {Event} e The key down event.
* @private
*/
handleKeyDown_: function(e) {
if (!this.actionHandler_ || e.keyIdentifier != 'Enter')
return;
this.actionHandler_(e);
stopEventPropagation(e);
},
/** /**
* Called when mouse enters the icon. It sets timeout for showing the * Called when mouse enters the icon. It sets timeout for showing the
* tooltip. * tooltip.
...@@ -415,7 +530,7 @@ cr.define('login', function() { ...@@ -415,7 +530,7 @@ cr.define('login', function() {
}, },
/** /**
* Shows the current tooltip, if one is set. * Shows the current tooltip if one is set.
* @private * @private
*/ */
showTooltip_: function() { showTooltip_: function() {
...@@ -973,7 +1088,8 @@ cr.define('login', function() { ...@@ -973,7 +1088,8 @@ cr.define('login', function() {
* @type {bool} * @type {bool}
*/ */
get isAuthTypePassword() { get isAuthTypePassword() {
return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD; return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD ||
this.authType_ == AUTH_TYPE.FORCE_OFFLINE_PASSWORD;
}, },
/** /**
...@@ -2199,6 +2315,12 @@ cr.define('login', function() { ...@@ -2199,6 +2315,12 @@ cr.define('login', function() {
pod.customIconElement.setSize(icon.size || {width: 0, height: 0}); pod.customIconElement.setSize(icon.size || {width: 0, height: 0});
pod.customIconElement.setAnimation(icon.animation || null); pod.customIconElement.setAnimation(icon.animation || null);
pod.customIconElement.setOpacity(icon.opacity || 100); pod.customIconElement.setOpacity(icon.opacity || 100);
if (icon.hardlockOnClick) {
pod.customIconElement.setInteractive(
this.hardlockUserPod_.bind(this, username));
} else {
pod.customIconElement.setInteractive(null);
}
pod.customIconElement.show(); pod.customIconElement.show();
// This has to be called after |show| in case the tooltip should be shown // This has to be called after |show| in case the tooltip should be shown
// immediatelly. // immediatelly.
...@@ -2206,6 +2328,17 @@ cr.define('login', function() { ...@@ -2206,6 +2328,17 @@ cr.define('login', function() {
icon.tooltip || {text: '', autoshow: false}); icon.tooltip || {text: '', autoshow: false});
}, },
/**
* Hard-locks user pod for the user. If user pod is hard-locked, it can be
* only unlocked using password, and the authentication type cannot be
* changed.
* @param {!string} username The user's username.
* @private
*/
hardlockUserPod_: function(username) {
chrome.send('hardlockPod', [username]);
},
/** /**
* Hides the custom icon in the user pod added by showUserPodCustomIcon(). * Hides the custom icon in the user pod added by showUserPodCustomIcon().
* @param {string} username Username of pod to remove button * @param {string} username Username of pod to remove button
...@@ -2218,7 +2351,7 @@ cr.define('login', function() { ...@@ -2218,7 +2351,7 @@ cr.define('login', function() {
return; return;
} }
pod.customIconElement.hide(); pod.customIconElement.fadeOut();
}, },
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment