cros: Allow demo session to change locale and limit it to one session
We should allow demo session users to change locale from Settings,
which takes effect after a restart. But by the second time the session
restarts, the system should revert to the original locale.
This CL tries to simulate the following user actions:
1) In the Settings page, you can find the "Currently displayed" locale
(A), which comes from the kApplicationLocale in user profile.
When you select locale B, the Settings page calls |ChangeAppLocale|
but it requires a restart to take effect. Before the restart,
there's no visible UI change.
2) After restart B is displayed, now go to Settings and change the
locale back to A.
3) After a second restart A is displayed. We can assume the system is
now in the same state with 1).
So we could save the value A as the original default locale and
whenever we detect there's a difference between A and the current
locale, call |ChangeAppLocale| to change it back which takes effect in
the next session.
It's safe to initialize the original default locale with the value A,
because there's no way for users to change locale prior to this CL.
This is also preferable than getting the value directly from enrollment
because:
1) By the time this code is deployed, there're devices already enrolled
in demo mode so they can't go through OOBE again.
2) OOBE saves the locale pref in local state, and convert it to a pref
in user profile (which Settings page uses). It's safe to be
consistent with the Settings page so that the above user actions
are simulated.
Bug: 877749
Change-Id: Iabbdfcf10fdb19f931c3122c21fd3af792b93191
Reviewed-on: https://chromium-review.googlesource.com/c/1292054
Reviewed-by: Michael Giuffrida <[email protected]>
Reviewed-by: Dominic Battré <[email protected]>
Reviewed-by: Stefan Kuhne <[email protected]>
Commit-Queue: Wenzhao (Colin) Zang <[email protected]>
Cr-Commit-Position: refs/heads/master@{#602011}
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index f8f09f14..979810529 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -32,6 +32,8 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/image_loader_client.h"
#include "chromeos/settings/install_attributes.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user.h"
@@ -116,6 +118,46 @@
return extension_misc::kHighlightsAppId;
}
+// If the current locale is not the default one, ensure it is reverted to the
+// default when demo session restarts (i.e. user-selected locale is only allowed
+// to be used for a single session).
+void RestoreDefaultLocaleForNextSession() {
+ auto* user = user_manager::UserManager::Get()->GetActiveUser();
+ // Tests may not have an active user.
+ if (!user)
+ return;
+ if (!user->is_profile_created()) {
+ user->AddProfileCreatedObserver(
+ base::BindOnce(&RestoreDefaultLocaleForNextSession));
+ return;
+ }
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ DCHECK(profile);
+ const std::string current_locale =
+ profile->GetPrefs()->GetString(language::prefs::kApplicationLocale);
+ if (current_locale.empty()) {
+ LOG(WARNING) << "Current locale read from kApplicationLocale is empty!";
+ return;
+ }
+ const std::string default_locale =
+ g_browser_process->local_state()->GetString(
+ prefs::kDemoModeDefaultLocale);
+ if (default_locale.empty()) {
+ // If the default locale is uninitialized, consider the current locale to be
+ // the default. This is safe because users are not allowed to change the
+ // locale prior to introduction of this code.
+ g_browser_process->local_state()->SetString(prefs::kDemoModeDefaultLocale,
+ current_locale);
+ return;
+ }
+ if (current_locale != default_locale) {
+ // If the user has changed the locale, request to change it back (which will
+ // take effect when the session restarts).
+ profile->ChangeAppLocale(default_locale,
+ Profile::APP_LOCALE_CHANGED_VIA_DEMO_SESSION);
+ }
+}
+
} // namespace
// static
@@ -267,6 +309,11 @@
app_id != extension_misc::kGeniusAppId;
}
+// static
+void DemoSession::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kDemoModeDefaultLocale, std::string());
+}
+
void DemoSession::EnsureOfflineResourcesLoaded(
base::OnceClosure load_callback) {
if (offline_resources_loaded_) {
@@ -452,6 +499,8 @@
session_manager::SessionState::ACTIVE) {
return;
}
+ RestoreDefaultLocaleForNextSession();
+
if (!offline_enrolled_)
InstallAppFromUpdateUrl(GetHighlightsAppId());
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.h b/chrome/browser/chromeos/login/demo_mode/demo_session.h
index 23800f7..6edfffff 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.h
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.h
@@ -96,6 +96,8 @@
// in demo mode. Returns true for all apps in non-demo mode.
static bool ShouldDisplayInAppLauncher(const std::string& app_id);
+ static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
// Ensures that the load of offline demo session resources is requested.
// |load_callback| will be run once the offline resource load finishes.
void EnsureOfflineResourcesLoaded(base::OnceClosure load_callback);
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
index 80de2a5..dba9af1b 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session_unittest.cc
@@ -14,12 +14,24 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/component_updater/fake_cros_component_manager.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/common/pref_names.h"
#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/language/core/browser/pref_names.h"
#include "components/session_manager/core/session_manager.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -384,4 +396,116 @@
demo_session->GetDemoAppsPath());
}
+class DemoSessionLocaleTest : public DemoSessionTest {
+ public:
+ DemoSessionLocaleTest() {
+ auto fake_user_manager = std::make_unique<FakeChromeUserManager>();
+ user_manager_ = fake_user_manager.get();
+ scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
+ std::move(fake_user_manager));
+ }
+
+ ~DemoSessionLocaleTest() override = default;
+
+ void SetUp() override {
+ profile_manager_ = std::make_unique<TestingProfileManager>(
+ TestingBrowserProcess::GetGlobal());
+ ASSERT_TRUE(profile_manager_->SetUp());
+ DemoSessionTest::SetUp();
+ }
+
+ void TearDown() override {
+ profile_manager_->DeleteAllTestingProfiles();
+ DemoSessionTest::TearDown();
+ }
+
+ protected:
+ // Creates a dummy demo user with a testing profile and logs in.
+ TestingProfile* LoginDemoUser() {
+ const AccountId account_id(
+ AccountId::FromUserEmailGaiaId("[email protected]", "demo_user"));
+ const user_manager::User* user =
+ user_manager_->AddPublicAccountUser(account_id);
+
+ auto prefs =
+ std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+ RegisterUserProfilePrefs(prefs->registry());
+ TestingProfile* profile = profile_manager_->CreateTestingProfile(
+ "test-profile", std::move(prefs), base::ASCIIToUTF16("Test profile"),
+ 1 /* avatar_id */, std::string() /* supervised_user_id */,
+ TestingProfile::TestingFactories());
+ chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
+ profile);
+
+ user_manager_->LoginUser(account_id);
+ profile_manager_->SetLoggedIn(true);
+ return profile;
+ }
+
+ private:
+ FakeChromeUserManager* user_manager_ = nullptr;
+ std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+ std::unique_ptr<TestingProfileManager> profile_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoSessionLocaleTest);
+};
+
+TEST_F(DemoSessionLocaleTest, InitializeDefaultLocale) {
+ DemoSession* demo_session = DemoSession::StartIfInDemoMode();
+ ASSERT_TRUE(demo_session);
+
+ TestingProfile* profile = LoginDemoUser();
+ // When the default locale is empty, verify that it's initialized with the
+ // current locale.
+ constexpr char kCurrentLocale[] = "en-US";
+ profile->GetPrefs()->SetString(language::prefs::kApplicationLocale,
+ kCurrentLocale);
+ EXPECT_EQ("", TestingBrowserProcess::GetGlobal()->local_state()->GetString(
+ prefs::kDemoModeDefaultLocale));
+ session_manager_->SetSessionState(session_manager::SessionState::ACTIVE);
+ EXPECT_EQ(kCurrentLocale,
+ TestingBrowserProcess::GetGlobal()->local_state()->GetString(
+ prefs::kDemoModeDefaultLocale));
+ EXPECT_FALSE(profile->requested_locale().has_value());
+}
+
+TEST_F(DemoSessionLocaleTest, DefaultAndCurrentLocaleDifferent) {
+ DemoSession* demo_session = DemoSession::StartIfInDemoMode();
+ ASSERT_TRUE(demo_session);
+
+ TestingProfile* profile = LoginDemoUser();
+ // When the default locale is different from the current locale, verify that
+ // reverting to default locale is requested.
+ constexpr char kCurrentLocale[] = "zh-CN";
+ constexpr char kDefaultLocale[] = "en-US";
+ profile->GetPrefs()->SetString(language::prefs::kApplicationLocale,
+ kCurrentLocale);
+ TestingBrowserProcess::GetGlobal()->local_state()->SetString(
+ prefs::kDemoModeDefaultLocale, kDefaultLocale);
+ session_manager_->SetSessionState(session_manager::SessionState::ACTIVE);
+ EXPECT_EQ(kDefaultLocale,
+ TestingBrowserProcess::GetGlobal()->local_state()->GetString(
+ prefs::kDemoModeDefaultLocale));
+ EXPECT_EQ(kDefaultLocale, profile->requested_locale().value());
+}
+
+TEST_F(DemoSessionLocaleTest, DefaultAndCurrentLocaleIdentical) {
+ DemoSession* demo_session = DemoSession::StartIfInDemoMode();
+ ASSERT_TRUE(demo_session);
+
+ TestingProfile* profile = LoginDemoUser();
+ // When the default locale is the same with the current locale, verify that
+ // it's no-op.
+ constexpr char kDefaultLocale[] = "en-US";
+ profile->GetPrefs()->SetString(language::prefs::kApplicationLocale,
+ kDefaultLocale);
+ TestingBrowserProcess::GetGlobal()->local_state()->SetString(
+ prefs::kDemoModeDefaultLocale, kDefaultLocale);
+ session_manager_->SetSessionState(session_manager::SessionState::ACTIVE);
+ EXPECT_EQ(kDefaultLocale,
+ TestingBrowserProcess::GetGlobal()->local_state()->GetString(
+ prefs::kDemoModeDefaultLocale));
+ EXPECT_FALSE(profile->requested_locale().has_value());
+}
+
} // namespace chromeos
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index be3fc5c8..8c86eb18 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -235,6 +235,7 @@
#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_mode_detector.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_mode_resources_remover.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/chromeos/login/quick_unlock/fingerprint_storage.h"
#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h"
@@ -482,6 +483,7 @@
chromeos::ChromeUserManagerImpl::RegisterPrefs(registry);
chromeos::DemoModeDetector::RegisterPrefs(registry);
chromeos::DemoModeResourcesRemover::RegisterLocalStatePrefs(registry);
+ chromeos::DemoSession::RegisterLocalStatePrefs(registry);
chromeos::DemoSetupController::RegisterLocalStatePrefs(registry);
chromeos::DeviceOAuth2TokenService::RegisterPrefs(registry);
chromeos::device_settings_cache::RegisterPrefs(registry);
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index e52b848..9b651ce 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -232,8 +232,10 @@
APP_LOCALE_CHANGED_VIA_LOGIN,
// From login to a public session.
APP_LOCALE_CHANGED_VIA_PUBLIC_SESSION_LOGIN,
- // From AllowedUILocales policy
+ // From AllowedUILocales policy.
APP_LOCALE_CHANGED_VIA_POLICY,
+ // From demo session.
+ APP_LOCALE_CHANGED_VIA_DEMO_SESSION,
// Source unknown.
APP_LOCALE_CHANGED_VIA_UNKNOWN
};
diff --git a/chrome/browser/resources/settings/device_page/device_page.js b/chrome/browser/resources/settings/device_page/device_page.js
index 46c32cd..853e5249 100644
--- a/chrome/browser/resources/settings/device_page/device_page.js
+++ b/chrome/browser/resources/settings/device_page/device_page.js
@@ -61,8 +61,9 @@
hideStorageInfo_: {
type: Boolean,
value: function() {
- return loadTimeData.valueExists('hideStorageInfo') &&
- loadTimeData.getBoolean('hideStorageInfo');
+ // TODO(crbug.com/868747): Show an explanatory message instead.
+ return loadTimeData.valueExists('isDemoSession') &&
+ loadTimeData.getBoolean('isDemoSession');
},
readOnly: true,
},
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.js b/chrome/browser/resources/settings/languages_page/languages_page.js
index 1e7beeb2..7928445 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -213,8 +213,10 @@
menu.querySelector('#uiLanguageItem').hidden = true;
// The UI language choice doesn't persist for guests.
- if (loadTimeData.getBoolean('isGuest'))
+ if (loadTimeData.getBoolean('isGuest') &&
+ !loadTimeData.getBoolean('isDemoSession')) {
menu.querySelector('#uiLanguageItem').hidden = true;
+ }
},
/**
diff --git a/chrome/browser/ui/webui/settings/languages_handler.cc b/chrome/browser/ui/webui/settings/languages_handler.cc
index 0346fa1..f7ef878 100644
--- a/chrome/browser/ui/webui/settings/languages_handler.cc
+++ b/chrome/browser/ui/webui/settings/languages_handler.cc
@@ -16,6 +16,7 @@
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/base/locale_util.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
@@ -82,13 +83,15 @@
PrefService* prefs = g_browser_process->local_state();
prefs->SetString(language::prefs::kApplicationLocale, language_code);
#elif defined(OS_CHROMEOS)
- // Secondary users and public session users cannot change the locale.
+ // Secondary users and public session users (except for demo session users)
+ // cannot change the locale.
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
if (user &&
user->GetAccountId() == user_manager->GetPrimaryUser()->GetAccountId() &&
- user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
+ (user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT ||
+ chromeos::DemoSession::IsDeviceInDemoMode())) {
profile_->ChangeAppLocale(language_code,
Profile::APP_LOCALE_CHANGED_VIA_SETTINGS);
}
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 16892cf..cb9b7f53 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -325,9 +325,7 @@
html_source->AddBoolean("showCrostini",
crostini::IsCrostiniUIAllowedForProfile(profile));
- // TODO(crbug.com/868747): Show an explanatory message instead of hiding the
- // storage management info.
- html_source->AddBoolean("hideStorageInfo",
+ html_source->AddBoolean("isDemoSession",
chromeos::DemoSession::IsDeviceInDemoMode());
// We have 2 variants of Android apps settings. Default case, when the Play