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.
5 #include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
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/synchronization/cancellation_flag.h"
11 #include "base/values.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/feedback/feedback_data.h"
15 #include "chrome/browser/feedback/feedback_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search_engines/template_url_service.h"
18 #include "chrome/browser/search_engines/template_url_service_factory.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/pref_names.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "grit/generated_resources.h"
23 #include "grit/google_chrome_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
28 // Feedback bucket labels.
29 const char kProfileResetPromptBucket
[] = "SamplingOfSettingsResetPrompt";
30 const char kProfileResetWebUIBucket
[] = "ProfileResetReport";
32 // Dictionary keys for feedback report.
33 const char kDefaultSearchEnginePath
[] = "default_search_engine";
34 const char kEnabledExtensions
[] = "enabled_extensions";
35 const char kHomepageIsNewTabPage
[] = "homepage_is_ntp";
36 const char kHomepagePath
[] = "homepage";
37 const char kShortcuts
[] = "shortcuts";
38 const char kStartupTypePath
[] = "startup_type";
39 const char kStartupURLPath
[] = "startup_urls";
41 template <class StringType
>
42 void AddPair(base::ListValue
* list
,
43 const base::string16
& key
,
44 const StringType
& value
) {
45 base::DictionaryValue
* results
= new base::DictionaryValue();
46 results
->SetString("key", key
);
47 results
->SetString("value", value
);
48 list
->Append(results
);
53 ResettableSettingsSnapshot::ResettableSettingsSnapshot(
55 : startup_(SessionStartupPref::GetStartupPref(profile
)),
56 shortcuts_determined_(false),
57 weak_ptr_factory_(this) {
58 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
59 // URLs are always stored sorted.
60 std::sort(startup_
.urls
.begin(), startup_
.urls
.end());
62 PrefService
* prefs
= profile
->GetPrefs();
64 homepage_
= prefs
->GetString(prefs::kHomePage
);
65 homepage_is_ntp_
= prefs
->GetBoolean(prefs::kHomePageIsNewTabPage
);
67 TemplateURLService
* service
=
68 TemplateURLServiceFactory::GetForProfile(profile
);
70 TemplateURL
* dse
= service
->GetDefaultSearchProvider();
72 dse_url_
= dse
->url();
74 ExtensionService
* extension_service
= profile
->GetExtensionService();
75 DCHECK(extension_service
);
76 const extensions::ExtensionSet
* enabled_ext
= extension_service
->extensions();
77 enabled_extensions_
.reserve(enabled_ext
->size());
79 for (extensions::ExtensionSet::const_iterator it
= enabled_ext
->begin();
80 it
!= enabled_ext
->end(); ++it
)
81 enabled_extensions_
.push_back(std::make_pair((*it
)->id(), (*it
)->name()));
83 // ExtensionSet is sorted but it seems to be an implementation detail.
84 std::sort(enabled_extensions_
.begin(), enabled_extensions_
.end());
87 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {
88 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
89 if (cancellation_flag_
)
90 cancellation_flag_
->data
.Set();
93 void ResettableSettingsSnapshot::Subtract(
94 const ResettableSettingsSnapshot
& snapshot
) {
95 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
96 ExtensionList extensions
= base::STLSetDifference
<ExtensionList
>(
97 enabled_extensions_
, snapshot
.enabled_extensions_
);
98 enabled_extensions_
.swap(extensions
);
101 int ResettableSettingsSnapshot::FindDifferentFields(
102 const ResettableSettingsSnapshot
& snapshot
) const {
103 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
106 if (startup_
.type
!= snapshot
.startup_
.type
||
107 startup_
.urls
!= snapshot
.startup_
.urls
)
108 bit_mask
|= STARTUP_MODE
;
110 if (homepage_is_ntp_
!= snapshot
.homepage_is_ntp_
||
111 homepage_
!= snapshot
.homepage_
)
112 bit_mask
|= HOMEPAGE
;
114 if (dse_url_
!= snapshot
.dse_url_
)
117 if (enabled_extensions_
!= snapshot
.enabled_extensions_
)
118 bit_mask
|= EXTENSIONS
;
120 if (shortcuts_
!= snapshot
.shortcuts_
)
121 bit_mask
|= SHORTCUTS
;
123 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS
== 31,
129 void ResettableSettingsSnapshot::RequestShortcuts(
130 const base::Closure
& callback
) {
131 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
132 DCHECK(!cancellation_flag_
&& !shortcuts_determined());
134 cancellation_flag_
= new SharedCancellationFlag
;
135 content::BrowserThread::PostTaskAndReplyWithResult(
136 content::BrowserThread::FILE,
138 base::Bind(&GetChromeLaunchShortcuts
, cancellation_flag_
),
139 base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport
,
140 weak_ptr_factory_
.GetWeakPtr(),
144 void ResettableSettingsSnapshot::SetShortcutsAndReport(
145 const base::Closure
& callback
,
146 const std::vector
<ShortcutCommand
>& shortcuts
) {
147 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
148 shortcuts_
= shortcuts
;
149 shortcuts_determined_
= true;
150 cancellation_flag_
= NULL
;
152 if (!callback
.is_null())
156 std::string
SerializeSettingsReport(const ResettableSettingsSnapshot
& snapshot
,
158 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
159 base::DictionaryValue dict
;
161 if (field_mask
& ResettableSettingsSnapshot::STARTUP_MODE
) {
162 base::ListValue
* list
= new base::ListValue
;
163 const std::vector
<GURL
>& urls
= snapshot
.startup_urls();
164 for (std::vector
<GURL
>::const_iterator i
= urls
.begin();
165 i
!= urls
.end(); ++i
)
166 list
->AppendString(i
->spec());
167 dict
.Set(kStartupURLPath
, list
);
168 dict
.SetInteger(kStartupTypePath
, snapshot
.startup_type());
171 if (field_mask
& ResettableSettingsSnapshot::HOMEPAGE
) {
172 dict
.SetString(kHomepagePath
, snapshot
.homepage());
173 dict
.SetBoolean(kHomepageIsNewTabPage
, snapshot
.homepage_is_ntp());
176 if (field_mask
& ResettableSettingsSnapshot::DSE_URL
)
177 dict
.SetString(kDefaultSearchEnginePath
, snapshot
.dse_url());
179 if (field_mask
& ResettableSettingsSnapshot::EXTENSIONS
) {
180 base::ListValue
* list
= new base::ListValue
;
181 const ResettableSettingsSnapshot::ExtensionList
& extensions
=
182 snapshot
.enabled_extensions();
183 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i
=
184 extensions
.begin(); i
!= extensions
.end(); ++i
) {
185 // Replace "\"" to simplify server-side analysis.
186 std::string ext_name
;
187 base::ReplaceChars(i
->second
, "\"", "\'", &ext_name
);
188 list
->AppendString(i
->first
+ ";" + ext_name
);
190 dict
.Set(kEnabledExtensions
, list
);
193 if (field_mask
& ResettableSettingsSnapshot::SHORTCUTS
) {
194 base::ListValue
* list
= new base::ListValue
;
195 const std::vector
<ShortcutCommand
>& shortcuts
= snapshot
.shortcuts();
196 for (std::vector
<ShortcutCommand
>::const_iterator i
= shortcuts
.begin();
197 i
!= shortcuts
.end(); ++i
) {
198 base::string16 arguments
;
199 // Replace "\"" to simplify server-side analysis.
200 base::ReplaceChars(i
->second
, base::ASCIIToUTF16("\"").c_str(),
201 base::ASCIIToUTF16("\'"), &arguments
);
202 list
->AppendString(arguments
);
204 dict
.Set(kShortcuts
, list
);
207 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS
== 31,
208 serialize_new_field_here
);
211 base::JSONWriter::Write(&dict
, &json
);
215 void SendSettingsFeedback(const std::string
& report
,
217 SnapshotCaller caller
) {
218 scoped_refptr
<FeedbackData
> feedback_data
= new FeedbackData();
221 case PROFILE_RESET_WEBUI
:
222 bucket
= kProfileResetWebUIBucket
;
224 case PROFILE_RESET_PROMPT
:
225 bucket
= kProfileResetPromptBucket
;
228 feedback_data
->set_category_tag(bucket
);
229 feedback_data
->set_description(report
);
231 feedback_data
->set_image(make_scoped_ptr(new std::string
));
232 feedback_data
->set_profile(profile
);
234 feedback_data
->set_page_url("");
235 feedback_data
->set_user_email("");
237 feedback_util::SendReport(feedback_data
);
240 scoped_ptr
<base::ListValue
> GetReadableFeedbackForSnapshot(
242 const ResettableSettingsSnapshot
& snapshot
) {
244 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
245 scoped_ptr
<base::ListValue
> list(new base::ListValue
);
247 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE
),
248 g_browser_process
->GetApplicationLocale());
250 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT
),
251 content::GetUserAgent(GURL()));
252 chrome::VersionInfo version_info
;
253 std::string version
= version_info
.Version();
254 version
+= chrome::VersionInfo::GetVersionStringModifier();
256 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
259 // Add snapshot data.
260 const std::vector
<GURL
>& urls
= snapshot
.startup_urls();
261 std::string startup_urls
;
262 for (std::vector
<GURL
>::const_iterator i
= urls
.begin();
263 i
!= urls
.end(); ++i
) {
264 if (!startup_urls
.empty())
266 startup_urls
+= i
->host();
268 if (!startup_urls
.empty()) {
270 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS
),
274 base::string16 startup_type
;
275 switch (snapshot
.startup_type()) {
276 case SessionStartupPref::DEFAULT
:
277 startup_type
= l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB
);
279 case SessionStartupPref::LAST
:
280 startup_type
= l10n_util::GetStringUTF16(
281 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION
);
283 case SessionStartupPref::URLS
:
284 startup_type
= l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES
);
290 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE
),
293 if (!snapshot
.homepage().empty()) {
295 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE
),
296 snapshot
.homepage());
299 int is_ntp_message_id
= snapshot
.homepage_is_ntp() ?
300 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE
:
301 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE
;
303 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP
),
304 l10n_util::GetStringUTF16(is_ntp_message_id
));
306 TemplateURLService
* service
=
307 TemplateURLServiceFactory::GetForProfile(profile
);
309 TemplateURL
* dse
= service
->GetDefaultSearchProvider();
312 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE
),
313 TemplateURLService::GenerateSearchURL(dse
).host());
316 if (snapshot
.shortcuts_determined()) {
317 base::string16 shortcut_targets
;
318 const std::vector
<ShortcutCommand
>& shortcuts
= snapshot
.shortcuts();
319 for (std::vector
<ShortcutCommand
>::const_iterator i
=
320 shortcuts
.begin(); i
!= shortcuts
.end(); ++i
) {
321 if (!shortcut_targets
.empty())
322 shortcut_targets
+= base::ASCIIToUTF16("\n");
323 shortcut_targets
+= base::ASCIIToUTF16("chrome.exe ");
324 shortcut_targets
+= i
->second
;
326 if (!shortcut_targets
.empty()) {
328 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS
),
333 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS
),
334 l10n_util::GetStringUTF16(
335 IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS
));
338 const ResettableSettingsSnapshot::ExtensionList
& extensions
=
339 snapshot
.enabled_extensions();
340 std::string extension_names
;
341 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i
=
342 extensions
.begin(); i
!= extensions
.end(); ++i
) {
343 if (!extension_names
.empty())
344 extension_names
+= '\n';
345 extension_names
+= i
->second
;
347 if (!extension_names
.empty()) {
349 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS
),