master_preferences.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/installer/util/master_preferences.h"
6
7#include "base/environment.h"
8#include "base/file_util.h"
9#include "base/json/json_string_value_serializer.h"
10#include "base/lazy_instance.h"
11#include "base/logging.h"
12#include "base/path_service.h"
13#include "base/strings/string_util.h"
14#include "chrome/common/env_vars.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/installer/util/master_preferences_constants.h"
17#include "chrome/installer/util/util_constants.h"
18
19namespace {
20
21const char kFirstRunTabs[] = "first_run_tabs";
22
23base::LazyInstance<installer::MasterPreferences> g_master_preferences =
24    LAZY_INSTANCE_INITIALIZER;
25
26bool GetURLFromValue(const Value* in_value, std::string* out_value) {
27  return in_value && out_value && in_value->GetAsString(out_value);
28}
29
30std::vector<std::string> GetNamedList(const char* name,
31                                      const DictionaryValue* prefs) {
32  std::vector<std::string> list;
33  if (!prefs)
34    return list;
35
36  const ListValue* value_list = NULL;
37  if (!prefs->GetList(name, &value_list))
38    return list;
39
40  list.reserve(value_list->GetSize());
41  for (size_t i = 0; i < value_list->GetSize(); ++i) {
42    const Value* entry;
43    std::string url_entry;
44    if (!value_list->Get(i, &entry) || !GetURLFromValue(entry, &url_entry)) {
45      NOTREACHED();
46      break;
47    }
48    list.push_back(url_entry);
49  }
50  return list;
51}
52
53DictionaryValue* ParseDistributionPreferences(const std::string& json_data) {
54  JSONStringValueSerializer json(json_data);
55  std::string error;
56  scoped_ptr<Value> root(json.Deserialize(NULL, &error));
57  if (!root.get()) {
58    LOG(WARNING) << "Failed to parse master prefs file: " << error;
59    return NULL;
60  }
61  if (!root->IsType(Value::TYPE_DICTIONARY)) {
62    LOG(WARNING) << "Failed to parse master prefs file: "
63                 << "Root item must be a dictionary.";
64    return NULL;
65  }
66  return static_cast<DictionaryValue*>(root.release());
67}
68
69}  // namespace
70
71namespace installer {
72
73MasterPreferences::MasterPreferences() : distribution_(NULL),
74                                         preferences_read_from_file_(false),
75                                         chrome_(true),
76                                         chrome_app_launcher_(false),
77                                         chrome_frame_(false),
78                                         multi_install_(false) {
79  InitializeFromCommandLine(*CommandLine::ForCurrentProcess());
80}
81
82MasterPreferences::MasterPreferences(const CommandLine& cmd_line)
83    : distribution_(NULL),
84      preferences_read_from_file_(false),
85      chrome_(true),
86      chrome_app_launcher_(false),
87      chrome_frame_(false),
88      multi_install_(false) {
89  InitializeFromCommandLine(cmd_line);
90}
91
92MasterPreferences::MasterPreferences(const base::FilePath& prefs_path)
93    : distribution_(NULL),
94      preferences_read_from_file_(false),
95      chrome_(true),
96      chrome_app_launcher_(false),
97      chrome_frame_(false),
98      multi_install_(false) {
99  std::string json_data;
100  // Failure to read the file is ignored as |json_data| will be the empty string
101  // and the remainder of this MasterPreferences object should still be
102  // initialized as best as possible.
103  if (file_util::PathExists(prefs_path) &&
104      !file_util::ReadFileToString(prefs_path, &json_data)) {
105    LOG(ERROR) << "Failed to read preferences from " << prefs_path.value();
106  }
107  if (InitializeFromString(json_data))
108    preferences_read_from_file_ = true;
109}
110
111MasterPreferences::MasterPreferences(const std::string& prefs)
112    : distribution_(NULL),
113      preferences_read_from_file_(false),
114      chrome_(true),
115      chrome_app_launcher_(false),
116      chrome_frame_(false),
117      multi_install_(false) {
118  InitializeFromString(prefs);
119}
120
121MasterPreferences::~MasterPreferences() {
122}
123
124void MasterPreferences::InitializeFromCommandLine(const CommandLine& cmd_line) {
125#if defined(OS_WIN)
126  if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
127    base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
128        installer::switches::kInstallerData));
129    this->MasterPreferences::MasterPreferences(prefs_path);
130  } else {
131    master_dictionary_.reset(new DictionaryValue());
132  }
133
134  DCHECK(master_dictionary_.get());
135
136  // A simple map from command line switches to equivalent switches in the
137  // distribution dictionary.  Currently all switches added will be set to
138  // 'true'.
139  static const struct CmdLineSwitchToDistributionSwitch {
140    const char* cmd_line_switch;
141    const char* distribution_switch;
142  } translate_switches[] = {
143    { installer::switches::kAutoLaunchChrome,
144      installer::master_preferences::kAutoLaunchChrome },
145    { installer::switches::kChromeAppHostDeprecated,
146      installer::master_preferences::kChromeAppHostDeprecated },
147    { installer::switches::kChromeAppLauncher,
148      installer::master_preferences::kChromeAppLauncher },
149    { installer::switches::kChrome,
150      installer::master_preferences::kChrome },
151    { installer::switches::kChromeFrame,
152      installer::master_preferences::kChromeFrame },
153    { installer::switches::kChromeFrameReadyMode,
154      installer::master_preferences::kChromeFrameReadyMode },
155    { installer::switches::kDisableLogging,
156      installer::master_preferences::kDisableLogging },
157    { installer::switches::kMsi,
158      installer::master_preferences::kMsi },
159    { installer::switches::kMultiInstall,
160      installer::master_preferences::kMultiInstall },
161    { installer::switches::kDoNotRegisterForUpdateLaunch,
162      installer::master_preferences::kDoNotRegisterForUpdateLaunch },
163    { installer::switches::kDoNotLaunchChrome,
164      installer::master_preferences::kDoNotLaunchChrome },
165    { installer::switches::kMakeChromeDefault,
166      installer::master_preferences::kMakeChromeDefault },
167    { installer::switches::kSystemLevel,
168      installer::master_preferences::kSystemLevel },
169    { installer::switches::kVerboseLogging,
170      installer::master_preferences::kVerboseLogging },
171  };
172
173  std::string name(installer::master_preferences::kDistroDict);
174  for (int i = 0; i < arraysize(translate_switches); ++i) {
175    if (cmd_line.HasSwitch(translate_switches[i].cmd_line_switch)) {
176      name.assign(installer::master_preferences::kDistroDict);
177      name.append(".").append(translate_switches[i].distribution_switch);
178      master_dictionary_->SetBoolean(name, true);
179    }
180  }
181
182  // See if the log file path was specified on the command line.
183  std::wstring str_value(cmd_line.GetSwitchValueNative(
184      installer::switches::kLogFile));
185  if (!str_value.empty()) {
186    name.assign(installer::master_preferences::kDistroDict);
187    name.append(".").append(installer::master_preferences::kLogFile);
188    master_dictionary_->SetString(name, str_value);
189  }
190
191  // Handle the special case of --system-level being implied by the presence of
192  // the kGoogleUpdateIsMachineEnvVar environment variable.
193  scoped_ptr<base::Environment> env(base::Environment::Create());
194  if (env != NULL) {
195    std::string is_machine_var;
196    env->GetVar(env_vars::kGoogleUpdateIsMachineEnvVar, &is_machine_var);
197    if (!is_machine_var.empty() && is_machine_var[0] == '1') {
198      VLOG(1) << "Taking system-level from environment.";
199      name.assign(installer::master_preferences::kDistroDict);
200      name.append(".").append(installer::master_preferences::kSystemLevel);
201      master_dictionary_->SetBoolean(name, true);
202    }
203  }
204
205  // Cache a pointer to the distribution dictionary. Ignore errors if any.
206  master_dictionary_->GetDictionary(installer::master_preferences::kDistroDict,
207                                    &distribution_);
208
209  InitializeProductFlags();
210#endif
211}
212
213bool MasterPreferences::InitializeFromString(const std::string& json_data) {
214  if (!json_data.empty())
215    master_dictionary_.reset(ParseDistributionPreferences(json_data));
216
217  bool data_is_valid = true;
218  if (!master_dictionary_.get()) {
219    master_dictionary_.reset(new DictionaryValue());
220    data_is_valid = false;
221  } else {
222    // Cache a pointer to the distribution dictionary.
223    master_dictionary_->GetDictionary(
224        installer::master_preferences::kDistroDict, &distribution_);
225  }
226
227  InitializeProductFlags();
228  EnforceLegacyPreferences();
229  return data_is_valid;
230}
231
232void MasterPreferences::InitializeProductFlags() {
233  // Make sure we start out with the correct defaults.
234  multi_install_ = false;
235  chrome_frame_ = false;
236  chrome_app_launcher_ = false;
237  chrome_ = true;
238
239  GetBool(installer::master_preferences::kMultiInstall, &multi_install_);
240  GetBool(installer::master_preferences::kChromeFrame, &chrome_frame_);
241
242  GetBool(installer::master_preferences::kChromeAppLauncher,
243          &chrome_app_launcher_);
244
245  // The deprecated switch --app-host behaves like --app-launcher.
246  bool chrome_app_host = false;
247  GetBool(installer::master_preferences::kChromeAppHostDeprecated,
248          &chrome_app_host);
249  chrome_app_launcher_ = chrome_app_launcher_ || chrome_app_host;
250
251  // When multi-install is specified, the checks are pretty simple (in theory):
252  // In order to be installed/uninstalled, each product must have its switch
253  // present on the command line.
254  // Before multi-install was introduced however, we only supported installing
255  // two products, Chrome and Chrome Frame.  For the time being we need to
256  // continue to support this mode where multi-install is not set.
257  // So, when multi-install is not set, we continue to support mutually
258  // exclusive installation of Chrome and Chrome Frame.
259  if (multi_install_) {
260    if (!GetBool(installer::master_preferences::kChrome, &chrome_))
261      chrome_ = false;
262  } else {
263    // If chrome-frame is on the command line however, we only install CF.
264    chrome_ = !chrome_frame_;
265  }
266}
267
268void MasterPreferences::EnforceLegacyPreferences() {
269  // If create_all_shortcuts was explicitly set to false, set
270  // do_not_create_(desktop|quick_launch)_shortcut to true.
271  bool create_all_shortcuts = true;
272  GetBool(installer::master_preferences::kCreateAllShortcuts,
273          &create_all_shortcuts);
274  if (!create_all_shortcuts) {
275    distribution_->SetBoolean(
276        installer::master_preferences::kDoNotCreateDesktopShortcut, true);
277    distribution_->SetBoolean(
278        installer::master_preferences::kDoNotCreateQuickLaunchShortcut, true);
279  }
280}
281
282bool MasterPreferences::GetBool(const std::string& name, bool* value) const {
283  bool ret = false;
284  if (distribution_)
285    ret = distribution_->GetBoolean(name, value);
286  return ret;
287}
288
289bool MasterPreferences::GetInt(const std::string& name, int* value) const {
290  bool ret = false;
291  if (distribution_)
292    ret = distribution_->GetInteger(name, value);
293  return ret;
294}
295
296bool MasterPreferences::GetString(const std::string& name,
297                                  std::string* value) const {
298  bool ret = false;
299  if (distribution_)
300    ret = (distribution_->GetString(name, value) && !value->empty());
301  return ret;
302}
303
304std::vector<std::string> MasterPreferences::GetFirstRunTabs() const {
305  return GetNamedList(kFirstRunTabs, master_dictionary_.get());
306}
307
308bool MasterPreferences::GetExtensionsBlock(DictionaryValue** extensions) const {
309  return master_dictionary_->GetDictionary(
310      master_preferences::kExtensionsBlock, extensions);
311}
312
313std::string MasterPreferences::GetVariationsSeed() const {
314  std::string variations_seed;
315  master_dictionary_->GetString(prefs::kVariationsSeed, &variations_seed);
316  return variations_seed;
317}
318
319// static
320const MasterPreferences& MasterPreferences::ForCurrentProcess() {
321  return g_master_preferences.Get();
322}
323
324}  // namespace installer
325