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