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/stl_util.h"
16#include "base/values.h"
17#include "chrome/browser/chrome_notification_types.h"
18#include "chrome/browser/policy/browser_policy_connector.h"
19#include "chrome/browser/policy/external_data_fetcher.h"
20#include "chrome/browser/policy/policy_map.h"
21#include "chrome/browser/policy/policy_types.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/common/url_constants.h"
27#include "chrome/test/base/ui_test_utils.h"
28#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_source.h"
30#include "content/public/browser/render_view_host.h"
31#include "content/public/browser/web_contents.h"
32#include "content/public/test/browser_test_utils.h"
33#include "policy/policy_constants.h"
34#include "testing/gtest/include/gtest/gtest.h"
35#include "url/gurl.h"
36
37#if defined(OS_CHROMEOS)
38#include "base/strings/stringprintf.h"
39#include "chrome/browser/browser_process.h"
40#include "chrome/browser/chromeos/net/proxy_config_handler.h"
41#include "chrome/browser/chromeos/proxy_cros_settings_parser.h"
42#include "chrome/browser/chromeos/settings/cros_settings.h"
43#include "chrome/browser/chromeos/settings/cros_settings_names.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 "content/public/test/test_utils.h"
51#include "third_party/cros_system_api/dbus/service_constants.h"
52#endif
53
54using testing::AllOf;
55using testing::AnyNumber;
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 Value& first, const 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 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  EXPECT_CALL(policy_provider_, RegisterPolicyDomain(_))
194      .Times(AnyNumber());
195  policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
196      &policy_provider_);
197};
198
199void PreferencesBrowserTest::TearDownInProcessBrowserTestFixture() {
200  STLDeleteElements(&default_values_);
201  STLDeleteElements(&non_default_values_);
202}
203
204void PreferencesBrowserTest::SetUserPolicies(
205    const std::vector<std::string>& names,
206    const std::vector<base::Value*>& values,
207    policy::PolicyLevel level) {
208  policy::PolicyMap map;
209  for (size_t i = 0; i < names.size(); ++i) {
210    map.Set(names[i], level, policy::POLICY_SCOPE_USER,
211            values[i]->DeepCopy(), NULL);
212  }
213  policy_provider_.UpdateChromePolicy(map);
214}
215
216void PreferencesBrowserTest::ClearUserPolicies() {
217  policy::PolicyMap empty_policy_map;
218  policy_provider_.UpdateChromePolicy(empty_policy_map);
219}
220
221void PreferencesBrowserTest::SetUserValues(
222    const std::vector<std::string>& names,
223    const std::vector<base::Value*>& values) {
224  for (size_t i = 0; i < names.size(); ++i)
225    pref_service_->Set(names[i].c_str(), *values[i]);
226}
227
228void PreferencesBrowserTest::VerifyKeyValue(const base::DictionaryValue& dict,
229                                            const std::string& key,
230                                            const base::Value& expected) {
231  const base::Value* actual = NULL;
232  EXPECT_TRUE(dict.Get(key, &actual)) << "Was checking key: " << key;
233  EXPECT_EQ(expected, *actual) << "Was checking key: " << key;
234}
235
236void PreferencesBrowserTest::VerifyPref(const base::DictionaryValue* prefs,
237                                        const std::string& name,
238                                        const base::Value* value,
239                                        const std::string& controlledBy,
240                                        bool disabled,
241                                        bool uncommitted) {
242  const base::Value* pref;
243  const base::DictionaryValue* dict;
244  ASSERT_TRUE(prefs->GetWithoutPathExpansion(name, &pref));
245  ASSERT_TRUE(pref->GetAsDictionary(&dict));
246  VerifyKeyValue(*dict, "value", *value);
247  if (!controlledBy.empty()) {
248    VerifyKeyValue(*dict, "controlledBy", base::StringValue(controlledBy));
249  } else {
250    EXPECT_FALSE(dict->HasKey("controlledBy"));
251  }
252  if (disabled)
253    VerifyKeyValue(*dict, "disabled", base::FundamentalValue(true));
254  else if (dict->HasKey("disabled"))
255    VerifyKeyValue(*dict, "disabled", base::FundamentalValue(false));
256  if (uncommitted)
257    VerifyKeyValue(*dict, "uncommitted", base::FundamentalValue(true));
258  else if (dict->HasKey("uncommitted"))
259    VerifyKeyValue(*dict, "uncommitted", base::FundamentalValue(false));
260}
261
262void PreferencesBrowserTest::VerifyObservedPref(const std::string& json,
263                                                const std::string& name,
264                                                const base::Value* value,
265                                                const std::string& controlledBy,
266                                                bool disabled,
267                                                bool uncommitted) {
268  scoped_ptr<base::Value> observed_value_ptr(base::JSONReader::Read(json));
269  const base::DictionaryValue* observed_dict;
270  ASSERT_TRUE(observed_value_ptr.get());
271  ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
272  VerifyPref(observed_dict, name, value, controlledBy, disabled, uncommitted);
273}
274
275void PreferencesBrowserTest::VerifyObservedPrefs(
276    const std::string& json,
277    const std::vector<std::string>& names,
278    const std::vector<base::Value*>& values,
279    const std::string& controlledBy,
280    bool disabled,
281    bool uncommitted) {
282  scoped_ptr<base::Value> observed_value_ptr(base::JSONReader::Read(json));
283  const base::DictionaryValue* observed_dict;
284  ASSERT_TRUE(observed_value_ptr.get());
285  ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
286  for (size_t i = 0; i < names.size(); ++i)
287    VerifyPref(observed_dict, names[i], values[i], controlledBy, disabled,
288               uncommitted);
289}
290
291void PreferencesBrowserTest::ExpectNoCommit(const std::string& name) {
292  pref_change_registrar_.Add(
293      name.c_str(),
294      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
295                 base::Unretained(this)));
296  EXPECT_CALL(*this, OnCommit(Property(&PrefService::Preference::name, name)))
297      .Times(0);
298}
299
300void PreferencesBrowserTest::ExpectSetCommit(const std::string& name,
301                                             const base::Value* value) {
302  pref_change_registrar_.Add(
303      name.c_str(),
304      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
305                 base::Unretained(this)));
306  EXPECT_CALL(*this, OnCommit(AllOf(
307      Property(&PrefService::Preference::name, name),
308      Property(&PrefService::Preference::IsUserControlled, true),
309      Property(&PrefService::Preference::GetValue, EqualsValue(value)))));
310}
311
312void PreferencesBrowserTest::ExpectClearCommit(const std::string& name) {
313  pref_change_registrar_.Add(
314      name.c_str(),
315      base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
316                 base::Unretained(this)));
317  EXPECT_CALL(*this, OnCommit(AllOf(
318      Property(&PrefService::Preference::name, name),
319      Property(&PrefService::Preference::IsUserControlled, false))));
320}
321
322void PreferencesBrowserTest::VerifyAndClearExpectations() {
323  Mock::VerifyAndClearExpectations(this);
324  pref_change_registrar_.RemoveAll();
325}
326
327void PreferencesBrowserTest::SetupJavaScriptTestEnvironment(
328    const std::vector<std::string>& pref_names,
329    std::string* observed_json) const {
330  std::stringstream javascript;
331  javascript << "var testEnv = new TestEnv();";
332  for (std::vector<std::string>::const_iterator name = pref_names.begin();
333       name != pref_names.end(); ++name)
334    javascript << "testEnv.addPref('" << name->c_str() << "');";
335  javascript << "testEnv.setupAndReply();";
336  std::string temp_observed_json;
337  if (!observed_json)
338    observed_json = &temp_observed_json;
339  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
340      render_view_host_, javascript.str(), observed_json));
341}
342
343void PreferencesBrowserTest::SetPref(const std::string& name,
344                                     const std::string& type,
345                                     const base::Value* value,
346                                     bool commit,
347                                     std::string* observed_json) {
348  scoped_ptr<base::Value> commit_ptr(new base::FundamentalValue(commit));
349  std::stringstream javascript;
350  javascript << "testEnv.runAndReply(function() {"
351             << "  Preferences.set" << type << "Pref("
352             << "      '" << name << "',"
353             << "      " << *value << ","
354             << "      " << *commit_ptr << ");"
355             << "});";
356  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
357      render_view_host_, javascript.str(), observed_json));
358}
359
360void PreferencesBrowserTest::VerifySetPref(const std::string& name,
361                                           const std::string& type,
362                                           const base::Value* value,
363                                           bool commit) {
364  if (commit)
365    ExpectSetCommit(name, value);
366  else
367    ExpectNoCommit(name);
368  std::string observed_json;
369  SetPref(name, type, value, commit, &observed_json);
370  VerifyObservedPref(observed_json, name, value, std::string(), false, !commit);
371  VerifyAndClearExpectations();
372}
373
374void PreferencesBrowserTest::VerifyClearPref(const std::string& name,
375                                             const base::Value* value,
376                                             bool commit) {
377  if (commit)
378    ExpectClearCommit(name);
379  else
380    ExpectNoCommit(name);
381  scoped_ptr<base::Value> commit_ptr(new base::FundamentalValue(commit));
382  std::string commit_json;
383  base::JSONWriter::Write(commit_ptr.get(), &commit_json);
384  std::stringstream javascript;
385  javascript << "testEnv.runAndReply(function() {"
386             << "    Preferences.clearPref("
387             << "      '" << name.c_str() << "',"
388             << "      " << commit_json.c_str() << ");});";
389  std::string observed_json;
390  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
391      render_view_host_, javascript.str(), &observed_json));
392  VerifyObservedPref(observed_json, name, value, "recommended", false, !commit);
393  VerifyAndClearExpectations();
394}
395
396void PreferencesBrowserTest::VerifyCommit(const std::string& name,
397                                          const base::Value* value,
398                                          const std::string& controlledBy) {
399  std::stringstream javascript;
400  javascript << "testEnv.runAndReply(function() {"
401             << "    Preferences.getInstance().commitPref("
402             << "        '" << name.c_str() << "');});";
403  std::string observed_json;
404  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
405      render_view_host_, javascript.str(), &observed_json));
406  VerifyObservedPref(observed_json, name, value, controlledBy, false, false);
407}
408
409void PreferencesBrowserTest::VerifySetCommit(const std::string& name,
410                                             const base::Value* value) {
411  ExpectSetCommit(name, value);
412  VerifyCommit(name, value, std::string());
413  VerifyAndClearExpectations();
414}
415
416void PreferencesBrowserTest::VerifyClearCommit(const std::string& name,
417                                               const base::Value* value) {
418  ExpectClearCommit(name);
419  VerifyCommit(name, value, "recommended");
420  VerifyAndClearExpectations();
421}
422
423void PreferencesBrowserTest::VerifyRollback(const std::string& name,
424                                            const base::Value* value,
425                                            const std::string& controlledBy) {
426  ExpectNoCommit(name);
427  std::stringstream javascript;
428  javascript << "testEnv.runAndReply(function() {"
429             << "    Preferences.getInstance().rollbackPref("
430             << "        '" << name.c_str() << "');});";
431  std::string observed_json;
432  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
433      render_view_host_, javascript.str(), &observed_json));
434  VerifyObservedPref(observed_json, name, value, controlledBy, false, true);
435  VerifyAndClearExpectations();
436}
437
438void PreferencesBrowserTest::StartObserving() {
439  ASSERT_TRUE(content::ExecuteScript(
440      render_view_host_, "testEnv.startObserving();"));
441}
442
443void PreferencesBrowserTest::FinishObserving(std::string* observed_json) {
444  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
445      render_view_host_,
446      "testEnv.finishObservingAndReply();",
447      observed_json));
448}
449
450void PreferencesBrowserTest::UseDefaultTestPrefs(bool includeListPref) {
451  // Boolean pref.
452  types_.push_back("Boolean");
453  pref_names_.push_back(prefs::kAlternateErrorPagesEnabled);
454  policy_names_.push_back(policy::key::kAlternateErrorPagesEnabled);
455  non_default_values_.push_back(new base::FundamentalValue(false));
456
457  // Integer pref.
458  types_.push_back("Integer");
459  pref_names_.push_back(prefs::kRestoreOnStartup);
460  policy_names_.push_back(policy::key::kRestoreOnStartup);
461  non_default_values_.push_back(new base::FundamentalValue(4));
462
463  // List pref.
464  if (includeListPref) {
465    types_.push_back("List");
466    pref_names_.push_back(prefs::kURLsToRestoreOnStartup);
467    policy_names_.push_back(policy::key::kRestoreOnStartupURLs);
468    base::ListValue* list = new base::ListValue;
469    list->Append(new base::StringValue("http://www.google.com"));
470    list->Append(new base::StringValue("http://example.com"));
471    non_default_values_.push_back(list);
472  }
473
474  // Retrieve default values.
475  for (std::vector<std::string>::const_iterator name = pref_names_.begin();
476        name != pref_names_.end(); ++name) {
477    default_values_.push_back(
478        pref_service_->GetDefaultPrefValue(name->c_str())->DeepCopy());
479  }
480}
481
482// Verifies that initializing the JavaScript Preferences class fires the correct
483// notifications in JavaScript.
484IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, FetchPrefs) {
485  UseDefaultTestPrefs(true);
486  std::string observed_json;
487
488  // Verify notifications when default values are in effect.
489  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
490  VerifyObservedPrefs(
491      observed_json, pref_names_, default_values_, std::string(), false, false);
492
493  // Verify notifications when recommended values are in effect.
494  SetUserPolicies(policy_names_, non_default_values_,
495                  policy::POLICY_LEVEL_RECOMMENDED);
496  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
497  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
498                      "recommended", false, false);
499
500  // Verify notifications when mandatory values are in effect.
501  SetUserPolicies(policy_names_, non_default_values_,
502                  policy::POLICY_LEVEL_MANDATORY);
503  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
504  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
505                      "policy", true, false);
506
507  // Verify notifications when user-modified values are in effect.
508  ClearUserPolicies();
509  SetUserValues(pref_names_, non_default_values_);
510  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
511  VerifyObservedPrefs(observed_json,
512                      pref_names_,
513                      non_default_values_,
514                      std::string(),
515                      false,
516                      false);
517}
518
519// Verifies that setting a user-modified pref value through the JavaScript
520// Preferences class fires the correct notification in JavaScript and causes the
521// change to be committed to the C++ backend.
522IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, SetPrefs) {
523  UseDefaultTestPrefs(false);
524
525  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
526  for (size_t i = 0; i < pref_names_.size(); ++i)
527    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], true);
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_,
537                  policy::POLICY_LEVEL_RECOMMENDED);
538  SetUserValues(pref_names_, non_default_values_);
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// Verifies that when the user-modified value of a dialog pref is set and the
545// change then committed through the JavaScript Preferences class, the correct
546// notifications fire and a commit to the C++ backend occurs in the latter step
547// only.
548IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetCommit) {
549  UseDefaultTestPrefs(false);
550
551  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
552  for (size_t i = 0; i < pref_names_.size(); ++i) {
553    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
554    VerifySetCommit(pref_names_[i], non_default_values_[i]);
555  }
556}
557
558// Verifies that when the user-modified value of a dialog pref is set and the
559// change then rolled back through the JavaScript Preferences class, the correct
560// notifications fire and no commit to the C++ backend occurs.
561IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetRollback) {
562  UseDefaultTestPrefs(false);
563
564  // Verify behavior when default values are in effect.
565  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
566  for (size_t i = 0; i < pref_names_.size(); ++i) {
567    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
568    VerifyRollback(pref_names_[i], default_values_[i], std::string());
569  }
570
571  // Verify behavior when recommended values are in effect.
572  SetUserPolicies(policy_names_, default_values_,
573                  policy::POLICY_LEVEL_RECOMMENDED);
574  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
575  for (size_t i = 0; i < pref_names_.size(); ++i) {
576    VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
577    VerifyRollback(pref_names_[i], default_values_[i], "recommended");
578  }
579}
580
581// Verifies that when the user-modified value of a dialog pref is cleared and
582// the change then committed through the JavaScript Preferences class, the
583// correct notifications fire and a commit to the C++ backend occurs in the
584// latter step only.
585IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearCommit) {
586  UseDefaultTestPrefs(false);
587
588  SetUserPolicies(policy_names_, default_values_,
589                  policy::POLICY_LEVEL_RECOMMENDED);
590  SetUserValues(pref_names_, non_default_values_);
591  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
592  for (size_t i = 0; i < pref_names_.size(); ++i) {
593    VerifyClearPref(pref_names_[i], default_values_[i], false);
594    VerifyClearCommit(pref_names_[i], default_values_[i]);
595  }
596}
597
598// Verifies that when the user-modified value of a dialog pref is cleared and
599// the change then rolled back through the JavaScript Preferences class, the
600// correct notifications fire and no commit to the C++ backend occurs.
601IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearRollback) {
602  UseDefaultTestPrefs(false);
603
604  SetUserPolicies(policy_names_, default_values_,
605                  policy::POLICY_LEVEL_RECOMMENDED);
606  SetUserValues(pref_names_, non_default_values_);
607  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
608  for (size_t i = 0; i < pref_names_.size(); ++i) {
609    VerifyClearPref(pref_names_[i], default_values_[i], false);
610    VerifyRollback(pref_names_[i], non_default_values_[i], std::string());
611  }
612}
613
614// Verifies that when preference values change in the C++ backend, the correct
615// notifications fire in JavaScript.
616IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, NotificationsOnBackendChanges) {
617  UseDefaultTestPrefs(false);
618  std::string observed_json;
619
620  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
621
622  // Verify notifications when recommended values come into effect.
623  StartObserving();
624  SetUserPolicies(policy_names_, non_default_values_,
625                  policy::POLICY_LEVEL_RECOMMENDED);
626  FinishObserving(&observed_json);
627  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
628                      "recommended", false, false);
629
630  // Verify notifications when mandatory values come into effect.
631  StartObserving();
632  SetUserPolicies(policy_names_, non_default_values_,
633                  policy::POLICY_LEVEL_MANDATORY);
634  FinishObserving(&observed_json);
635  VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
636                      "policy", true, false);
637
638  // Verify notifications when default values come into effect.
639  StartObserving();
640  ClearUserPolicies();
641  FinishObserving(&observed_json);
642  VerifyObservedPrefs(
643      observed_json, pref_names_, default_values_, std::string(), false, false);
644
645  // Verify notifications when user-modified values come into effect.
646  StartObserving();
647  SetUserValues(pref_names_, non_default_values_);
648  FinishObserving(&observed_json);
649  VerifyObservedPrefs(observed_json,
650                      pref_names_,
651                      non_default_values_,
652                      std::string(),
653                      false,
654                      false);
655}
656
657#if defined(OS_CHROMEOS)
658
659// Verifies that initializing the JavaScript Preferences class fires the correct
660// notifications in JavaScript for pref values handled by the
661// CoreChromeOSOptionsHandler class.
662IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, ChromeOSDeviceFetchPrefs) {
663  std::vector<base::Value*> decorated_non_default_values;
664  std::string observed_json;
665
666  // Boolean pref.
667  pref_names_.push_back(chromeos::kAccountsPrefAllowGuest);
668  default_values_.push_back(new base::FundamentalValue(true));
669  non_default_values_.push_back(new base::FundamentalValue(false));
670  decorated_non_default_values.push_back(
671      non_default_values_.back()->DeepCopy());
672
673  // String pref.
674  pref_names_.push_back(chromeos::kReleaseChannel);
675  default_values_.push_back(new base::StringValue(""));
676  non_default_values_.push_back(new base::StringValue("stable-channel"));
677  decorated_non_default_values.push_back(
678      non_default_values_.back()->DeepCopy());
679
680  // List pref.
681  pref_names_.push_back(chromeos::kAccountsPrefUsers);
682  default_values_.push_back(new base::ListValue);
683  base::ListValue* list = new base::ListValue;
684  list->Append(new base::StringValue("me@google.com"));
685  list->Append(new base::StringValue("you@google.com"));
686  non_default_values_.push_back(list);
687  list = new base::ListValue;
688  base::DictionaryValue* dict = new base::DictionaryValue;
689  dict->SetString("username", "me@google.com");
690  dict->SetString("name", "me@google.com");
691  dict->SetString("email", "");
692  dict->SetBoolean("owner", false);
693  list->Append(dict);
694  dict = new base::DictionaryValue;
695  dict->SetString("username", "you@google.com");
696  dict->SetString("name", "you@google.com");
697  dict->SetString("email", "");
698  dict->SetBoolean("owner", false);
699  list->Append(dict);
700  decorated_non_default_values.push_back(list);
701
702  // Verify notifications when default values are in effect.
703  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
704  VerifyObservedPrefs(observed_json, pref_names_, default_values_,
705                      "", true, false);
706
707  // Verify notifications when mandatory values are in effect.
708  chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
709  for (size_t i = 0; i < pref_names_.size(); ++i)
710    cros_settings->Set(pref_names_[i], *non_default_values_[i]);
711  // FIXME(bartfab): Find a way to simulate enterprise enrollment in browser
712  // tests. Only if Chrome thinks that it is enrolled will the device prefs be
713  // decorated with "controlledBy: policy".
714  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
715  VerifyObservedPrefs(observed_json, pref_names_, decorated_non_default_values,
716                      "", true, false);
717
718  STLDeleteElements(&decorated_non_default_values);
719}
720
721namespace {
722
723const char* kUserProfilePath = "user_profile";
724
725}  // namespace
726
727class ProxyPreferencesBrowserTest : public PreferencesBrowserTest {
728 public:
729  virtual void SetUpOnMainThread() OVERRIDE {
730    SetupNetworkEnvironment();
731    content::RunAllPendingInMessageLoop();
732
733    scoped_ptr<base::DictionaryValue> proxy_config_dict(
734        ProxyConfigDictionary::CreateFixedServers("127.0.0.1:8080",
735                                                  "*.google.com, 1.2.3.4:22"));
736
737    ProxyConfigDictionary proxy_config(proxy_config_dict.get());
738
739    const chromeos::NetworkState* network = GetDefaultNetwork();
740    chromeos::proxy_config::SetProxyConfigForNetwork(proxy_config, *network);
741
742    std::string url = base::StringPrintf("%s?network=%s",
743                                         chrome::kChromeUIProxySettingsURL,
744                                         network->path().c_str());
745
746    ui_test_utils::NavigateToURL(browser(), GURL(url));
747    SetUpPrefs();
748  }
749
750 protected:
751  void SetupNetworkEnvironment() {
752    chromeos::ShillProfileClient::TestInterface* profile_test =
753        chromeos::DBusThreadManager::Get()->GetShillProfileClient()
754            ->GetTestInterface();
755    chromeos::ShillServiceClient::TestInterface* service_test =
756        chromeos::DBusThreadManager::Get()->GetShillServiceClient()
757            ->GetTestInterface();
758
759    profile_test->AddProfile(kUserProfilePath, "user");
760
761    service_test->ClearServices();
762    service_test->AddService("stub_ethernet",
763                             "eth0",
764                             flimflam::kTypeEthernet,
765                             flimflam::kStateOnline,
766                             true,   // add to visible
767                             true);  // add to watchlist
768    service_test->SetServiceProperty("stub_ethernet",
769                                     flimflam::kGuidProperty,
770                                     base::StringValue("stub_ethernet"));
771    service_test->SetServiceProperty("stub_ethernet",
772                                     flimflam::kProfileProperty,
773                                     base::StringValue(kUserProfilePath));
774    profile_test->AddService(kUserProfilePath, "stub_wifi2");
775  }
776
777  void SetONCPolicy(const char* policy_name, policy::PolicyScope scope) {
778    std::string onc_policy =
779        "{ \"NetworkConfigurations\": ["
780        "    { \"GUID\": \"stub_ethernet\","
781        "      \"Type\": \"Ethernet\","
782        "      \"Name\": \"My Ethernet\","
783        "      \"Ethernet\": {"
784        "        \"Authentication\": \"None\" },"
785        "      \"ProxySettings\": {"
786        "        \"PAC\": \"http://domain.com/x\","
787        "        \"Type\": \"PAC\" }"
788        "    }"
789        "  ],"
790        "  \"Type\": \"UnencryptedConfiguration\""
791        "}";
792
793    policy::PolicyMap map;
794    map.Set(policy_name,
795            policy::POLICY_LEVEL_MANDATORY,
796            scope,
797            new base::StringValue(onc_policy),
798            NULL);
799    policy_provider_.UpdateChromePolicy(map);
800
801    content::RunAllPendingInMessageLoop();
802  }
803
804  const chromeos::NetworkState* GetDefaultNetwork() {
805    return chromeos::NetworkHandler::Get()->network_state_handler()
806        ->DefaultNetwork();
807  }
808
809  void SetProxyPref(const std::string& name, const base::Value& value) {
810    std::string type;
811    switch (value.GetType()) {
812      case base::Value::TYPE_BOOLEAN:
813        type = "Boolean";
814        break;
815      case base::Value::TYPE_INTEGER:
816        type = "Integer";
817        break;
818      case base::Value::TYPE_STRING:
819        type = "String";
820        break;
821      default:
822        ASSERT_TRUE(false);
823    }
824
825    std::string observed_json;
826    SetPref(name, type, &value, true, &observed_json);
827  }
828
829  void VerifyCurrentProxyServer(const std::string& expected_server,
830                                chromeos::onc::ONCSource expected_source) {
831    chromeos::onc::ONCSource actual_source;
832    scoped_ptr<ProxyConfigDictionary> proxy_dict =
833        chromeos::proxy_config::GetProxyConfigForNetwork(
834            pref_service_,
835            g_browser_process->local_state(),
836            *GetDefaultNetwork(),
837            &actual_source);
838    std::string actual_proxy_server;
839    EXPECT_TRUE(proxy_dict->GetProxyServer(&actual_proxy_server));
840    EXPECT_EQ(expected_server, actual_proxy_server);
841    EXPECT_EQ(expected_source, actual_source);
842  }
843};
844
845// Verifies that proxy settings are correctly pushed to JavaScript during
846// initialization of the proxy settings page.
847IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSInitializeProxy) {
848  // Boolean pref.
849  pref_names_.push_back(chromeos::kProxySingle);
850  non_default_values_.push_back(new base::FundamentalValue(true));
851
852  // Integer prefs.
853  pref_names_.push_back(chromeos::kProxySingleHttpPort);
854  non_default_values_.push_back(new base::FundamentalValue(8080));
855
856  // String pref.
857  pref_names_.push_back(chromeos::kProxySingleHttp);
858  non_default_values_.push_back(new base::StringValue("127.0.0.1"));
859
860  // List pref.
861  pref_names_.push_back(chromeos::kProxyIgnoreList);
862  base::ListValue* list = new base::ListValue();
863  list->Append(new base::StringValue("*.google.com"));
864  list->Append(new base::StringValue("1.2.3.4:22"));
865  non_default_values_.push_back(list);
866
867  // Verify that no policy is presented to the UI. This must be verified on the
868  // kProxyType and the kUseSharedProxies prefs.
869  pref_names_.push_back(chromeos::kProxyType);
870  non_default_values_.push_back(new base::FundamentalValue(2));
871
872  pref_names_.push_back(prefs::kUseSharedProxies);
873  non_default_values_.push_back(new base::FundamentalValue(false));
874
875  std::string observed_json;
876  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
877  VerifyObservedPrefs(
878      observed_json, pref_names_, non_default_values_, "", false, false);
879}
880
881IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ONCPolicy) {
882  SetONCPolicy(policy::key::kOpenNetworkConfiguration,
883               policy::POLICY_SCOPE_USER);
884
885  // Verify that per-network policy is presented to the UI. This must be
886  // verified on the kProxyType.
887  pref_names_.push_back(chromeos::kProxyType);
888  non_default_values_.push_back(new base::FundamentalValue(3));
889
890  std::string observed_json;
891  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
892  VerifyObservedPrefs(
893      observed_json, pref_names_, non_default_values_, "policy", true, false);
894
895  // Verify that 'use-shared-proxies' is not affected by per-network policy.
896  pref_names_.clear();
897  STLDeleteElements(&non_default_values_);
898  pref_names_.push_back(prefs::kUseSharedProxies);
899  non_default_values_.push_back(new base::FundamentalValue(false));
900
901  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
902  VerifyObservedPrefs(
903      observed_json, pref_names_, non_default_values_, "", false, false);
904}
905
906IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, DeviceONCPolicy) {
907  SetONCPolicy(policy::key::kDeviceOpenNetworkConfiguration,
908               policy::POLICY_SCOPE_MACHINE);
909
910  // Verify that the policy is presented to the UI. This verification must be
911  // done on the kProxyType pref.
912  pref_names_.push_back(chromeos::kProxyType);
913  non_default_values_.push_back(new base::FundamentalValue(3));
914
915  std::string observed_json;
916  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
917  VerifyObservedPrefs(
918      observed_json, pref_names_, non_default_values_, "policy", true, false);
919
920  // Verify that 'use-shared-proxies' is not affected by per-network policy.
921  pref_names_.clear();
922  STLDeleteElements(&non_default_values_);
923  pref_names_.push_back(prefs::kUseSharedProxies);
924  non_default_values_.push_back(new base::FundamentalValue(false));
925
926  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
927  VerifyObservedPrefs(
928      observed_json, pref_names_, non_default_values_, "", false, false);
929}
930
931IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, UserProxyPolicy) {
932  policy_names_.push_back(policy::key::kProxyMode);
933  default_values_.push_back(
934      new base::StringValue(ProxyPrefs::kAutoDetectProxyModeName));
935  SetUserPolicies(
936      policy_names_, default_values_, policy::POLICY_LEVEL_MANDATORY);
937  content::RunAllPendingInMessageLoop();
938
939  // Verify that the policy is presented to the UI. This verification must be
940  // done on the kProxyType pref.
941  pref_names_.push_back(chromeos::kProxyType);
942  non_default_values_.push_back(new base::FundamentalValue(3));
943
944  // Verify that 'use-shared-proxies' is controlled by the policy.
945  pref_names_.push_back(prefs::kUseSharedProxies);
946  non_default_values_.push_back(new base::FundamentalValue(false));
947
948  std::string observed_json;
949  SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
950  VerifyObservedPrefs(
951      observed_json, pref_names_, non_default_values_, "policy", true, false);
952}
953
954// Verifies that modifications to the proxy settings are correctly pushed from
955// JavaScript to the ProxyConfig property stored in the network configuration.
956IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSSetProxy) {
957  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
958
959  SetProxyPref(chromeos::kProxySingleHttpPort, base::FundamentalValue(123));
960  SetProxyPref(chromeos::kProxySingleHttp, base::StringValue("www.adomain.xy"));
961
962  VerifyCurrentProxyServer("www.adomain.xy:123",
963                           chromeos::onc::ONC_SOURCE_NONE);
964}
965
966// Verify that default proxy ports are used and that ports can be updated
967// without affecting the previously set hosts.
968IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSProxyDefaultPorts) {
969  ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
970
971  // Set to manual, per scheme proxy.
972  SetProxyPref(chromeos::kProxySingle, base::FundamentalValue(false));
973
974  // Set hosts but no ports.
975  SetProxyPref(chromeos::kProxyHttpUrl, base::StringValue("a.com"));
976  SetProxyPref(chromeos::kProxyHttpsUrl, base::StringValue("4.3.2.1"));
977  SetProxyPref(chromeos::kProxyFtpUrl, base::StringValue("c.com"));
978  SetProxyPref(chromeos::kProxySocks, base::StringValue("d.com"));
979
980  // Verify default ports.
981  VerifyCurrentProxyServer(
982      "http=a.com:80;https=4.3.2.1:80;ftp=c.com:80;socks=socks4://d.com:1080",
983      chromeos::onc::ONC_SOURCE_NONE);
984
985  // Set and verify the ports.
986  SetProxyPref(chromeos::kProxyHttpPort, base::FundamentalValue(1));
987  SetProxyPref(chromeos::kProxyHttpsPort, base::FundamentalValue(2));
988  SetProxyPref(chromeos::kProxyFtpPort, base::FundamentalValue(3));
989  SetProxyPref(chromeos::kProxySocksPort, base::FundamentalValue(4));
990
991  VerifyCurrentProxyServer(
992      "http=a.com:1;https=4.3.2.1:2;ftp=c.com:3;socks=socks4://d.com:4",
993      chromeos::onc::ONC_SOURCE_NONE);
994}
995
996#endif
997