| // 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. |
| |
| #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_ |
| #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include "ash/detachable_base/detachable_base_observer.h" |
| #include "ash/wallpaper/wallpaper_controller_observer.h" |
| #include "base/callback.h" |
| #include "base/compiler_specific.h" |
| #include "base/containers/hash_tables.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/scoped_observer.h" |
| #include "chrome/browser/chromeos/lock_screen_apps/state_observer.h" |
| #include "chrome/browser/chromeos/login/screens/error_screen.h" |
| #include "chrome/browser/chromeos/login/signin_specifics.h" |
| #include "chrome/browser/chromeos/login/ui/login_display.h" |
| #include "chrome/browser/chromeos/settings/cros_settings.h" |
| #include "chrome/browser/ui/ash/tablet_mode_client_observer.h" |
| #include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h" |
| #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h" |
| #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" |
| #include "chromeos/dbus/power_manager_client.h" |
| #include "chromeos/network/portal_detector/network_portal_detector.h" |
| #include "components/proximity_auth/screenlock_bridge.h" |
| #include "components/session_manager/core/session_manager_observer.h" |
| #include "components/user_manager/user_manager.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/web_ui.h" |
| #include "net/base/net_errors.h" |
| #include "ui/base/ime/chromeos/ime_keyboard.h" |
| #include "ui/base/ime/chromeos/input_method_manager.h" |
| #include "ui/events/event_handler.h" |
| |
| class AccountId; |
| |
| namespace ash { |
| namespace mojom { |
| enum class TrayActionState; |
| } // namespace mojom |
| |
| class DetachableBaseHandler; |
| } // namespace ash |
| |
| namespace base { |
| class DictionaryValue; |
| class ListValue; |
| } |
| |
| namespace lock_screen_apps { |
| class StateController; |
| } |
| |
| namespace session_manager { |
| class SessionManager; |
| } |
| |
| namespace chromeos { |
| |
| class CoreOobeView; |
| class ErrorScreensHistogramHelper; |
| class GaiaScreenHandler; |
| class LoginFeedback; |
| class NativeWindowDelegate; |
| class SupervisedUserCreationScreenHandler; |
| class User; |
| class UserContext; |
| |
| // Helper class to pass initial parameters to the login screen. |
| class LoginScreenContext { |
| public: |
| LoginScreenContext(); |
| explicit LoginScreenContext(const base::ListValue* args); |
| |
| void set_email(const std::string& email) { email_ = email; } |
| const std::string& email() const { return email_; } |
| |
| void set_oobe_ui(bool oobe_ui) { oobe_ui_ = oobe_ui; } |
| bool oobe_ui() const { return oobe_ui_; } |
| |
| private: |
| void Init(); |
| |
| std::string email_; |
| bool oobe_ui_; |
| }; |
| |
| // An interface for WebUILoginDisplay to call SigninScreenHandler. |
| class LoginDisplayWebUIHandler { |
| public: |
| virtual void ClearAndEnablePassword() = 0; |
| virtual void ClearUserPodPassword() = 0; |
| virtual void OnUserRemoved(const AccountId& account_id, |
| bool last_user_removed) = 0; |
| virtual void OnUserImageChanged(const user_manager::User& user) = 0; |
| virtual void OnPreferencesChanged() = 0; |
| virtual void ResetSigninScreenHandlerDelegate() = 0; |
| virtual void ShowError(int login_attempts, |
| const std::string& error_text, |
| const std::string& help_link_text, |
| HelpAppLauncher::HelpTopic help_topic_id) = 0; |
| virtual void ShowErrorScreen(LoginDisplay::SigninError error_id) = 0; |
| virtual void ShowSigninUI(const std::string& email) = 0; |
| virtual void ShowPasswordChangedDialog(bool show_password_error, |
| const std::string& email) = 0; |
| // Show sign-in screen for the given credentials. |
| virtual void ShowSigninScreenForCreds(const std::string& username, |
| const std::string& password) = 0; |
| virtual void ShowWhitelistCheckFailedError() = 0; |
| virtual void ShowUnrecoverableCrypthomeErrorDialog() = 0; |
| virtual void LoadUsers(const user_manager::UserList& users, |
| const base::ListValue& users_list) = 0; |
| |
| protected: |
| virtual ~LoginDisplayWebUIHandler() {} |
| }; |
| |
| // An interface for SigninScreenHandler to call WebUILoginDisplay. |
| class SigninScreenHandlerDelegate { |
| public: |
| // --------------- Password change flow methods. |
| // Cancels current password changed flow. |
| virtual void CancelPasswordChangedFlow() = 0; |
| |
| // Decrypt cryptohome using user provided |old_password| |
| // and migrate to new password. |
| virtual void MigrateUserData(const std::string& old_password) = 0; |
| |
| // Ignore password change, remove existing cryptohome and |
| // force full sync of user data. |
| virtual void ResyncUserData() = 0; |
| |
| // --------------- Sign in/out methods. |
| // Sign in using username and password specified as a part of |user_context|. |
| // Used for both known and new users. |
| virtual void Login(const UserContext& user_context, |
| const SigninSpecifics& specifics) = 0; |
| |
| // Returns true if sign in is in progress. |
| virtual bool IsSigninInProgress() const = 0; |
| |
| // Signs out if the screen is currently locked. |
| virtual void Signout() = 0; |
| |
| // --------------- Shared with login display methods. |
| // Notify the delegate when the sign-in UI is finished loading. |
| virtual void OnSigninScreenReady() = 0; |
| |
| // Shows Enterprise Enrollment screen. |
| virtual void ShowEnterpriseEnrollmentScreen() = 0; |
| |
| // Shows Enable Developer Features screen. |
| virtual void ShowEnableDebuggingScreen() = 0; |
| |
| // Shows Demo Mode Setup screen. |
| virtual void ShowDemoModeSetupScreen() = 0; |
| |
| // Shows Kiosk Enable screen. |
| virtual void ShowKioskEnableScreen() = 0; |
| |
| // Shows Reset screen. |
| virtual void ShowKioskAutolaunchScreen() = 0; |
| |
| // Show wrong hwid screen. |
| virtual void ShowWrongHWIDScreen() = 0; |
| |
| // Show update required screen. |
| virtual void ShowUpdateRequiredScreen() = 0; |
| |
| // --------------- Rest of the methods. |
| // Cancels user adding. |
| virtual void CancelUserAdding() = 0; |
| |
| // Attempts to remove given user. |
| virtual void RemoveUser(const AccountId& account_id) = 0; |
| |
| // Let the delegate know about the handler it is supposed to be using. |
| virtual void SetWebUIHandler(LoginDisplayWebUIHandler* webui_handler) = 0; |
| |
| // Whether login as guest is available. |
| virtual bool IsShowGuest() const = 0; |
| |
| // Whether to show the user pods or only GAIA sign in. |
| // Public sessions are always shown. |
| virtual bool IsShowUsers() const = 0; |
| |
| // Whether the show user pods setting has changed. |
| virtual bool ShowUsersHasChanged() const = 0; |
| |
| // Whether the create new account option in GAIA is enabled by the setting. |
| virtual bool IsAllowNewUser() const = 0; |
| |
| // Whether the allow new user setting has changed. |
| virtual bool AllowNewUserChanged() const = 0; |
| |
| // Whether user sign in has completed. |
| virtual bool IsUserSigninCompleted() const = 0; |
| |
| // Request to (re)load user list. |
| virtual void HandleGetUsers() = 0; |
| |
| // Runs an OAuth token validation check for user. |
| virtual void CheckUserStatus(const AccountId& account_id) = 0; |
| |
| protected: |
| virtual ~SigninScreenHandlerDelegate() {} |
| }; |
| |
| // A class that handles the WebUI hooks in sign-in screen in OobeUI and |
| // LoginDisplay. |
| class SigninScreenHandler |
| : public BaseWebUIHandler, |
| public LoginDisplayWebUIHandler, |
| public content::NotificationObserver, |
| public NetworkStateInformer::NetworkStateInformerObserver, |
| public PowerManagerClient::Observer, |
| public input_method::ImeKeyboard::Observer, |
| public TabletModeClientObserver, |
| public lock_screen_apps::StateObserver, |
| public OobeUI::Observer, |
| public session_manager::SessionManagerObserver, |
| public ash::WallpaperControllerObserver, |
| public ash::DetachableBaseObserver { |
| public: |
| SigninScreenHandler( |
| const scoped_refptr<NetworkStateInformer>& network_state_informer, |
| ErrorScreen* error_screen, |
| CoreOobeView* core_oobe_view, |
| GaiaScreenHandler* gaia_screen_handler, |
| JSCallsContainer* js_calls_container); |
| ~SigninScreenHandler() override; |
| |
| static std::string GetUserLastInputMethod(const std::string& username); |
| |
| // Update current input method (namely keyboard layout) in the given IME state |
| // to last input method used by this user. |
| static void SetUserInputMethod( |
| const std::string& username, |
| input_method::InputMethodManager::State* ime_state); |
| |
| // Shows the sign in screen. |
| void Show(const LoginScreenContext& context); |
| |
| // Sets delegate to be used by the handler. It is guaranteed that valid |
| // delegate is set before Show() method will be called. |
| void SetDelegate(SigninScreenHandlerDelegate* delegate); |
| |
| void SetNativeWindowDelegate(NativeWindowDelegate* native_window_delegate); |
| |
| // NetworkStateInformer::NetworkStateInformerObserver implementation: |
| void OnNetworkReady() override; |
| void UpdateState(NetworkError::ErrorReason reason) override; |
| |
| // Required Local State preferences. |
| static void RegisterPrefs(PrefRegistrySimple* registry); |
| |
| // OobeUI::Observer implementation: |
| void OnCurrentScreenChanged(OobeScreen current_screen, |
| OobeScreen new_screen) override; |
| void OnScreenInitialized(OobeScreen screen) override{}; |
| |
| // ash::WallpaperControllerObserver implementation: |
| void OnWallpaperDataChanged() override; |
| void OnWallpaperColorsChanged() override; |
| void OnWallpaperBlurChanged() override; |
| |
| // ash::DetachableBaseObserver: |
| void OnDetachableBasePairingStatusChanged( |
| ash::DetachableBasePairingStatus pairing_status) override; |
| void OnDetachableBaseRequiresUpdateChanged(bool requires_update) override; |
| |
| void SetFocusPODCallbackForTesting(base::Closure callback); |
| |
| // To avoid spurious error messages on flaky networks, the offline message is |
| // only shown if the network is offline for a threshold number of seconds. |
| // This method provides an ability to reduce the threshold to zero, allowing |
| // the offline message to show instantaneously in tests. The threshold can |
| // also be set to a high value to disable the offline message on slow |
| // configurations like MSAN, where it otherwise triggers on every run. |
| void SetOfflineTimeoutForTesting(base::TimeDelta offline_timeout); |
| |
| // Gets the keyboard remapped pref value for |pref_name| key. Returns true if |
| // successful, otherwise returns false. |
| bool GetKeyboardRemappedPrefValue(const std::string& pref_name, int* value); |
| |
| private: |
| enum UIState { |
| UI_STATE_UNKNOWN = 0, |
| UI_STATE_GAIA_SIGNIN, |
| UI_STATE_ACCOUNT_PICKER, |
| }; |
| |
| friend class GaiaScreenHandler; |
| friend class ReportDnsCacheClearedOnUIThread; |
| friend class SupervisedUserCreationScreenHandler; |
| |
| void ShowImpl(); |
| |
| // Updates current UI of the signin screen according to |ui_state| |
| // argument. Optionally it can pass screen initialization data via |
| // |params| argument. |
| void UpdateUIState(UIState ui_state, base::DictionaryValue* params); |
| |
| void UpdateStateInternal(NetworkError::ErrorReason reason, bool force_update); |
| void SetupAndShowOfflineMessage(NetworkStateInformer::State state, |
| NetworkError::ErrorReason reason); |
| void HideOfflineMessage(NetworkStateInformer::State state, |
| NetworkError::ErrorReason reason); |
| void ReloadGaia(bool force_reload); |
| |
| // Updates the color of the scrollable container on account picker screen, |
| // based on wallpaper color extraction results. |
| void UpdateAccountPickerColors(); |
| |
| // BaseScreenHandler implementation: |
| void DeclareLocalizedValues( |
| ::login::LocalizedValuesBuilder* builder) override; |
| void Initialize() override; |
| gfx::NativeWindow GetNativeWindow() override; |
| |
| // WebUIMessageHandler implementation: |
| void RegisterMessages() override; |
| |
| // LoginDisplayWebUIHandler implementation: |
| void ClearAndEnablePassword() override; |
| void ClearUserPodPassword() override; |
| void OnUserRemoved(const AccountId& account_id, |
| bool last_user_removed) override; |
| void OnUserImageChanged(const user_manager::User& user) override; |
| void OnPreferencesChanged() override; |
| void ResetSigninScreenHandlerDelegate() override; |
| void ShowError(int login_attempts, |
| const std::string& error_text, |
| const std::string& help_link_text, |
| HelpAppLauncher::HelpTopic help_topic_id) override; |
| void ShowSigninUI(const std::string& email) override; |
| void ShowPasswordChangedDialog(bool show_password_error, |
| const std::string& email) override; |
| void ShowErrorScreen(LoginDisplay::SigninError error_id) override; |
| void ShowSigninScreenForCreds(const std::string& username, |
| const std::string& password) override; |
| void ShowWhitelistCheckFailedError() override; |
| void ShowUnrecoverableCrypthomeErrorDialog() override; |
| void LoadUsers(const user_manager::UserList& users, |
| const base::ListValue& users_list) override; |
| |
| // content::NotificationObserver implementation: |
| void Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) override; |
| |
| // PowerManagerClient::Observer implementation: |
| void SuspendDone(const base::TimeDelta& sleep_duration) override; |
| |
| // TabletModeClientObserver: |
| void OnTabletModeToggled(bool enabled) override; |
| |
| // session_manager::SessionManagerObserver: |
| void OnSessionStateChanged() override; |
| |
| // lock_screen_apps::StateObserver: |
| void OnLockScreenNoteStateChanged(ash::mojom::TrayActionState state) override; |
| |
| void UpdateAddButtonStatus(); |
| |
| // Restore input focus to current user pod. |
| void RefocusCurrentPod(); |
| |
| // Enable or disable the pin keyboard for the given account. |
| void UpdatePinKeyboardState(const AccountId& account_id); |
| |
| // WebUI message handlers. |
| void HandleGetUsers(); |
| void HandleAuthenticateUser(const AccountId& account_id, |
| const std::string& password, |
| bool authenticated_by_pin); |
| void HandleAttemptUnlock(const std::string& username); |
| void HandleLaunchIncognito(); |
| void HandleLaunchPublicSession(const AccountId& account_id, |
| const std::string& locale, |
| const std::string& input_method); |
| void HandleOfflineLogin(const base::ListValue* args); |
| void HandleShutdownSystem(); |
| void HandleRebootSystem(); |
| void HandleRemoveUser(const AccountId& account_id); |
| void HandleToggleEnrollmentScreen(); |
| void HandleToggleEnrollmentAd(); |
| void HandleToggleEnableDebuggingScreen(); |
| void HandleSetupDemoMode(); |
| void HandleToggleKioskEnableScreen(); |
| void HandleToggleResetScreen(); |
| void HandleToggleKioskAutolaunchScreen(); |
| void HandleAccountPickerReady(); |
| void HandleWallpaperReady(); |
| void HandleSignOutUser(); |
| void HandleOpenInternetDetailDialog(); |
| void HandleLoginVisible(const std::string& source); |
| void HandleCancelPasswordChangedFlow(const AccountId& account_id); |
| void HandleCancelUserAdding(); |
| void HandleMigrateUserData(const std::string& password); |
| void HandleResyncUserData(); |
| void HandleLoginUIStateChanged(const std::string& source, bool active); |
| void HandleUnlockOnLoginSuccess(); |
| void HandleLoginScreenUpdate(); |
| void HandleShowLoadingTimeoutError(); |
| void HandleShowSupervisedUserCreationScreen(); |
| void HandleFocusPod(const AccountId& account_id, bool is_large_pod); |
| void HandleNoPodFocused(); |
| void HandleHardlockPod(const std::string& user_id); |
| void HandleLaunchKioskApp(const AccountId& app_account_id, |
| bool diagnostic_mode); |
| void HandleLaunchArcKioskApp(const AccountId& app_account_id); |
| void HandleGetPublicSessionKeyboardLayouts(const AccountId& account_id, |
| const std::string& locale); |
| void HandleGetTabletModeState(); |
| void HandleLogRemoveUserWarningShown(); |
| void HandleFirstIncorrectPasswordAttempt(const AccountId& account_id); |
| void HandleMaxIncorrectPasswordAttempts(const AccountId& account_id); |
| void HandleSendFeedback(); |
| void HandleSendFeedbackAndResyncUserData(); |
| void HandleRequestNewNoteAction(const std::string& request_type); |
| void HandleNewNoteLaunchAnimationDone(); |
| void HandleCloseLockScreenApp(); |
| |
| // Sends the list of |keyboard_layouts| available for the |locale| that is |
| // currently selected for the public session identified by |user_id|. |
| void SendPublicSessionKeyboardLayouts( |
| const AccountId& account_id, |
| const std::string& locale, |
| std::unique_ptr<base::ListValue> keyboard_layouts); |
| |
| // Returns true iff |
| // (i) log in is restricted to some user list, |
| // (ii) all users in the restricted list are present. |
| bool AllWhitelistedUsersPresent(); |
| |
| // Cancels password changed flow - switches back to login screen. |
| // Called as a callback after cookies are cleared. |
| void CancelPasswordChangedFlowInternal(); |
| |
| // Returns true if current visible screen is the Gaia sign-in page. |
| bool IsGaiaVisible() const; |
| |
| // Returns true if current visible screen is the error screen over |
| // Gaia sign-in page. |
| bool IsGaiaHiddenByError() const; |
| |
| // Returns true if current screen is the error screen over signin |
| // screen. |
| bool IsSigninScreenHiddenByError() const; |
| |
| // Returns true if guest signin is allowed. |
| bool IsGuestSigninAllowed() const; |
| |
| bool ShouldLoadGaia() const; |
| |
| net::Error FrameError() const; |
| |
| // input_method::ImeKeyboard::Observer implementation: |
| void OnCapsLockChanged(bool enabled) override; |
| void OnLayoutChanging(const std::string& layout_name) override {} |
| |
| // Callback invoked after the feedback is finished. |
| void OnFeedbackFinished(); |
| |
| // Callback invoked after the feedback sent from the unrecoverable cryptohome |
| // page is finished. |
| void OnUnrecoverableCryptohomeFeedbackFinished(); |
| |
| // Called when the cros property controlling allowed input methods changes. |
| void OnAllowedInputMethodsChanged(); |
| |
| // After proxy auth information has been supplied, this function re-enables |
| // responding to network state notifications. |
| void ReenableNetworkStateUpdatesAfterProxyAuth(); |
| |
| // Determines whether a warning about the detachable base getting changed |
| // should be shown to the user. The warning is shown a detachable base is |
| // present, and the user whose pod is currently focused has used a different |
| // base last time. It updates the detachable base warning visibility as |
| // required. |
| void UpdateDetachableBaseChangedError(); |
| |
| // Sends a request to the UI to show a detachable base change warning for the |
| // currently focused user pod. The warning warns the user that the currently |
| // attached base is different than the one they last used, and that it might |
| // not be trusted. |
| void ShowDetachableBaseChangedError(); |
| |
| // If a detachable base change warning was requested to be shown, sends a |
| // request to UI to hide the warning. |
| void HideDetachableBaseChangedError(); |
| |
| // Current UI state of the signin screen. |
| UIState ui_state_ = UI_STATE_UNKNOWN; |
| |
| // A delegate that glues this handler with backend LoginDisplay. |
| SigninScreenHandlerDelegate* delegate_ = nullptr; |
| |
| // A delegate used to get gfx::NativeWindow. |
| NativeWindowDelegate* native_window_delegate_ = nullptr; |
| |
| // Whether screen should be shown right after initialization. |
| bool show_on_init_ = false; |
| |
| // Keeps whether screen should be shown for OOBE. |
| bool oobe_ui_ = false; |
| |
| // Is account picker being shown for the first time. |
| bool is_account_picker_showing_first_time_ = false; |
| |
| // Network state informer used to keep signin screen up. |
| scoped_refptr<NetworkStateInformer> network_state_informer_; |
| |
| // Set to true once |LOGIN_WEBUI_VISIBLE| notification is observed. |
| bool webui_visible_ = false; |
| bool preferences_changed_delayed_ = false; |
| |
| ErrorScreen* error_screen_ = nullptr; |
| CoreOobeView* core_oobe_view_ = nullptr; |
| |
| NetworkStateInformer::State last_network_state_ = |
| NetworkStateInformer::UNKNOWN; |
| |
| base::CancelableClosure update_state_closure_; |
| base::CancelableClosure connecting_closure_; |
| |
| content::NotificationRegistrar registrar_; |
| |
| std::unique_ptr<CrosSettings::ObserverSubscription> |
| allowed_input_methods_subscription_; |
| |
| // Whether we're currently ignoring network state updates because a proxy auth |
| // UI pending (or we're waiting for a grace period after the proxy auth UI is |
| // finished for the network to switch into the ONLINE state). |
| bool network_state_ignored_until_proxy_auth_ = false; |
| |
| // Used for pending GAIA reloads. |
| NetworkError::ErrorReason gaia_reload_reason_ = |
| NetworkError::ERROR_REASON_NONE; |
| |
| bool caps_lock_enabled_ = false; |
| |
| // If network has accidentally changed to the one that requires proxy |
| // authentication, we will automatically reload gaia page that will bring |
| // "Proxy authentication" dialog to the user. To prevent flakiness, we will do |
| // it at most 3 times. |
| int proxy_auth_dialog_reload_times_; |
| |
| // True if we need to reload gaia page to bring back "Proxy authentication" |
| // dialog. |
| bool proxy_auth_dialog_need_reload_ = false; |
| |
| // Non-owning ptr. |
| // TODO(antrim@): remove this dependency. |
| GaiaScreenHandler* gaia_screen_handler_ = nullptr; |
| |
| // Input Method Engine state used at signin screen. |
| scoped_refptr<input_method::InputMethodManager::State> ime_state_; |
| |
| // This callback captures "focusPod finished" event for tests. |
| base::Closure test_focus_pod_callback_; |
| |
| // True if SigninScreenHandler has already been added to OobeUI observers. |
| bool oobe_ui_observer_added_ = false; |
| |
| bool is_offline_timeout_for_test_set_ = false; |
| base::TimeDelta offline_timeout_for_test_; |
| |
| std::unique_ptr<ErrorScreensHistogramHelper> histogram_helper_; |
| |
| std::unique_ptr<LoginFeedback> login_feedback_; |
| |
| std::unique_ptr<AccountId> focused_pod_account_id_; |
| |
| // If set, the account for which detachable base change warning was shown in |
| // the login UI. |
| base::Optional<AccountId> account_with_detachable_base_error_; |
| |
| ScopedObserver<session_manager::SessionManager, |
| session_manager::SessionManagerObserver> |
| session_manager_observer_; |
| ScopedObserver<lock_screen_apps::StateController, |
| lock_screen_apps::StateObserver> |
| lock_screen_apps_observer_; |
| |
| ScopedObserver<ash::DetachableBaseHandler, ash::DetachableBaseObserver> |
| detachable_base_observer_; |
| |
| base::WeakPtrFactory<SigninScreenHandler> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SigninScreenHandler); |
| }; |
| |
| } // namespace chromeos |
| |
| #endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_ |