OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/basictypes.h" | 5 #include "base/basictypes.h" |
6 #include "base/compiler_specific.h" | 6 #include "base/compiler_specific.h" |
7 #include "base/prefs/pref_service.h" | 7 #include "base/prefs/pref_service.h" |
| 8 #include "base/strings/stringprintf.h" |
8 #include "chrome/browser/chromeos/login/login_manager_test.h" | 9 #include "chrome/browser/chromeos/login/login_manager_test.h" |
9 #include "chrome/browser/chromeos/login/startup_utils.h" | 10 #include "chrome/browser/chromeos/login/startup_utils.h" |
10 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h" | 11 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h" |
11 #include "chrome/browser/chromeos/login/users/user_manager.h" | 12 #include "chrome/browser/chromeos/login/users/user_manager.h" |
12 #include "chrome/browser/chromeos/settings/cros_settings.h" | 13 #include "chrome/browser/chromeos/settings/cros_settings.h" |
13 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" | 14 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" |
14 #include "chrome/browser/ui/browser.h" | 15 #include "chrome/browser/ui/browser.h" |
| 16 #include "chrome/browser/ui/browser_commands.h" |
15 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 17 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
16 #include "chrome/common/pref_names.h" | 18 #include "chrome/common/pref_names.h" |
17 #include "chrome/test/base/ui_test_utils.h" | 19 #include "chrome/test/base/ui_test_utils.h" |
18 #include "chromeos/settings/cros_settings_names.h" | 20 #include "chromeos/settings/cros_settings_names.h" |
19 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
20 #include "content/public/test/browser_test_utils.h" | 22 #include "content/public/test/browser_test_utils.h" |
21 #include "content/public/test/test_utils.h" | 23 #include "content/public/test/test_utils.h" |
22 | 24 |
23 namespace chromeos { | 25 namespace chromeos { |
24 | 26 |
25 namespace { | 27 namespace { |
26 | 28 |
27 const char* kTestUsers[] = { "test-user1@gmail.com", "test-user2@gmail.com" }; | 29 const char* kTestOwner = "test-owner@example.com"; |
| 30 const char* kTestNonOwner = "test-user1@example.com"; |
| 31 |
| 32 const char* kKnownSettings[] = { |
| 33 kDeviceOwner, |
| 34 kAccountsPrefAllowGuest, |
| 35 kAccountsPrefAllowNewUser, |
| 36 kAccountsPrefDeviceLocalAccounts, |
| 37 kAccountsPrefShowUserNamesOnSignIn, |
| 38 kAccountsPrefSupervisedUsersEnabled, |
| 39 }; |
| 40 |
| 41 // Stub settings provider that only handles the settings we need to control. |
| 42 // StubCrosSettingsProvider handles more settings but leaves many of them unset |
| 43 // which the Settings page doesn't expect. |
| 44 class StubAccountSettingsProvider : public StubCrosSettingsProvider { |
| 45 public: |
| 46 StubAccountSettingsProvider() { |
| 47 } |
| 48 |
| 49 virtual ~StubAccountSettingsProvider() { |
| 50 } |
| 51 |
| 52 // StubCrosSettingsProvider implementation. |
| 53 virtual bool HandlesSetting(const std::string& path) const OVERRIDE { |
| 54 const char** end = kKnownSettings + arraysize(kKnownSettings); |
| 55 return std::find(kKnownSettings, end, path) != end; |
| 56 } |
| 57 }; |
| 58 |
| 59 struct PrefTest { |
| 60 const char* pref_name; |
| 61 bool owner_only; |
| 62 bool indicator; |
| 63 }; |
| 64 |
| 65 const PrefTest kPrefTests[] = { |
| 66 { kSystemTimezone, false, false }, |
| 67 { prefs::kUse24HourClock, false, false }, |
| 68 { kAttestationForContentProtectionEnabled, true, true }, |
| 69 { kAccountsPrefAllowGuest, true, false }, |
| 70 { kAccountsPrefAllowNewUser, true, false }, |
| 71 { kAccountsPrefShowUserNamesOnSignIn, true, false }, |
| 72 { kAccountsPrefSupervisedUsersEnabled, true, false }, |
| 73 #if defined(GOOGLE_CHROME_BUILD) |
| 74 { kStatsReportingPref, true, true }, |
| 75 { prefs::kSpellCheckUseSpellingService, false, false }, |
| 76 #endif |
| 77 }; |
28 | 78 |
29 } // namespace | 79 } // namespace |
30 | 80 |
31 class SharedOptionsTest : public LoginManagerTest { | 81 class SharedOptionsTest : public LoginManagerTest { |
32 public: | 82 public: |
33 SharedOptionsTest() | 83 SharedOptionsTest() |
34 : LoginManagerTest(false), | 84 : LoginManagerTest(false), |
35 device_settings_provider_(NULL) { | 85 device_settings_provider_(NULL) { |
36 stub_settings_provider_.Set(kDeviceOwner, base::StringValue(kTestUsers[0])); | 86 stub_settings_provider_.Set(kDeviceOwner, base::StringValue(kTestOwner)); |
37 } | 87 } |
38 | 88 |
39 virtual ~SharedOptionsTest() { | 89 virtual ~SharedOptionsTest() { |
40 } | 90 } |
41 | 91 |
42 virtual void SetUpOnMainThread() OVERRIDE { | 92 virtual void SetUpOnMainThread() OVERRIDE { |
43 LoginManagerTest::SetUpOnMainThread(); | 93 LoginManagerTest::SetUpOnMainThread(); |
| 94 |
44 CrosSettings* settings = CrosSettings::Get(); | 95 CrosSettings* settings = CrosSettings::Get(); |
| 96 |
| 97 // Add the stub settings provider, moving the device settings provider |
| 98 // behind it so our stub takes precedence. |
45 device_settings_provider_ = settings->GetProvider(kDeviceOwner); | 99 device_settings_provider_ = settings->GetProvider(kDeviceOwner); |
46 settings->RemoveSettingsProvider(device_settings_provider_); | 100 settings->RemoveSettingsProvider(device_settings_provider_); |
47 settings->AddSettingsProvider(&stub_settings_provider_); | 101 settings->AddSettingsProvider(&stub_settings_provider_); |
| 102 settings->AddSettingsProvider(device_settings_provider_); |
48 } | 103 } |
49 | 104 |
50 virtual void CleanUpOnMainThread() OVERRIDE { | 105 virtual void CleanUpOnMainThread() OVERRIDE { |
51 CrosSettings* settings = CrosSettings::Get(); | 106 CrosSettings* settings = CrosSettings::Get(); |
52 settings->RemoveSettingsProvider(&stub_settings_provider_); | 107 settings->RemoveSettingsProvider(&stub_settings_provider_); |
53 settings->AddSettingsProvider(device_settings_provider_); | |
54 LoginManagerTest::CleanUpOnMainThread(); | 108 LoginManagerTest::CleanUpOnMainThread(); |
55 } | 109 } |
56 | 110 |
57 protected: | 111 protected: |
58 void CheckOptionsUI(const User* user, bool is_primary) { | 112 void CheckOptionsUI(const User* user, bool is_owner, bool is_primary) { |
| 113 Browser* browser = CreateBrowserForUser(user); |
| 114 content::WebContents* contents = |
| 115 browser->tab_strip_model()->GetActiveWebContents(); |
| 116 |
| 117 for (size_t i = 0; i < sizeof(kPrefTests) / sizeof(kPrefTests[0]); i++) { |
| 118 CheckPreference(contents, |
| 119 kPrefTests[i].pref_name, |
| 120 !is_owner && kPrefTests[i].owner_only, |
| 121 !is_owner && kPrefTests[i].indicator ? "owner" : |
| 122 std::string()); |
| 123 } |
| 124 CheckBanner(contents, is_primary); |
| 125 CheckSharedSections(contents, is_primary); |
| 126 CheckAccountsOverlay(contents, is_owner); |
| 127 } |
| 128 |
| 129 // Creates a browser and navigates to the Settings page. |
| 130 Browser* CreateBrowserForUser(const User* user) { |
59 Profile* profile = UserManager::Get()->GetProfileByUser(user); | 131 Profile* profile = UserManager::Get()->GetProfileByUser(user); |
60 profile->GetPrefs()->SetString(prefs::kGoogleServicesUsername, | 132 profile->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
61 user->email()); | 133 user->email()); |
62 | 134 |
63 ui_test_utils::BrowserAddedObserver observer; | 135 ui_test_utils::BrowserAddedObserver observer; |
64 Browser* browser = CreateBrowser(profile); | 136 Browser* browser = CreateBrowser(profile); |
65 observer.WaitForSingleNewBrowser(); | 137 observer.WaitForSingleNewBrowser(); |
66 | 138 |
67 ui_test_utils::NavigateToURL(browser, | 139 ui_test_utils::NavigateToURL(browser, |
68 GURL("chrome://settings-frame")); | 140 GURL("chrome://settings-frame")); |
69 content::WebContents* contents = | 141 return browser; |
70 browser->tab_strip_model()->GetActiveWebContents(); | 142 } |
71 | 143 |
| 144 // Verifies a preference's disabled state and controlled-by indicator. |
| 145 void CheckPreference(content::WebContents* contents, |
| 146 std::string pref_name, |
| 147 bool disabled, |
| 148 std::string controlled_by) { |
| 149 bool success; |
| 150 std::string js_expression = base::StringPrintf( |
| 151 "var prefSelector = '[pref=\"%s\"]';" |
| 152 "var controlledBy = '%s';" |
| 153 "var input = document.querySelector(" |
| 154 " 'input' + prefSelector + ', select' + prefSelector);" |
| 155 "var success = false;" |
| 156 "if (input) {" |
| 157 " success = input.disabled == %d;" |
| 158 " var indicator = document.querySelector(input.tagName +" |
| 159 " prefSelector + ' + span span.controlled-setting-indicator');" |
| 160 " if (controlledBy) {" |
| 161 " success = success && indicator &&" |
| 162 " indicator.getAttribute('controlled-by') == controlledBy;" |
| 163 " } else {" |
| 164 " success = success && (!indicator ||" |
| 165 " !indicator.hasAttribute('controlled-by') ||" |
| 166 " indicator.getAttribute('controlled-by') == '')" |
| 167 " }" |
| 168 "}" |
| 169 "window.domAutomationController.send(!!success);", |
| 170 pref_name.c_str(), controlled_by.c_str(), disabled); |
| 171 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
| 172 contents, js_expression, &success)); |
| 173 EXPECT_TRUE(success); |
| 174 } |
| 175 |
| 176 // Verifies a checkbox's disabled state, controlled-by indicator and value. |
| 177 void CheckBooleanPreference(content::WebContents* contents, |
| 178 std::string pref_name, |
| 179 bool disabled, |
| 180 std::string controlled_by, |
| 181 bool expected_value) { |
| 182 CheckPreference(contents, pref_name, disabled, controlled_by); |
| 183 bool actual_value; |
| 184 std::string js_expression = base::StringPrintf( |
| 185 "window.domAutomationController.send(document.querySelector('" |
| 186 " input[type=\"checkbox\"][pref=\"%s\"]').checked);", |
| 187 pref_name.c_str()); |
| 188 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
| 189 contents, js_expression, &actual_value)); |
| 190 EXPECT_EQ(expected_value, actual_value); |
| 191 } |
| 192 |
| 193 // Verifies that the shared settings banner is visible only for |
| 194 // secondary users. |
| 195 void CheckBanner(content::WebContents* contents, |
| 196 bool is_primary) { |
72 bool banner_visible; | 197 bool banner_visible; |
73 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( | 198 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
74 contents, | 199 contents, |
75 "var e = document.getElementById('secondary-user-banner');" | 200 "var e = $('secondary-user-banner');" |
76 "var visible = e.offsetWidth > 0 && e.offsetHeight > 0;" | 201 "window.domAutomationController.send(e && !e.hidden);", |
77 "window.domAutomationController.send(visible);", | |
78 &banner_visible)); | 202 &banner_visible)); |
79 EXPECT_EQ(is_primary, !banner_visible); | 203 EXPECT_EQ(!is_primary, banner_visible); |
80 } | 204 } |
81 | 205 |
82 StubCrosSettingsProvider stub_settings_provider_; | 206 // Verifies that sections of shared settings have the appropriate indicator. |
| 207 void CheckSharedSections(content::WebContents* contents, |
| 208 bool is_primary) { |
| 209 // This only applies to the Internet options section. |
| 210 std::string controlled_by; |
| 211 ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
| 212 contents, |
| 213 "var e = document.querySelector(" |
| 214 " '#network-section-header span.controlled-setting-indicator');" |
| 215 "if (!e || !e.getAttribute('controlled-by')) {" |
| 216 " window.domAutomationController.send('');" |
| 217 "} else {" |
| 218 " window.domAutomationController.send(" |
| 219 " e.getAttribute('controlled-by'));" |
| 220 "}", |
| 221 &controlled_by)); |
| 222 EXPECT_EQ(!is_primary ? "shared" : std::string(), controlled_by); |
| 223 } |
| 224 |
| 225 // Checks the Accounts header and non-checkbox inputs. |
| 226 void CheckAccountsOverlay(content::WebContents* contents, bool is_owner) { |
| 227 // Set cros.accounts.allowGuest to false so we can test the accounts list. |
| 228 // This has to be done after the PRE_* test or we can't add the owner. |
| 229 stub_settings_provider_.Set( |
| 230 kAccountsPrefAllowNewUser, base::FundamentalValue(false)); |
| 231 |
| 232 bool success; |
| 233 std::string js_expression = base::StringPrintf( |
| 234 "var controlled = %d;" |
| 235 "var warning = $('ownerOnlyWarning');" |
| 236 "var userList = $('userList');" |
| 237 "var input = $('userNameEdit');" |
| 238 "var success;" |
| 239 "if (controlled)" |
| 240 " success = warning && !warning.hidden && userList.disabled &&" |
| 241 " input.disabled;" |
| 242 "else" |
| 243 " success = (!warning || warning.hidden) && !userList.disabled &&" |
| 244 " !input.disabled;" |
| 245 "window.domAutomationController.send(!!success);", |
| 246 !is_owner); |
| 247 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
| 248 contents, js_expression, &success)); |
| 249 EXPECT_TRUE(success) << "Accounts overlay incorrect for " << |
| 250 (is_owner ? "owner." : "non-owner."); |
| 251 } |
| 252 |
| 253 StubAccountSettingsProvider stub_settings_provider_; |
83 CrosSettingsProvider* device_settings_provider_; | 254 CrosSettingsProvider* device_settings_provider_; |
84 | 255 |
85 private: | 256 private: |
86 DISALLOW_COPY_AND_ASSIGN(SharedOptionsTest); | 257 DISALLOW_COPY_AND_ASSIGN(SharedOptionsTest); |
87 }; | 258 }; |
88 | 259 |
89 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_SharedOptions) { | 260 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_SharedOptions) { |
90 RegisterUser(kTestUsers[0]); | 261 RegisterUser(kTestOwner); |
91 RegisterUser(kTestUsers[1]); | 262 RegisterUser(kTestNonOwner); |
92 StartupUtils::MarkOobeCompleted(); | 263 StartupUtils::MarkOobeCompleted(); |
93 } | 264 } |
94 | 265 |
95 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, SharedOptions) { | 266 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, SharedOptions) { |
96 LoginUser(kTestUsers[0]); | 267 // Log in the owner first, then add a secondary user. |
| 268 LoginUser(kTestOwner); |
97 UserAddingScreen::Get()->Start(); | 269 UserAddingScreen::Get()->Start(); |
98 content::RunAllPendingInMessageLoop(); | 270 content::RunAllPendingInMessageLoop(); |
99 AddUser(kTestUsers[1]); | 271 AddUser(kTestNonOwner); |
100 | 272 |
101 UserManager* manager = UserManager::Get(); | 273 UserManager* manager = UserManager::Get(); |
102 ASSERT_EQ(2u, manager->GetLoggedInUsers().size()); | 274 ASSERT_EQ(2u, manager->GetLoggedInUsers().size()); |
| 275 { |
| 276 SCOPED_TRACE("Checking settings for owner, primary user."); |
| 277 CheckOptionsUI(manager->FindUser(manager->GetOwnerEmail()), true, true); |
| 278 } |
| 279 { |
| 280 SCOPED_TRACE("Checking settings for non-owner, secondary user."); |
| 281 CheckOptionsUI(manager->FindUser(kTestNonOwner), false, false); |
| 282 } |
| 283 // TODO(michaelpg): Add tests for non-primary owner and primary non-owner |
| 284 // when the owner-only multiprofile restriction is removed, probably M38. |
| 285 } |
103 | 286 |
104 CheckOptionsUI(manager->FindUser(kTestUsers[0]), true /* is_primary */); | 287 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_ScreenLockPreference) { |
105 CheckOptionsUI(manager->FindUser(kTestUsers[1]), false /* is_primary */); | 288 RegisterUser(kTestOwner); |
| 289 RegisterUser(kTestNonOwner); |
| 290 StartupUtils::MarkOobeCompleted(); |
| 291 } |
| 292 |
| 293 // Tests that a shared setting indicator appears for the auto-lock setting |
| 294 // when the user has the checkbox unselected but another user has enabled |
| 295 // auto-lock. (The checkbox is unset if the user's preference is false, |
| 296 // but if any other signed-in user has enabled this preference, the shared |
| 297 // setting indicator explains this.) |
| 298 IN_PROC_BROWSER_TEST_F(SharedOptionsTest, ScreenLockPreference) { |
| 299 LoginUser(kTestOwner); |
| 300 UserAddingScreen::Get()->Start(); |
| 301 content::RunAllPendingInMessageLoop(); |
| 302 AddUser(kTestNonOwner); |
| 303 |
| 304 UserManager* manager = UserManager::Get(); |
| 305 const User* user1 = manager->FindUser(kTestOwner); |
| 306 const User* user2 = manager->FindUser(kTestNonOwner); |
| 307 |
| 308 PrefService* prefs1 = manager->GetProfileByUser(user1)->GetPrefs(); |
| 309 PrefService* prefs2 = manager->GetProfileByUser(user2)->GetPrefs(); |
| 310 prefs1->SetBoolean(prefs::kEnableAutoScreenLock, false); |
| 311 prefs2->SetBoolean(prefs::kEnableAutoScreenLock, false); |
| 312 |
| 313 Browser* browser1 = CreateBrowserForUser(user1); |
| 314 Browser* browser2 = CreateBrowserForUser(user2); |
| 315 content::WebContents* contents1 = |
| 316 browser1->tab_strip_model()->GetActiveWebContents(); |
| 317 content::WebContents* contents2 = |
| 318 browser2->tab_strip_model()->GetActiveWebContents(); |
| 319 |
| 320 bool disabled = false; |
| 321 bool expected_value; |
| 322 std::string empty_controlled; |
| 323 std::string shared_controlled("shared"); |
| 324 |
| 325 // First test case: secondary user dependent on primary user. |
| 326 { |
| 327 SCOPED_TRACE("Screen lock false for both users"); |
| 328 expected_value = false; |
| 329 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 330 empty_controlled, expected_value); |
| 331 CheckBooleanPreference(contents2, prefs::kEnableAutoScreenLock, disabled, |
| 332 empty_controlled, expected_value); |
| 333 } |
| 334 // Set the preference to true for the primary user and check that the value |
| 335 // changes appropriately. |
| 336 prefs1->SetBoolean(prefs::kEnableAutoScreenLock, true); |
| 337 { |
| 338 SCOPED_TRACE("Screen lock true for primary user"); |
| 339 expected_value = true; |
| 340 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 341 empty_controlled, expected_value); |
| 342 } |
| 343 // Reload the secondary user's browser to see the updated controlled-by |
| 344 // indicator. Also reload the primary user's to make sure the setting still |
| 345 // starts out correctly. |
| 346 chrome::Reload(browser1, CURRENT_TAB); |
| 347 chrome::Reload(browser2, CURRENT_TAB); |
| 348 content::WaitForLoadStop(contents1); |
| 349 content::WaitForLoadStop(contents2); |
| 350 { |
| 351 SCOPED_TRACE("Screen lock true for primary user, false for secondary user"); |
| 352 expected_value = true; |
| 353 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 354 empty_controlled, expected_value); |
| 355 expected_value = false; |
| 356 CheckBooleanPreference(contents2, prefs::kEnableAutoScreenLock, disabled, |
| 357 shared_controlled, expected_value); |
| 358 } |
| 359 // Set the preference to true for the secondary user and check that the |
| 360 // indicator disappears. |
| 361 prefs2->SetBoolean(prefs::kEnableAutoScreenLock, true); |
| 362 { |
| 363 SCOPED_TRACE("Screen lock true for secondary user"); |
| 364 expected_value = true; |
| 365 CheckBooleanPreference(contents2, prefs::kEnableAutoScreenLock, disabled, |
| 366 empty_controlled, expected_value); |
| 367 } |
| 368 |
| 369 // Second test case: primary user dependent on secondary user. |
| 370 chrome::Reload(browser1, CURRENT_TAB); |
| 371 chrome::Reload(browser2, CURRENT_TAB); |
| 372 content::WaitForLoadStop(contents1); |
| 373 content::WaitForLoadStop(contents2); |
| 374 { |
| 375 SCOPED_TRACE("Screen lock true for both users"); |
| 376 expected_value = true; |
| 377 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 378 empty_controlled, expected_value); |
| 379 CheckBooleanPreference(contents2, prefs::kEnableAutoScreenLock, disabled, |
| 380 empty_controlled, expected_value); |
| 381 } |
| 382 // Set the preference to false for the primary user and check that the |
| 383 // value changes correctly. |
| 384 prefs1->SetBoolean(prefs::kEnableAutoScreenLock, false); |
| 385 { |
| 386 SCOPED_TRACE("Screen lock false for primary user"); |
| 387 expected_value = false; |
| 388 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 389 shared_controlled, expected_value); |
| 390 } |
| 391 // The primary user should now see a shared setting indicator. |
| 392 chrome::Reload(browser1, CURRENT_TAB); |
| 393 chrome::Reload(browser2, CURRENT_TAB); |
| 394 content::WaitForLoadStop(contents1); |
| 395 content::WaitForLoadStop(contents2); |
| 396 { |
| 397 SCOPED_TRACE("Screen lock false for primary user, true for secondary user"); |
| 398 expected_value = false; |
| 399 CheckBooleanPreference(contents1, prefs::kEnableAutoScreenLock, disabled, |
| 400 shared_controlled, expected_value); |
| 401 expected_value = true; |
| 402 CheckBooleanPreference(contents2, prefs::kEnableAutoScreenLock, disabled, |
| 403 empty_controlled, expected_value); |
| 404 } |
106 } | 405 } |
107 | 406 |
108 } // namespace chromeos | 407 } // namespace chromeos |
OLD | NEW |