1// Copyright (c) 2012 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 "chrome/browser/ui/webui/options/preferences_browsertest.h"
6
7#include <iostream>
8#include <sstream>
9
10#include "base/callback.h"
11#include "base/json/json_reader.h"
12#include "base/json/json_writer.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/prefs/pref_service.h"
15#include "base/values.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/tabs/tab_strip_model.h"
20#include "chrome/common/pref_names.h"
21#include "chrome/common/url_constants.h"
22#include "chrome/test/base/ui_test_utils.h"
23#include "components/policy/core/browser/browser_policy_connector.h"
24#include "components/policy/core/common/external_data_fetcher.h"
25#include "components/policy/core/common/policy_map.h"
26#include "components/policy/core/common/policy_types.h"
27#include "content/public/browser/notification_details.h"
28#include "content/public/browser/notification_source.h"
29#include "content/public/browser/render_view_host.h"
30#include "content/public/browser/web_contents.h"
31#include "content/public/test/browser_test_utils.h"
32#include "policy/policy_constants.h"
33#include "testing/gtest/include/gtest/gtest.h"
34#include "url/gurl.h"
35
36#if defined(OS_CHROMEOS)
37#include "base/strings/stringprintf.h"
38#include "chrome/browser/browser_process.h"
39#include "chrome/browser/chromeos/net/proxy_config_handler.h"
40#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
41#include "chrome/browser/chromeos/policy/stub_enterprise_install_attributes.h"
42#include "chrome/browser/chromeos/proxy_cros_settings_parser.h"
43#include "chrome/browser/chromeos/settings/cros_settings.h"
44#include "chrome/browser/prefs/proxy_config_dictionary.h"
45#include "chromeos/dbus/dbus_thread_manager.h"
46#include "chromeos/dbus/shill_profile_client.h"
47#include "chromeos/dbus/shill_service_client.h"
48#include "chromeos/network/network_state.h"
49#include "chromeos/network/network_state_handler.h"
50#include "chromeos/settings/cros_settings_names.h"
51#include "content/public/test/test_utils.h"
52#include "third_party/cros_system_api/dbus/service_constants.h"
53#endif
54
55using testing::AllOf;
56using testing::Mock;
57using testing::Property;
58using testing::Return;
59using testing::_;
60
61namespace base {
62
63// Helper for using EXPECT_EQ() with base::Value.
64bool operator==(const base::Value& first, const base::Value& second) {
65  return first.Equals(&second);
66}
67
68// Helper for pretty-printing the contents of base::Value in case of failures.
69void PrintTo(const base::Value& value, std::ostream* stream) {
70  std::string json;
71  JSONWriter::Write(&value, &json);
72  *stream << json;
73}
74
75}  // namespace base
76
77// Googlemock matcher for base::Value.
78MATCHER_P(EqualsValue, expected, "") {
79  return arg && arg->Equals(expected);
80}
81
82PreferencesBrowserTest::PreferencesBrowserTest() {
83}
84
85PreferencesBrowserTest::~PreferencesBrowserTest() {
86}
87
88// Navigates to the settings page, causing the JavaScript pref handling code to
89// load and injects JavaScript testing code.
90void PreferencesBrowserTest::SetUpOnMainThread() {
91  ui_test_utils::NavigateToURL(browser(),
92                               GURL(chrome::kChromeUISettingsFrameURL));
93  SetUpPrefs();
94}
95
96void PreferencesBrowserTest::SetUpPrefs() {
97  content::WebContents* web_contents =
98      browser()->tab_strip_model()->GetActiveWebContents();
99  ASSERT_TRUE(web_contents);
100  render_view_host_ = web_contents->GetRenderViewHost();
101  ASSERT_TRUE(render_view_host_);
102  pref_service_ = browser()->profile()->GetPrefs();
103  pref_change_registrar_.Init(pref_service_);
104  ASSERT_TRUE(content::ExecuteScript(render_view_host_,
105      "function TestEnv() {"
106      "  this.sentinelName_ = 'download.prompt_for_download';"
107      "  this.prefs_ = [];"
108      "  TestEnv.instance_ = this;"
109      "}"
110      ""
111      "TestEnv.handleEvent = function(event) {"
112      "  var env = TestEnv.instance_;"
113      "  var name = event.type;"
114      "  env.removePrefListener_(name);"
115      "  if (name == TestEnv.sentinelName_)"
116      "    env.sentinelValue_ = event.value.value;"
117      "  else"
118      "    env.reply_[name] = event.value;"
119      "  if (env.fetching_ && !--env.fetching_ ||"
120      "      !env.fetching_ && name == env.sentinelName_) {"
121      "    env.removePrefListeners_();"
122      "    window.domAutomationController.send(JSON.stringify(env.reply_));"
123      "    delete env.reply_;"
124      "  }"
125      "};"
126      ""
127      "TestEnv.prototype = {"
128      "  addPrefListener_: function(name) {"
129      "    Preferences.getInstance().addEventListener(name,"
130      "                                               TestEnv.handleEvent);"
131      "  },"
132      ""
133      "  addPrefListeners_: function() {"
134      "    for (var i in this.prefs_)"
135      "      this.addPrefListener_(this.prefs_[i]);"
136      "  },"
137      ""
138      "  removePrefListener_: function(name) {"
139      "    Preferences.getInstance().removeEventListener(name,"
140      "                                                  TestEnv.handleEvent);"
141      "  },"
142      ""
143      "  removePrefListeners_: function() {"
144      "    for (var i in this.prefs_)"
145      "      this.removePrefListener_(this.prefs_[i]);"
146      "  },"
147      ""
148      ""
149      "  addPref: function(name) {"
150      "    this.prefs_.push(name);"
151      "  },"
152      ""
153      "  setupAndReply: function() {"
154      "    this.reply_ = {};"
155      "    Preferences.instance_ = new Preferences();"
156      "    this.addPref(this.sentinelName_);"
157      "    this.fetching_ = this.prefs_.length;"
158      "    this.addPrefListeners_();"
159      "    Preferences.getInstance().initialize();"
160      "  },"
161      ""
162      "  runAndReply: function(test) {"
163      "    this.reply_ = {};"
164      "    this.addPrefListeners_();"
165      "    test();"
166      "    this.sentinelValue_ = !this.sentinelValue_;"
167      "    Preferences.setBooleanPref(this.sentinelName_, this.sentinelValue_,"
168      "                               true);"
169      "  },"
170      ""
171      "  startObserving: function() {"
172      "    this.reply_ = {};"
173      "    this.addPrefListeners_();"
174      "  },"
175      ""
176      "  finishObservingAndReply: function() {"
177      "    this.sentinelValue_ = !this.sentinelValue_;"
178      "    Preferences.setBooleanPref(this.sentinelName_, this.sentinelValue_,"
179      "                               true);"
180      "  }"
181      "};"));
182}
183
184// Forwards notifications received when pref values change in the backend.
185void PreferencesBrowserTest::OnPreferenceChanged(const std::string& pref_name) {
186  OnCommit(pref_service_->FindPreference(pref_name.c_str()));
187}
188
189void PreferencesBrowserTest::SetUpInProcessBrowserTestFixture() {
190  // Sets up a mock policy provider for user and device policies.
191  EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
192      .WillRepeatedly(Return(true));
193  policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
194      &policy_provider_);
195};
196
197void PreferencesBrowserTest::SetUserPolicies(
198    const std::vector<std::string>& names,
199    const std::vector<base::Value*>& values,
200    policy::PolicyLevel level) {
201  policy::PolicyMap map;
202  for (size_t i = 0; i < names.size(); ++i) {
203    map.Set(names[i], level, policy::POLICY_SCOPE_USER,
204            values[i]->DeepCopy(), NULL);
205  }
206  policy_provider_.UpdateChromePolicy(map);
207}
208
209void PreferencesBrowserTest::ClearUserPolicies() {
210  policy::PolicyMap empty_policy_map;
211  policy_provider_.UpdateChromePolicy(empty_policy_map);
212}
213
214void PreferencesBrowserTest::SetUserValues(
215    const std::vector<std::string>& names,
216    const std::vector<base::Value*>& values) {
217  for (size_t i = 0; i < names.size(); ++i) {
218    pref_service_->Set(names[i].c_str(), *values[i]);
219  }
220}
221
222void PreferencesBrowserTest::VerifyKeyValue(const base::DictionaryValue& dict,
223                                            const std::string& key,
224                                            const base::Value& expected) {
225  const base::Value* actual = NULL;
226  EXPECT_TRUE(dict.Get(key, &actual)) << "Was checking key: " << key;
227  if (actual)
228    EXPECT_EQ(expected, *actual) << "Was checking key: " << key;
229}
230
231void PreferencesBrowserTest::VerifyPref(const base::DictionaryValue* prefs,
232                                        const std::string& name,
233                                        const base::Value* value,
234                                        const std::string& controlledBy,
235                                        bool disabled,
236                                        bool uncommitted) {
237  const base::Value* pref = NULL;
238  const base::DictionaryValue* dict = NULL;
239  ASSERT_TRUE(prefs->GetWithoutPathExpansion(name, &pref));
240  ASSERT_TRUE(pref->GetAsDictionary(&dict));
241  VerifyKeyValue(*dict, "value", *value);
242  if (!controlledBy.empty())
243    VerifyKeyValue(*dict, "controlledBy", base::StringValue(controlledBy));
244  else
245    EXPECT_FALSE(dict->HasKey("controlledBy"));
246
247  if (disabled)
248    VerifyKeyValue(*dict, "disabled", base::FundamentalValue(true));
249  else if (dict->HasKey("disabled"))
250    VerifyKeyValue(*dict, "disabled", base::FundamentalValue(false));
251
252  if (uncommitted)
253    VerifyKeyValue(*dict, "uncommitted", base::FundamentalValue(true));
254  else if (dict->HasKey("uncommitted"))
255    VerifyKeyValue(*dict, "uncommitted", base::FundamentalValue(false));
256}
257
258void PreferencesBrowserTest::VerifyObservedPref(const std::string& json,
259                                                const std::string& name,
260                                                const base::Value* value,
261                                                const std::string& controlledBy,
262                                                bool disabled,
263                                                bool uncommitted) {
264  scoped_ptr<base::Value> observed_value_ptr(base::JSONReader::Read(json));
265  const base::DictionaryValue* observed_dict;
266  ASSERT_TRUE(observed_value_ptr.get());
267  ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
268  VerifyPref(observed_dict, name, value, controlledBy, disabled, uncommitted);
269}
270
271void PreferencesBrowserTest::VerifyObservedPrefs(
272    const std::string& json,
273    const std::vector<std::string>& names,
274    const std::vector<base::Value*>& values,
275    const std::string& controlledBy,
276    bool disabled,
277    bool uncommitted) {
278  scoped_ptr<base::Value> observed_value_ptr(base::JSONReader::Read(json));
279  const base::DictionaryValue* observed_dict;
280  ASSERT_TRUE(observed_value_ptr.get());
281  ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
282  for (size_t i = 0; i < names.size(); ++i) {
283    VerifyPref(observed_dict, names[i], values[i], controlledBy, disabled,
284               uncommitted);
285  }
286}
287
288void PreferencesBrowserTest::ExpectNoCommit(const std::string& name) {
289  pref_change_registrar_.Add(
290      name.c_str(),
291      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
292                 base::Unretained(this)));
293  EXPECT_CALL(*this, OnCommit(Property(&PrefService::Preference::name, name)))
294      .Times(0);
295}
296
297void PreferencesBrowserTest::ExpectSetCommit(const std::string& name,
298                                             const base::Value* value) {
299  pref_change_registrar_.Add(
300      name.c_str(),
301      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
302                 base::Unretained(this)));
303  EXPECT_CALL(*this, OnCommit(AllOf(
304      Property(&PrefService::Preference::name, name),
305      Property(&PrefService::Preference::IsUserControlled, true),
306      Property(&PrefService::Preference::GetValue, EqualsValue(value)))));
307}
308
309void PreferencesBrowserTest::ExpectClearCommit(const std::string& name) {
310  pref_change_registrar_.Add(
311      name.c_str(),
312      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
313                 base::Unretained(this)));
314  EXPECT_CALL(*this, OnCommit(AllOf(
315      Property(&PrefService::Preference::name, name),
316      Property(&PrefService::Preference::IsUserControlled, false))));
317}
318
319void PreferencesBrowserTest::VerifyAndClearExpectations() {
320  Mock::VerifyAndClearExpectations(this);
321  pref_change_registrar_.RemoveAll();
322}
323
324void PreferencesBrowserTest::SetupJavaScriptTestEnvironment(
325    const std::vector<std::string>& pref_names,
326    std::string* observed_json) const {
327  std::stringstream javascript;
328  javascript << "var testEnv = new TestEnv();";
329  for (std::vector<std::string>::const_iterator name = pref_names.begin();
330       name != pref_names.end(); ++name) {
331    javascript << "testEnv.addPref('" << name->c_str() << "');";
332  }
333  javascript << "testEnv.setupAndReply();";
334  std::string temp_observed_json;
335  if (!observed_json)
336    observed_json = &temp_observed_json;
337  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
338      render_view_host_, javascript.str(), observed_json));
339}
340
341void PreferencesBrowserTest::SetPref(const std::string& name,
342                                     const std::string& type,
343                                     const base::Value* value,
344                                     bool commit,
345                                     std::string* observed_json) {
346  scoped_ptr<base::Value> commit_ptr(new base::FundamentalValue(commit));
347  std::stringstream javascript;
348  javascript << "testEnv.runAndReply(function() {"
349             << "  Preferences.set" << type << "Pref("
350             << "      '" << name << "',"
351             << "      " << *value << ","
352             << "      " << *commit_ptr << ");"
353             << "});";
354  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
355      render_view_host_, javascript.str(), observed_json));
356}
357
358void PreferencesBrowserTest::VerifySetPref(const std::string& name,
359                                           const std::string& type,
360                                           const base::Value* value,
361                                           bool commit) {
362  if (commit)
363    ExpectSetCommit(name, value);
364  else
365    ExpectNoCommit(name);
366  std::string observed_json;
367  SetPref(name, type, value, commit, &observed_json);
368  VerifyObservedPref(observed_json, name, value, std::string(), false, !commit);
369  VerifyAndClearExpectations();
370}
371
372void PreferencesBrowserTest::VerifyClearPref(const std::string& name,
373                                             const base::Value* value,
374                                             bool commit) {
375  if (commit)
376    ExpectClearCommit(name);
377  else
378    ExpectNoCommit(name);
379  scoped_ptr<base::Value> commit_ptr(new base::FundamentalValue(commit));
380  std::string commit_json;
381  base::JSONWriter::Write(commit_ptr.get(), &commit_json);
382  std::stringstream javascript;
383  javascript << "testEnv.runAndReply(function() {"
384             << "    Preferences.clearPref("
385             << "      '" << name.c_str() << "',"
386             << "      " << commit_json.c_str() << ");});";
387  std::string observed_json;
388  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
389      render_view_host_, javascript.str(), &observed_json));
390  VerifyObservedPref(observed_json, name, value, "recommended", false, !commit);
391  VerifyAndClearExpectations();
392}
393
394void PreferencesBrowserTest::VerifyCommit(const std::string& name,
395                                          const base::Value* value,
396                                          const std::string& controlledBy) {
397  std::stringstream javascript;
398  javascript << "testEnv.runAndReply(function() {"
399             << "    Preferences.getInstance().commitPref("
400             << "        '" << name.c_str() << "');});";
401  std::string observed_json;
402  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
403      render_view_host_, javascript.str(), &observed_json));
404  VerifyObservedPref(observed_json, name, value, controlledBy, false, false);
405}
406
407void PreferencesBrowserTest::VerifySetCommit(const std::string& name,
408                                             const base::Value* value) {
409  ExpectSetCommit(name, value);
410  VerifyCommit(name, value, std::string());
411  VerifyAndClearExpectations();
412}
413
414void PreferencesBrowserTest::VerifyClearCommit(const std::string& name,
415                                               const base::Value* value) {
416  ExpectClearCommit(name);
417  VerifyCommit(name, value, "recommended");
418  VerifyAndClearExpectations();
419}
420
421void PreferencesBrowserTest::VerifyRollback(const std::string& name,
422                                            const base::Value* value,
423                                            const std::string& controlledBy) {
424  ExpectNoCommit(name);
425  std::stringstream javascript;
426  javascript << "testEnv.runAndReply(function() {"
427             << "    Preferences.getInstance().rollbackPref("
428             << "        '" << name.c_str() << "');});";
429  std::string observed_json;
430  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
431      render_view_host_, javascript.str(), &observed_json));
432  VerifyObservedPref(observed_json, name, value, controlledBy, false, true);
433  VerifyAndClearExpectations();
434}
435
436void PreferencesBrowserTest::StartObserving() {
437  ASSERT_TRUE(content::ExecuteScript(
438      render_view_host_, "testEnv.startObserving();"));
439}
440
441void PreferencesBrowserTest::FinishObserving(std::string* observed_json) {
442  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
443      render_view_host_,
444      "testEnv.finishObservingAndReply();",
445      observed_json));
446}
447
448void PreferencesBrowserTest::UseDefaultTestPrefs(bool includeListPref) {
449  // Boolean pref.
450  types_.push_back("Boolean");
451  pref_names_.push_back(prefs::kAlternateErrorPagesEnabled);
452  policy_names_.push_back(policy::key::kAlternateErrorPagesEnabled);
453  non_default_values_.push_back(new base::FundamentalValue(false));
454
455  // Integer pref.
456  types_.push_back("Integer");
457  pref_names_.push_back(prefs::kRestoreOnStartup);
458  policy_names_.push_back(policy::key::kRestoreOnStartup);
459  non_default_values_.push_back(new base::FundamentalValue(4));
460
461  // List pref.
462  if (includeListPref) {
463    types_.push_back("List");
464    pref_names_.push_back(prefs::kURLsToRestoreOnStartup);
465    policy_names_.push_back(policy::key::kRestoreOnStartupURLs);
466    base::ListValue* list = new base::ListValue;
467    list->Append(new base::StringValue("http://www.example.com"));
468    list->Append(new base::StringValue("http://example.com"));
469    non_default_values_.push_back(list);
470  }
471
472  // Retrieve default values.
473  for (std::vector<std::string>::const_iterator name = pref_names_.begin();
474        name != pref_names_.end(); ++name) {
475    default_values_.push_back(
476        pref_service_->GetDefaultPrefValue(name->c_str())->DeepCopy());
477  }
478}
479
480// Verifies that initializing the JavaScript Preferences class fires the correct
481// notifications in JavaScript.
482IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, FetchPrefs) {
483  UseDefaultTestPrefs(true);
484  std::string observed_json;
485
486  // Verify notifications when default values are in effect.
487  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
488  VerifyObservedPrefs(
489      observed_json, pref_names_, default_values_.get(),
490      std::string(), false, false);
491
492  // Verify notifications when recommended values are in effect.
493  SetUserPolicies(policy_names_, non_default_values_.get(),
494                  policy::POLICY_LEVEL_RECOMMENDED);
495  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
496  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_.get(),
497                      "recommended", false, false);
498
499  // Verify notifications when mandatory values are in effect.
500  SetUserPolicies(policy_names_, non_default_values_.get(),
501                  policy::POLICY_LEVEL_MANDATORY);
502  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
503  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_.get(),
504                      "policy", true, false);
505
506  // Verify notifications when user-modified values are in effect.
507  ClearUserPolicies();
508  SetUserValues(pref_names_, non_default_values_.get());
509  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
510  VerifyObservedPrefs(observed_json,
511                      pref_names_,
512                      non_default_values_.get(),
513                      std::string(),
514                      false,
515                      false);
516}
517
518// Verifies that setting a user-modified pref value through the JavaScript
519// Preferences class fires the correct notification in JavaScript and causes the
520// change to be committed to the C++ backend.
521IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, SetPrefs) {
522  UseDefaultTestPrefs(false);
523
524  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
525  for (size_t i = 0; i < pref_names_.size(); ++i) {
526    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], true);
527  }
528}
529
530// Verifies that clearing a user-modified pref value through the JavaScript
531// Preferences class fires the correct notification in JavaScript and causes the
532// change to be committed to the C++ backend.
533IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, ClearPrefs) {
534  UseDefaultTestPrefs(false);
535
536  SetUserPolicies(policy_names_, default_values_.get(),
537                  policy::POLICY_LEVEL_RECOMMENDED);
538  SetUserValues(pref_names_, non_default_values_.get());
539  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
540  for (size_t i = 0; i < pref_names_.size(); ++i) {
541    VerifyClearPref(pref_names_[i], default_values_[i], true);
542  }
543}
544
545// Verifies that when the user-modified value of a dialog pref is set and the
546// change then committed through the JavaScript Preferences class, the correct
547// notifications fire and a commit to the C++ backend occurs in the latter step
548// only.
549IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetCommit) {
550  UseDefaultTestPrefs(false);
551
552  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
553  for (size_t i = 0; i < pref_names_.size(); ++i) {
554    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
555    VerifySetCommit(pref_names_[i], non_default_values_[i]);
556  }
557}
558
559// Verifies that when the user-modified value of a dialog pref is set and the
560// change then rolled back through the JavaScript Preferences class, the correct
561// notifications fire and no commit to the C++ backend occurs.
562IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetRollback) {
563  UseDefaultTestPrefs(false);
564
565  // Verify behavior when default values are in effect.
566  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
567  for (size_t i = 0; i < pref_names_.size(); ++i) {
568    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
569    VerifyRollback(pref_names_[i], default_values_[i], std::string());
570  }
571
572  // Verify behavior when recommended values are in effect.
573  SetUserPolicies(policy_names_, default_values_.get(),
574                  policy::POLICY_LEVEL_RECOMMENDED);
575  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
576  for (size_t i = 0; i < pref_names_.size(); ++i) {
577    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
578    VerifyRollback(pref_names_[i], default_values_[i], "recommended");
579  }
580}
581
582// Verifies that when the user-modified value of a dialog pref is cleared and
583// the change then committed through the JavaScript Preferences class, the
584// correct notifications fire and a commit to the C++ backend occurs in the
585// latter step only.
586IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearCommit) {
587  UseDefaultTestPrefs(false);
588
589  SetUserPolicies(policy_names_, default_values_.get(),
590                  policy::POLICY_LEVEL_RECOMMENDED);
591  SetUserValues(pref_names_, non_default_values_.get());
592  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
593  for (size_t i = 0; i < pref_names_.size(); ++i) {
594    VerifyClearPref(pref_names_[i], default_values_[i], false);
595    VerifyClearCommit(pref_names_[i], default_values_[i]);
596  }
597}
598
599// Verifies that when the user-modified value of a dialog pref is cleared and
600// the change then rolled back through the JavaScript Preferences class, the
601// correct notifications fire and no commit to the C++ backend occurs.
602IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearRollback) {
603  UseDefaultTestPrefs(false);
604
605  SetUserPolicies(policy_names_, default_values_.get(),
606                  policy::POLICY_LEVEL_RECOMMENDED);
607  SetUserValues(pref_names_, non_default_values_.get());
608  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
609  for (size_t i = 0; i < pref_names_.size(); ++i) {
610    VerifyClearPref(pref_names_[i], default_values_[i], false);
611    VerifyRollback(pref_names_[i], non_default_values_[i], std::string());
612  }
613}
614
615// Verifies that when preference values change in the C++ backend, the correct
616// notifications fire in JavaScript.
617IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, NotificationsOnBackendChanges) {
618  UseDefaultTestPrefs(false);
619  std::string observed_json;
620
621  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
622
623  // Verify notifications when recommended values come into effect.
624  StartObserving();
625  SetUserPolicies(policy_names_, non_default_values_.get(),
626                  policy::POLICY_LEVEL_RECOMMENDED);
627  FinishObserving(&observed_json);
628  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_.get(),
629                      "recommended", false, false);
630
631  // Verify notifications when mandatory values come into effect.
632  StartObserving();
633  SetUserPolicies(policy_names_, non_default_values_.get(),
634                  policy::POLICY_LEVEL_MANDATORY);
635  FinishObserving(&observed_json);
636  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_.get(),
637                      "policy", true, false);
638
639  // Verify notifications when default values come into effect.
640  StartObserving();
641  ClearUserPolicies();
642  FinishObserving(&observed_json);
643  VerifyObservedPrefs(
644      observed_json, pref_names_, default_values_.get(),
645      std::string(), false, false);
646
647  // Verify notifications when user-modified values come into effect.
648  StartObserving();
649  SetUserValues(pref_names_, non_default_values_.get());
650  FinishObserving(&observed_json);
651  VerifyObservedPrefs(observed_json,
652                      pref_names_,
653                      non_default_values_.get(),
654                      std::string(),
655                      false,
656                      false);
657}
658
659#if defined(OS_CHROMEOS)
660
661// Verifies that initializing the JavaScript Preferences class fires the correct
662// notifications in JavaScript for pref values handled by the
663// CoreChromeOSOptionsHandler class.
664IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, ChromeOSDeviceFetchPrefs) {
665  std::string observed_json;
666
667  // Boolean pref.
668  pref_names_.push_back(chromeos::kAccountsPrefAllowGuest);
669  default_values_.push_back(new base::FundamentalValue(true));
670
671  // String pref.
672  pref_names_.push_back(chromeos::kReleaseChannel);
673  default_values_.push_back(new base::StringValue(""));
674
675  // List pref.
676  pref_names_.push_back(chromeos::kAccountsPrefUsers);
677  default_values_.push_back(new base::ListValue);
678
679  // Verify notifications when default values are in effect.
680  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
681  VerifyObservedPrefs(observed_json, pref_names_, default_values_.get(),
682                      "owner", true, false);
683}
684
685// Verifies that initializing the JavaScript Preferences class fires the correct
686// notifications in JavaScript for non-privileged pref values handled by the
687// CoreChromeOSOptionsHandler class.
688IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest,
689                       ChromeOSDeviceFetchNonPrivilegedPrefs) {
690  ScopedVector<base::Value> decorated_non_default_values;
691  std::string observed_json;
692
693  // Non-privileged string pref.
694  pref_names_.push_back(chromeos::kSystemTimezone);
695  default_values_.push_back(new base::StringValue("America/Los_Angeles"));
696  non_default_values_.push_back(new base::StringValue("America/New_York"));
697  decorated_non_default_values.push_back(
698      non_default_values_.back()->DeepCopy());
699
700  // Verify notifications when default values are in effect.
701  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
702  VerifyObservedPrefs(observed_json, pref_names_, default_values_.get(),
703                      std::string(), false, false);
704
705  chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
706  cros_settings->Set(pref_names_[0], *non_default_values_[0]);
707
708  // Verify notifications when non-default values are in effect.
709  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
710  VerifyObservedPrefs(observed_json, pref_names_,
711                      decorated_non_default_values.get(),
712                      std::string(), false, false);
713}
714
715class ManagedPreferencesBrowserTest : public PreferencesBrowserTest {
716 protected:
717  // PreferencesBrowserTest implementation:
718  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
719    // Set up fake install attributes.
720    scoped_ptr<policy::StubEnterpriseInstallAttributes> attributes(
721        new policy::StubEnterpriseInstallAttributes());
722    attributes->SetDomain("example.com");
723    attributes->SetRegistrationUser("user@example.com");
724    policy::BrowserPolicyConnectorChromeOS::SetInstallAttributesForTesting(
725        attributes.release());
726
727    PreferencesBrowserTest::SetUpInProcessBrowserTestFixture();
728  }
729};
730
731// Verifies that initializing the JavaScript Preferences class fires the correct
732// notifications in JavaScript for pref values handled by the
733// CoreChromeOSOptionsHandler class for a managed device.
734IN_PROC_BROWSER_TEST_F(ManagedPreferencesBrowserTest,
735                       ChromeOSDeviceFetchPrefs) {
736  ScopedVector<base::Value> decorated_non_default_values;
737  std::string observed_json;
738
739  // Boolean pref.
740  pref_names_.push_back(chromeos::kAccountsPrefAllowGuest);
741  non_default_values_.push_back(new base::FundamentalValue(false));
742  decorated_non_default_values.push_back(
743      non_default_values_.back()->DeepCopy());
744
745  // String pref.
746  pref_names_.push_back(chromeos::kReleaseChannel);
747  non_default_values_.push_back(new base::StringValue("stable-channel"));
748  decorated_non_default_values.push_back(
749      non_default_values_.back()->DeepCopy());
750
751  // List pref.
752  pref_names_.push_back(chromeos::kAccountsPrefUsers);
753  base::ListValue* list = new base::ListValue;
754  list->Append(new base::StringValue("me@google.com"));
755  list->Append(new base::StringValue("you@google.com"));
756  non_default_values_.push_back(list);
757  list = new base::ListValue;
758  base::DictionaryValue* dict = new base::DictionaryValue;
759  dict->SetString("username", "me@google.com");
760  dict->SetString("name", "me@google.com");
761  dict->SetString("email", "");
762  dict->SetBoolean("owner", false);
763  list->Append(dict);
764  dict = new base::DictionaryValue;
765  dict->SetString("username", "you@google.com");
766  dict->SetString("name", "you@google.com");
767  dict->SetString("email", "");
768  dict->SetBoolean("owner", false);
769  list->Append(dict);
770  decorated_non_default_values.push_back(list);
771
772  chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
773  for (size_t i = 0; i < pref_names_.size(); ++i) {
774    cros_settings->Set(pref_names_[i], *non_default_values_[i]);
775  }
776
777  // Verify notifications when mandatory values are in effect.
778  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
779  VerifyObservedPrefs(observed_json, pref_names_,
780                      decorated_non_default_values.get(),
781                      "policy", true, false);
782}
783
784// Verifies that initializing the JavaScript Preferences class fires the correct
785// notifications in JavaScript for non-privileged pref values handled by the
786// CoreChromeOSOptionsHandler class for a managed device.
787IN_PROC_BROWSER_TEST_F(ManagedPreferencesBrowserTest,
788                       ChromeOSDeviceFetchNonPrivilegedPrefs) {
789  ScopedVector<base::Value> decorated_non_default_values;
790  std::string observed_json;
791
792  // Non-privileged string pref.
793  pref_names_.push_back(chromeos::kSystemTimezone);
794  non_default_values_.push_back(new base::StringValue("America/New_York"));
795  decorated_non_default_values.push_back(
796      non_default_values_.back()->DeepCopy());
797
798  // Verify notifications when mandatory values are in effect.
799  chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
800  cros_settings->Set(pref_names_[0], *non_default_values_[0]);
801
802  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
803  VerifyObservedPrefs(observed_json, pref_names_,
804                      decorated_non_default_values.get(),
805                      std::string(), false, false);
806}
807
808namespace {
809
810const char* kUserProfilePath = "user_profile";
811
812}  // namespace
813
814class ProxyPreferencesBrowserTest : public PreferencesBrowserTest {
815 public:
816  virtual void SetUpOnMainThread() OVERRIDE {
817    SetupNetworkEnvironment();
818    content::RunAllPendingInMessageLoop();
819
820    scoped_ptr<base::DictionaryValue> proxy_config_dict(
821        ProxyConfigDictionary::CreateFixedServers("127.0.0.1:8080",
822                                                  "*.google.com, 1.2.3.4:22"));
823
824    ProxyConfigDictionary proxy_config(proxy_config_dict.get());
825
826    const chromeos::NetworkState* network = GetDefaultNetwork();
827    ASSERT_TRUE(network);
828    chromeos::proxy_config::SetProxyConfigForNetwork(proxy_config, *network);
829
830    std::string url = base::StringPrintf("%s?network=%s",
831                                         chrome::kChromeUIProxySettingsURL,
832                                         network->path().c_str());
833
834    ui_test_utils::NavigateToURL(browser(), GURL(url));
835    SetUpPrefs();
836  }
837
838 protected:
839  void SetupNetworkEnvironment() {
840    chromeos::ShillProfileClient::TestInterface* profile_test =
841        chromeos::DBusThreadManager::Get()->GetShillProfileClient()
842            ->GetTestInterface();
843    chromeos::ShillServiceClient::TestInterface* service_test =
844        chromeos::DBusThreadManager::Get()->GetShillServiceClient()
845            ->GetTestInterface();
846
847    profile_test->AddProfile(kUserProfilePath, "user");
848
849    service_test->ClearServices();
850    service_test->AddService("stub_ethernet",
851                             "stub_ethernet_guid",
852                             "eth0",
853                             shill::kTypeEthernet,
854                             shill::kStateOnline,
855                             true /* add_to_visible */ );
856    service_test->SetServiceProperty("stub_ethernet",
857                                     shill::kProfileProperty,
858                                     base::StringValue(kUserProfilePath));
859    profile_test->AddService(kUserProfilePath, "stub_wifi2");
860  }
861
862  void SetONCPolicy(const char* policy_name, policy::PolicyScope scope) {
863    std::string onc_policy =
864        "{ \"NetworkConfigurations\": ["
865        "    { \"GUID\": \"stub_ethernet_guid\","
866        "      \"Type\": \"Ethernet\","
867        "      \"Name\": \"My Ethernet\","
868        "      \"Ethernet\": {"
869        "        \"Authentication\": \"None\" },"
870        "      \"ProxySettings\": {"
871        "        \"PAC\": \"http://domain.com/x\","
872        "        \"Type\": \"PAC\" }"
873        "    }"
874        "  ],"
875        "  \"Type\": \"UnencryptedConfiguration\""
876        "}";
877
878    policy::PolicyMap map;
879    map.Set(policy_name,
880            policy::POLICY_LEVEL_MANDATORY,
881            scope,
882            new base::StringValue(onc_policy),
883            NULL);
884    policy_provider_.UpdateChromePolicy(map);
885
886    content::RunAllPendingInMessageLoop();
887  }
888
889  const chromeos::NetworkState* GetDefaultNetwork() {
890    chromeos::NetworkStateHandler* handler =
891        chromeos::NetworkHandler::Get()->network_state_handler();
892    return handler->DefaultNetwork();
893  }
894
895  void SetProxyPref(const std::string& name, const base::Value& value) {
896    std::string type;
897    switch (value.GetType()) {
898      case base::Value::TYPE_BOOLEAN:
899        type = "Boolean";
900        break;
901      case base::Value::TYPE_INTEGER:
902        type = "Integer";
903        break;
904      case base::Value::TYPE_STRING:
905        type = "String";
906        break;
907      default:
908        ASSERT_TRUE(false);
909    }
910
911    std::string observed_json;
912    SetPref(name, type, &value, true, &observed_json);
913  }
914
915  void VerifyCurrentProxyServer(const std::string& expected_server,
916                                onc::ONCSource expected_source) {
917    const chromeos::NetworkState* network = GetDefaultNetwork();
918    ASSERT_TRUE(network);
919    onc::ONCSource actual_source;
920    scoped_ptr<ProxyConfigDictionary> proxy_dict =
921        chromeos::proxy_config::GetProxyConfigForNetwork(
922            pref_service_,
923            g_browser_process->local_state(),
924            *network,
925            &actual_source);
926    ASSERT_TRUE(proxy_dict);
927    std::string actual_proxy_server;
928    EXPECT_TRUE(proxy_dict->GetProxyServer(&actual_proxy_server));
929    EXPECT_EQ(expected_server, actual_proxy_server);
930    EXPECT_EQ(expected_source, actual_source);
931  }
932};
933
934// Verifies that proxy settings are correctly pushed to JavaScript during
935// initialization of the proxy settings page.
936IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSInitializeProxy) {
937  // Boolean pref.
938  pref_names_.push_back(chromeos::kProxySingle);
939  non_default_values_.push_back(new base::FundamentalValue(true));
940
941  // Integer prefs.
942  pref_names_.push_back(chromeos::kProxySingleHttpPort);
943  non_default_values_.push_back(new base::FundamentalValue(8080));
944
945  // String pref.
946  pref_names_.push_back(chromeos::kProxySingleHttp);
947  non_default_values_.push_back(new base::StringValue("127.0.0.1"));
948
949  // List pref.
950  pref_names_.push_back(chromeos::kProxyIgnoreList);
951  base::ListValue* list = new base::ListValue();
952  list->Append(new base::StringValue("*.google.com"));
953  list->Append(new base::StringValue("1.2.3.4:22"));
954  non_default_values_.push_back(list);
955
956  // Verify that no policy is presented to the UI. This must be verified on the
957  // kProxyType and the kUseSharedProxies prefs.
958  pref_names_.push_back(chromeos::kProxyType);
959  non_default_values_.push_back(new base::FundamentalValue(2));
960
961  pref_names_.push_back(prefs::kUseSharedProxies);
962  non_default_values_.push_back(new base::FundamentalValue(false));
963
964  std::string observed_json;
965  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
966  VerifyObservedPrefs(
967      observed_json, pref_names_, non_default_values_.get(), "", false, false);
968}
969
970IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ONCPolicy) {
971  SetONCPolicy(policy::key::kOpenNetworkConfiguration,
972               policy::POLICY_SCOPE_USER);
973
974  // Verify that per-network policy is presented to the UI. This must be
975  // verified on the kProxyType.
976  pref_names_.push_back(chromeos::kProxyType);
977  non_default_values_.push_back(new base::FundamentalValue(3));
978
979  std::string observed_json;
980  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
981  VerifyObservedPrefs(
982      observed_json, pref_names_, non_default_values_.get(),
983      "policy", true, false);
984
985  // Verify that 'use-shared-proxies' is not affected by per-network policy.
986  pref_names_.clear();
987  non_default_values_.clear();
988  pref_names_.push_back(prefs::kUseSharedProxies);
989  non_default_values_.push_back(new base::FundamentalValue(false));
990
991  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
992  VerifyObservedPrefs(
993      observed_json, pref_names_, non_default_values_.get(), "", false, false);
994}
995
996IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, DeviceONCPolicy) {
997  SetONCPolicy(policy::key::kDeviceOpenNetworkConfiguration,
998               policy::POLICY_SCOPE_MACHINE);
999
1000  // Verify that the policy is presented to the UI. This verification must be
1001  // done on the kProxyType pref.
1002  pref_names_.push_back(chromeos::kProxyType);
1003  non_default_values_.push_back(new base::FundamentalValue(3));
1004
1005  std::string observed_json;
1006  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
1007  VerifyObservedPrefs(
1008      observed_json, pref_names_, non_default_values_.get(),
1009      "policy", true, false);
1010
1011  // Verify that 'use-shared-proxies' is not affected by per-network policy.
1012  pref_names_.clear();
1013  non_default_values_.clear();
1014  pref_names_.push_back(prefs::kUseSharedProxies);
1015  non_default_values_.push_back(new base::FundamentalValue(false));
1016
1017  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
1018  VerifyObservedPrefs(
1019      observed_json, pref_names_, non_default_values_.get(), "", false, false);
1020}
1021
1022IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, UserProxyPolicy) {
1023  policy_names_.push_back(policy::key::kProxyMode);
1024  default_values_.push_back(
1025      new base::StringValue(ProxyPrefs::kAutoDetectProxyModeName));
1026  SetUserPolicies(
1027      policy_names_, default_values_.get(), policy::POLICY_LEVEL_MANDATORY);
1028  content::RunAllPendingInMessageLoop();
1029
1030  // Verify that the policy is presented to the UI. This verification must be
1031  // done on the kProxyType pref.
1032  pref_names_.push_back(chromeos::kProxyType);
1033  non_default_values_.push_back(new base::FundamentalValue(3));
1034
1035  // Verify that 'use-shared-proxies' is controlled by the policy.
1036  pref_names_.push_back(prefs::kUseSharedProxies);
1037  non_default_values_.push_back(new base::FundamentalValue(false));
1038
1039  std::string observed_json;
1040  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
1041  VerifyObservedPrefs(
1042      observed_json, pref_names_, non_default_values_.get(),
1043      "policy", true, false);
1044}
1045
1046// Verifies that modifications to the proxy settings are correctly pushed from
1047// JavaScript to the ProxyConfig property stored in the network configuration.
1048IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSSetProxy) {
1049  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
1050
1051  SetProxyPref(chromeos::kProxySingleHttpPort, base::FundamentalValue(123));
1052  SetProxyPref(chromeos::kProxySingleHttp, base::StringValue("www.adomain.xy"));
1053
1054  VerifyCurrentProxyServer("www.adomain.xy:123",
1055                           onc::ONC_SOURCE_NONE);
1056}
1057
1058// Verify that default proxy ports are used and that ports can be updated
1059// without affecting the previously set hosts.
1060IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSProxyDefaultPorts) {
1061  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
1062
1063  // Set to manual, per scheme proxy.
1064  SetProxyPref(chromeos::kProxySingle, base::FundamentalValue(false));
1065
1066  // Set hosts but no ports.
1067  SetProxyPref(chromeos::kProxyHttpUrl, base::StringValue("a.com"));
1068  SetProxyPref(chromeos::kProxyHttpsUrl, base::StringValue("4.3.2.1"));
1069  SetProxyPref(chromeos::kProxyFtpUrl, base::StringValue("c.com"));
1070  SetProxyPref(chromeos::kProxySocks, base::StringValue("d.com"));
1071
1072  // Verify default ports.
1073  VerifyCurrentProxyServer(
1074      "http=a.com:80;https=4.3.2.1:80;ftp=c.com:80;socks=socks4://d.com:1080",
1075      onc::ONC_SOURCE_NONE);
1076
1077  // Set and verify the ports.
1078  SetProxyPref(chromeos::kProxyHttpPort, base::FundamentalValue(1));
1079  SetProxyPref(chromeos::kProxyHttpsPort, base::FundamentalValue(2));
1080  SetProxyPref(chromeos::kProxyFtpPort, base::FundamentalValue(3));
1081  SetProxyPref(chromeos::kProxySocksPort, base::FundamentalValue(4));
1082
1083  VerifyCurrentProxyServer(
1084      "http=a.com:1;https=4.3.2.1:2;ftp=c.com:3;socks=socks4://d.com:4",
1085      onc::ONC_SOURCE_NONE);
1086}
1087
1088#endif
1089