resettable_settings_snapshot.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1// Copyright (c) 2013 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/profile_resetter/resettable_settings_snapshot.h"
6
7#include "base/json/json_writer.h"
8#include "base/prefs/pref_service.h"
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/synchronization/cancellation_flag.h"
12#include "base/values.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/search_engines/template_url_service_factory.h"
16#include "chrome/common/chrome_content_client.h"
17#include "chrome/common/chrome_version_info.h"
18#include "chrome/common/pref_names.h"
19#include "components/feedback/feedback_data.h"
20#include "components/feedback/feedback_util.h"
21#include "components/search_engines/template_url_service.h"
22#include "content/public/browser/browser_thread.h"
23#include "extensions/browser/extension_registry.h"
24#include "grit/generated_resources.h"
25#include "grit/google_chrome_strings.h"
26#include "ui/base/l10n/l10n_util.h"
27
28using feedback::FeedbackData;
29
30namespace {
31
32// Feedback bucket label.
33const char kProfileResetWebUIBucket[] = "ProfileResetReport";
34
35// Dictionary keys for feedback report.
36const char kDefaultSearchEnginePath[] = "default_search_engine";
37const char kEnabledExtensions[] = "enabled_extensions";
38const char kHomepageIsNewTabPage[] = "homepage_is_ntp";
39const char kHomepagePath[] = "homepage";
40const char kShortcuts[] = "shortcuts";
41const char kShowHomeButton[] = "show_home_button";
42const char kStartupTypePath[] = "startup_type";
43const char kStartupURLPath[] = "startup_urls";
44
45template <class StringType>
46void AddPair(base::ListValue* list,
47             const base::string16& key,
48             const StringType& value) {
49  base::DictionaryValue* results = new base::DictionaryValue();
50  results->SetString("key", key);
51  results->SetString("value", value);
52  list->Append(results);
53}
54
55}  // namespace
56
57ResettableSettingsSnapshot::ResettableSettingsSnapshot(
58    Profile* profile)
59    : startup_(SessionStartupPref::GetStartupPref(profile)),
60      shortcuts_determined_(false),
61      weak_ptr_factory_(this) {
62  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
63  // URLs are always stored sorted.
64  std::sort(startup_.urls.begin(), startup_.urls.end());
65
66  PrefService* prefs = profile->GetPrefs();
67  DCHECK(prefs);
68  homepage_ = prefs->GetString(prefs::kHomePage);
69  homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage);
70  show_home_button_ = prefs->GetBoolean(prefs::kShowHomeButton);
71
72  TemplateURLService* service =
73      TemplateURLServiceFactory::GetForProfile(profile);
74  DCHECK(service);
75  TemplateURL* dse = service->GetDefaultSearchProvider();
76  if (dse)
77    dse_url_ = dse->url();
78
79  const extensions::ExtensionSet& enabled_ext =
80      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
81  enabled_extensions_.reserve(enabled_ext.size());
82
83  for (extensions::ExtensionSet::const_iterator it = enabled_ext.begin();
84       it != enabled_ext.end(); ++it)
85    enabled_extensions_.push_back(std::make_pair((*it)->id(), (*it)->name()));
86
87  // ExtensionSet is sorted but it seems to be an implementation detail.
88  std::sort(enabled_extensions_.begin(), enabled_extensions_.end());
89}
90
91ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {
92  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
93  if (cancellation_flag_)
94    cancellation_flag_->data.Set();
95}
96
97void ResettableSettingsSnapshot::Subtract(
98    const ResettableSettingsSnapshot& snapshot) {
99  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
100  ExtensionList extensions = base::STLSetDifference<ExtensionList>(
101      enabled_extensions_, snapshot.enabled_extensions_);
102  enabled_extensions_.swap(extensions);
103}
104
105int ResettableSettingsSnapshot::FindDifferentFields(
106    const ResettableSettingsSnapshot& snapshot) const {
107  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
108  int bit_mask = 0;
109
110  if (startup_.type != snapshot.startup_.type ||
111      startup_.urls != snapshot.startup_.urls)
112    bit_mask |= STARTUP_MODE;
113
114  if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ ||
115      homepage_ != snapshot.homepage_ ||
116      show_home_button_ != snapshot.show_home_button_)
117    bit_mask |= HOMEPAGE;
118
119  if (dse_url_ != snapshot.dse_url_)
120    bit_mask |= DSE_URL;
121
122  if (enabled_extensions_ != snapshot.enabled_extensions_)
123    bit_mask |= EXTENSIONS;
124
125  if (shortcuts_ != snapshot.shortcuts_)
126    bit_mask |= SHORTCUTS;
127
128  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
129                 add_new_field_here);
130
131  return bit_mask;
132}
133
134void ResettableSettingsSnapshot::RequestShortcuts(
135    const base::Closure& callback) {
136  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
137  DCHECK(!cancellation_flag_ && !shortcuts_determined());
138
139  cancellation_flag_ = new SharedCancellationFlag;
140  content::BrowserThread::PostTaskAndReplyWithResult(
141      content::BrowserThread::FILE,
142      FROM_HERE,
143      base::Bind(&GetChromeLaunchShortcuts, cancellation_flag_),
144      base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport,
145                 weak_ptr_factory_.GetWeakPtr(),
146                 callback));
147}
148
149void ResettableSettingsSnapshot::SetShortcutsAndReport(
150    const base::Closure& callback,
151    const std::vector<ShortcutCommand>& shortcuts) {
152  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
153  shortcuts_ = shortcuts;
154  shortcuts_determined_ = true;
155  cancellation_flag_ = NULL;
156
157  if (!callback.is_null())
158    callback.Run();
159}
160
161std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot,
162                                    int field_mask) {
163  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
164  base::DictionaryValue dict;
165
166  if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) {
167    base::ListValue* list = new base::ListValue;
168    const std::vector<GURL>& urls = snapshot.startup_urls();
169    for (std::vector<GURL>::const_iterator i = urls.begin();
170         i != urls.end(); ++i)
171      list->AppendString(i->spec());
172    dict.Set(kStartupURLPath, list);
173    dict.SetInteger(kStartupTypePath, snapshot.startup_type());
174  }
175
176  if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) {
177    dict.SetString(kHomepagePath, snapshot.homepage());
178    dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp());
179    dict.SetBoolean(kShowHomeButton, snapshot.show_home_button());
180  }
181
182  if (field_mask & ResettableSettingsSnapshot::DSE_URL)
183    dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url());
184
185  if (field_mask & ResettableSettingsSnapshot::EXTENSIONS) {
186    base::ListValue* list = new base::ListValue;
187    const ResettableSettingsSnapshot::ExtensionList& extensions =
188        snapshot.enabled_extensions();
189    for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
190         extensions.begin(); i != extensions.end(); ++i) {
191      // Replace "\"" to simplify server-side analysis.
192      std::string ext_name;
193      base::ReplaceChars(i->second, "\"", "\'", &ext_name);
194      list->AppendString(i->first + ";" + ext_name);
195    }
196    dict.Set(kEnabledExtensions, list);
197  }
198
199  if (field_mask & ResettableSettingsSnapshot::SHORTCUTS) {
200    base::ListValue* list = new base::ListValue;
201    const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts();
202    for (std::vector<ShortcutCommand>::const_iterator i = shortcuts.begin();
203         i != shortcuts.end(); ++i) {
204      base::string16 arguments;
205      // Replace "\"" to simplify server-side analysis.
206      base::ReplaceChars(i->second, base::ASCIIToUTF16("\""),
207                         base::ASCIIToUTF16("\'"), &arguments);
208      list->AppendString(arguments);
209    }
210    dict.Set(kShortcuts, list);
211  }
212
213  COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31,
214                 serialize_new_field_here);
215
216  std::string json;
217  base::JSONWriter::Write(&dict, &json);
218  return json;
219}
220
221void SendSettingsFeedback(const std::string& report,
222                          Profile* profile) {
223  scoped_refptr<FeedbackData> feedback_data = new FeedbackData();
224  feedback_data->set_category_tag(kProfileResetWebUIBucket);
225  feedback_data->set_description(report);
226
227  feedback_data->set_image(make_scoped_ptr(new std::string));
228  feedback_data->set_context(profile);
229
230  feedback_data->set_page_url("");
231  feedback_data->set_user_email("");
232
233  feedback_util::SendReport(feedback_data);
234}
235
236scoped_ptr<base::ListValue> GetReadableFeedbackForSnapshot(
237    Profile* profile,
238    const ResettableSettingsSnapshot& snapshot) {
239  DCHECK(profile);
240  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
241  scoped_ptr<base::ListValue> list(new base::ListValue);
242  AddPair(list.get(),
243          l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE),
244          g_browser_process->GetApplicationLocale());
245  AddPair(list.get(),
246          l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT),
247          GetUserAgent());
248  chrome::VersionInfo version_info;
249  std::string version = version_info.Version();
250  version += chrome::VersionInfo::GetVersionStringModifier();
251  AddPair(list.get(),
252          l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
253          version);
254
255  // Add snapshot data.
256  const std::vector<GURL>& urls = snapshot.startup_urls();
257  std::string startup_urls;
258  for (std::vector<GURL>::const_iterator i = urls.begin();
259       i != urls.end(); ++i) {
260    if (!startup_urls.empty())
261      startup_urls += ' ';
262    startup_urls += i->host();
263  }
264  if (!startup_urls.empty()) {
265    AddPair(list.get(),
266            l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS),
267            startup_urls);
268  }
269
270  base::string16 startup_type;
271  switch (snapshot.startup_type()) {
272    case SessionStartupPref::DEFAULT:
273      startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB);
274      break;
275    case SessionStartupPref::LAST:
276      startup_type = l10n_util::GetStringUTF16(
277          IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION);
278      break;
279    case SessionStartupPref::URLS:
280      startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES);
281      break;
282    default:
283      break;
284  }
285  AddPair(list.get(),
286          l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE),
287          startup_type);
288
289  if (!snapshot.homepage().empty()) {
290    AddPair(list.get(),
291            l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE),
292            snapshot.homepage());
293  }
294
295  int is_ntp_message_id = snapshot.homepage_is_ntp() ?
296      IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE :
297      IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE;
298  AddPair(list.get(),
299          l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP),
300          l10n_util::GetStringUTF16(is_ntp_message_id));
301
302  int show_home_button_id = snapshot.show_home_button() ?
303      IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_TRUE :
304      IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_FALSE;
305  AddPair(
306      list.get(),
307      l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON),
308      l10n_util::GetStringUTF16(show_home_button_id));
309
310  TemplateURLService* service =
311      TemplateURLServiceFactory::GetForProfile(profile);
312  DCHECK(service);
313  TemplateURL* dse = service->GetDefaultSearchProvider();
314  if (dse) {
315    AddPair(list.get(),
316            l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE),
317            dse->GenerateSearchURL(service->search_terms_data()).host());
318  }
319
320  if (snapshot.shortcuts_determined()) {
321    base::string16 shortcut_targets;
322    const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts();
323    for (std::vector<ShortcutCommand>::const_iterator i =
324         shortcuts.begin(); i != shortcuts.end(); ++i) {
325      if (!shortcut_targets.empty())
326        shortcut_targets += base::ASCIIToUTF16("\n");
327      shortcut_targets += base::ASCIIToUTF16("chrome.exe ");
328      shortcut_targets += i->second;
329    }
330    if (!shortcut_targets.empty()) {
331      AddPair(list.get(),
332              l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS),
333              shortcut_targets);
334    }
335  } else {
336    AddPair(list.get(),
337            l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS),
338            l10n_util::GetStringUTF16(
339                IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS));
340  }
341
342  const ResettableSettingsSnapshot::ExtensionList& extensions =
343      snapshot.enabled_extensions();
344  std::string extension_names;
345  for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
346       extensions.begin(); i != extensions.end(); ++i) {
347    if (!extension_names.empty())
348      extension_names += '\n';
349    extension_names += i->second;
350  }
351  if (!extension_names.empty()) {
352    AddPair(list.get(),
353            l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS),
354            extension_names);
355  }
356  return list.Pass();
357}
358