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/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/feedback/feedback_data.h"
14 #include "chrome/browser/feedback/feedback_util.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search_engines/template_url_service.h"
17 #include "chrome/browser/search_engines/template_url_service_factory.h"
18 #include "chrome/common/chrome_version_info.h"
19 #include "chrome/common/pref_names.h"
20 #include "grit/generated_resources.h"
21 #include "grit/google_chrome_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23
24 namespace {
25
26 // Feedback bucket labels.
27 const char kProfileResetPromptBucket[] = "SamplingOfSettingsResetPrompt";
28 const char kProfileResetWebUIBucket[] = "ProfileResetReport";
29
30 // Dictionary keys for feedback report.
31 const char kDefaultSearchEnginePath[] = "default_search_engine";
32 const char kEnabledExtensions[] = "enabled_extensions";
33 const char kHomepageIsNewTabPage[] = "homepage_is_ntp";
34 const char kHomepagePath[] = "homepage";
35 const char kStartupTypePath[] = "startup_type";
36 const char kStartupURLPath[] = "startup_urls";
37
38 template <class StringType>
AddPair(ListValue * list,const base::string16 & key,const StringType & value)39 void AddPair(ListValue* list,
40 const base::string16& key,
41 const StringType& value) {
42 DictionaryValue* results = new DictionaryValue();
43 results->SetString("key", key);
44 results->SetString("value", value);
45 list->Append(results);
46 }
47
48 } // namespace
49
ResettableSettingsSnapshot(Profile * profile)50 ResettableSettingsSnapshot::ResettableSettingsSnapshot(Profile* profile)
51 : startup_(SessionStartupPref::GetStartupPref(profile)) {
52 // URLs are always stored sorted.
53 std::sort(startup_.urls.begin(), startup_.urls.end());
54
55 PrefService* prefs = profile->GetPrefs();
56 DCHECK(prefs);
57 homepage_ = prefs->GetString(prefs::kHomePage);
58 homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage);
59
60 TemplateURLService* service =
61 TemplateURLServiceFactory::GetForProfile(profile);
62 DCHECK(service);
63 TemplateURL* dse = service->GetDefaultSearchProvider();
64 if (dse)
65 dse_url_ = dse->url();
66
67 ExtensionService* extension_service = profile->GetExtensionService();
68 DCHECK(extension_service);
69 const ExtensionSet* enabled_ext = extension_service->extensions();
70 enabled_extensions_.reserve(enabled_ext->size());
71
72 for (ExtensionSet::const_iterator it = enabled_ext->begin();
73 it != enabled_ext->end(); ++it)
74 enabled_extensions_.push_back(std::make_pair((*it)->id(), (*it)->name()));
75
76 // ExtensionSet is sorted but it seems to be an implementation detail.
77 std::sort(enabled_extensions_.begin(), enabled_extensions_.end());
78 }
79
~ResettableSettingsSnapshot()80 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {}
81
Subtract(const ResettableSettingsSnapshot & snapshot)82 void ResettableSettingsSnapshot::Subtract(
83 const ResettableSettingsSnapshot& snapshot) {
84 ExtensionList extensions = base::STLSetDifference<ExtensionList>(
85 enabled_extensions_, snapshot.enabled_extensions_);
86 enabled_extensions_.swap(extensions);
87 }
88
FindDifferentFields(const ResettableSettingsSnapshot & snapshot) const89 int ResettableSettingsSnapshot::FindDifferentFields(
90 const ResettableSettingsSnapshot& snapshot) const {
91 int bit_mask = 0;
92
93 if (startup_.type != snapshot.startup_.type ||
94 startup_.urls != snapshot.startup_.urls)
95 bit_mask |= STARTUP_MODE;
96
97 if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ ||
98 homepage_ != snapshot.homepage_)
99 bit_mask |= HOMEPAGE;
100
101 if (dse_url_ != snapshot.dse_url_)
102 bit_mask |= DSE_URL;
103
104 if (enabled_extensions_ != snapshot.enabled_extensions_)
105 bit_mask |= EXTENSIONS;
106
107 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15,
108 add_new_field_here);
109
110 return bit_mask;
111 }
112
SerializeSettingsReport(const ResettableSettingsSnapshot & snapshot,int field_mask)113 std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot,
114 int field_mask) {
115 DictionaryValue dict;
116
117 if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) {
118 ListValue* list = new ListValue;
119 const std::vector<GURL>& urls = snapshot.startup_urls();
120 for (std::vector<GURL>::const_iterator i = urls.begin();
121 i != urls.end(); ++i)
122 list->AppendString(i->spec());
123 dict.Set(kStartupURLPath, list);
124 dict.SetInteger(kStartupTypePath, snapshot.startup_type());
125 }
126
127 if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) {
128 dict.SetString(kHomepagePath, snapshot.homepage());
129 dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp());
130 }
131
132 if (field_mask & ResettableSettingsSnapshot::DSE_URL)
133 dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url());
134
135 if (field_mask & ResettableSettingsSnapshot::EXTENSIONS) {
136 ListValue* list = new ListValue;
137 const ResettableSettingsSnapshot::ExtensionList& extensions =
138 snapshot.enabled_extensions();
139 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
140 extensions.begin(); i != extensions.end(); ++i) {
141 // Replace "\"" to simplify server-side analysis.
142 std::string ext_name;
143 base::ReplaceChars(i->second, "\"", "\'", &ext_name);
144 list->AppendString(i->first + ";" + ext_name);
145 }
146 dict.Set(kEnabledExtensions, list);
147 }
148
149 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15,
150 serialize_new_field_here);
151
152 std::string json;
153 base::JSONWriter::Write(&dict, &json);
154 return json;
155 }
156
SendSettingsFeedback(const std::string & report,Profile * profile,SnapshotCaller caller)157 void SendSettingsFeedback(const std::string& report,
158 Profile* profile,
159 SnapshotCaller caller) {
160 scoped_refptr<FeedbackData> feedback_data = new FeedbackData();
161 std::string bucket;
162 switch (caller) {
163 case PROFILE_RESET_WEBUI:
164 bucket = kProfileResetWebUIBucket;
165 break;
166 case PROFILE_RESET_PROMPT:
167 bucket = kProfileResetPromptBucket;
168 break;
169 }
170 feedback_data->set_category_tag(bucket);
171 feedback_data->set_description(report);
172
173 feedback_data->set_image(scoped_ptr<std::string>(new std::string));
174 feedback_data->set_profile(profile);
175
176 feedback_data->set_page_url("");
177 feedback_data->set_user_email("");
178
179 feedback_util::SendReport(feedback_data);
180 }
181
GetReadableFeedback(Profile * profile)182 ListValue* GetReadableFeedback(Profile* profile) {
183 DCHECK(profile);
184 ListValue* list = new ListValue;
185 AddPair(list, l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE),
186 g_browser_process->GetApplicationLocale());
187 AddPair(list,
188 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT),
189 content::GetUserAgent(GURL()));
190 chrome::VersionInfo version_info;
191 std::string version = version_info.Version();
192 version += chrome::VersionInfo::GetVersionStringModifier();
193 AddPair(list,
194 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
195 version);
196
197 // Add snapshot data.
198 ResettableSettingsSnapshot snapshot(profile);
199 const std::vector<GURL>& urls = snapshot.startup_urls();
200 std::string startup_urls;
201 for (std::vector<GURL>::const_iterator i = urls.begin();
202 i != urls.end(); ++i) {
203 (startup_urls += i->host()) += ' ';
204 }
205 if (!startup_urls.empty()) {
206 startup_urls.erase(startup_urls.end() - 1);
207 AddPair(list,
208 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS),
209 startup_urls);
210 }
211
212 base::string16 startup_type;
213 switch (snapshot.startup_type()) {
214 case SessionStartupPref::DEFAULT:
215 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB);
216 break;
217 case SessionStartupPref::LAST:
218 startup_type = l10n_util::GetStringUTF16(
219 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION);
220 break;
221 case SessionStartupPref::URLS:
222 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES);
223 break;
224 default:
225 break;
226 }
227 AddPair(list,
228 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE),
229 startup_type);
230
231 if (!snapshot.homepage().empty()) {
232 AddPair(list,
233 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE),
234 snapshot.homepage());
235 }
236
237 int is_ntp_message_id = snapshot.homepage_is_ntp() ?
238 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE :
239 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE;
240 AddPair(list,
241 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP),
242 l10n_util::GetStringUTF16(is_ntp_message_id));
243
244 TemplateURLService* service =
245 TemplateURLServiceFactory::GetForProfile(profile);
246 DCHECK(service);
247 TemplateURL* dse = service->GetDefaultSearchProvider();
248 if (dse) {
249 AddPair(list,
250 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE),
251 TemplateURLService::GenerateSearchURL(dse).host());
252 }
253
254 const ResettableSettingsSnapshot::ExtensionList& extensions =
255 snapshot.enabled_extensions();
256 std::string extension_names;
257 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i =
258 extensions.begin(); i != extensions.end(); ++i) {
259 (extension_names += i->second) += '\n';
260 }
261 if (!extension_names.empty()) {
262 extension_names.erase(extension_names.end() - 1);
263 AddPair(list,
264 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS),
265 extension_names);
266 }
267 return list;
268 }
269