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/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 "chrome/grit/generated_resources.h"
20 #include "chrome/grit/google_chrome_strings.h"
21 #include "components/feedback/feedback_data.h"
22 #include "components/feedback/feedback_util.h"
23 #include "components/search_engines/template_url_service.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "ui/base/l10n/l10n_util.h"
28 using feedback::FeedbackData
;
32 // Feedback bucket labels.
33 const char kProfileResetPromptBucket
[] = "SamplingOfSettingsResetPrompt";
34 const char kProfileResetWebUIBucket
[] = "ProfileResetReport";
36 // Dictionary keys for feedback report.
37 const char kDefaultSearchEnginePath
[] = "default_search_engine";
38 const char kEnabledExtensions
[] = "enabled_extensions";
39 const char kHomepageIsNewTabPage
[] = "homepage_is_ntp";
40 const char kHomepagePath
[] = "homepage";
41 const char kShortcuts
[] = "shortcuts";
42 const char kShowHomeButton
[] = "show_home_button";
43 const char kStartupTypePath
[] = "startup_type";
44 const char kStartupURLPath
[] = "startup_urls";
46 template <class StringType
>
47 void AddPair(base::ListValue
* list
,
48 const base::string16
& key
,
49 const StringType
& value
) {
50 base::DictionaryValue
* results
= new base::DictionaryValue();
51 results
->SetString("key", key
);
52 results
->SetString("value", value
);
53 list
->Append(results
);
58 ResettableSettingsSnapshot::ResettableSettingsSnapshot(
60 : startup_(SessionStartupPref::GetStartupPref(profile
)),
61 shortcuts_determined_(false),
62 weak_ptr_factory_(this) {
63 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
64 // URLs are always stored sorted.
65 std::sort(startup_
.urls
.begin(), startup_
.urls
.end());
67 PrefService
* prefs
= profile
->GetPrefs();
69 homepage_
= prefs
->GetString(prefs::kHomePage
);
70 homepage_is_ntp_
= prefs
->GetBoolean(prefs::kHomePageIsNewTabPage
);
71 show_home_button_
= prefs
->GetBoolean(prefs::kShowHomeButton
);
73 TemplateURLService
* service
=
74 TemplateURLServiceFactory::GetForProfile(profile
);
76 TemplateURL
* dse
= service
->GetDefaultSearchProvider();
78 dse_url_
= dse
->url();
80 const extensions::ExtensionSet
& enabled_ext
=
81 extensions::ExtensionRegistry::Get(profile
)->enabled_extensions();
82 enabled_extensions_
.reserve(enabled_ext
.size());
84 for (extensions::ExtensionSet::const_iterator it
= enabled_ext
.begin();
85 it
!= enabled_ext
.end(); ++it
)
86 enabled_extensions_
.push_back(std::make_pair((*it
)->id(), (*it
)->name()));
88 // ExtensionSet is sorted but it seems to be an implementation detail.
89 std::sort(enabled_extensions_
.begin(), enabled_extensions_
.end());
92 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {
93 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
94 if (cancellation_flag_
.get())
95 cancellation_flag_
->data
.Set();
98 void ResettableSettingsSnapshot::Subtract(
99 const ResettableSettingsSnapshot
& snapshot
) {
100 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
101 ExtensionList extensions
= base::STLSetDifference
<ExtensionList
>(
102 enabled_extensions_
, snapshot
.enabled_extensions_
);
103 enabled_extensions_
.swap(extensions
);
106 int ResettableSettingsSnapshot::FindDifferentFields(
107 const ResettableSettingsSnapshot
& snapshot
) const {
108 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
111 if (startup_
.type
!= snapshot
.startup_
.type
||
112 startup_
.urls
!= snapshot
.startup_
.urls
)
113 bit_mask
|= STARTUP_MODE
;
115 if (homepage_is_ntp_
!= snapshot
.homepage_is_ntp_
||
116 homepage_
!= snapshot
.homepage_
||
117 show_home_button_
!= snapshot
.show_home_button_
)
118 bit_mask
|= HOMEPAGE
;
120 if (dse_url_
!= snapshot
.dse_url_
)
123 if (enabled_extensions_
!= snapshot
.enabled_extensions_
)
124 bit_mask
|= EXTENSIONS
;
126 if (shortcuts_
!= snapshot
.shortcuts_
)
127 bit_mask
|= SHORTCUTS
;
129 static_assert(ResettableSettingsSnapshot::ALL_FIELDS
== 31,
130 "new field needs to be added here");
135 void ResettableSettingsSnapshot::RequestShortcuts(
136 const base::Closure
& callback
) {
137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
138 DCHECK(!cancellation_flag_
.get() && !shortcuts_determined());
140 cancellation_flag_
= new SharedCancellationFlag
;
141 content::BrowserThread::PostTaskAndReplyWithResult(
142 content::BrowserThread::FILE,
144 base::Bind(&GetChromeLaunchShortcuts
, cancellation_flag_
),
145 base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport
,
146 weak_ptr_factory_
.GetWeakPtr(),
150 void ResettableSettingsSnapshot::SetShortcutsAndReport(
151 const base::Closure
& callback
,
152 const std::vector
<ShortcutCommand
>& shortcuts
) {
153 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
154 shortcuts_
= shortcuts
;
155 shortcuts_determined_
= true;
156 cancellation_flag_
= NULL
;
158 if (!callback
.is_null())
162 std::string
SerializeSettingsReport(const ResettableSettingsSnapshot
& snapshot
,
164 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
165 base::DictionaryValue dict
;
167 if (field_mask
& ResettableSettingsSnapshot::STARTUP_MODE
) {
168 base::ListValue
* list
= new base::ListValue
;
169 const std::vector
<GURL
>& urls
= snapshot
.startup_urls();
170 for (std::vector
<GURL
>::const_iterator i
= urls
.begin();
171 i
!= urls
.end(); ++i
)
172 list
->AppendString(i
->spec());
173 dict
.Set(kStartupURLPath
, list
);
174 dict
.SetInteger(kStartupTypePath
, snapshot
.startup_type());
177 if (field_mask
& ResettableSettingsSnapshot::HOMEPAGE
) {
178 dict
.SetString(kHomepagePath
, snapshot
.homepage());
179 dict
.SetBoolean(kHomepageIsNewTabPage
, snapshot
.homepage_is_ntp());
180 dict
.SetBoolean(kShowHomeButton
, snapshot
.show_home_button());
183 if (field_mask
& ResettableSettingsSnapshot::DSE_URL
)
184 dict
.SetString(kDefaultSearchEnginePath
, snapshot
.dse_url());
186 if (field_mask
& ResettableSettingsSnapshot::EXTENSIONS
) {
187 base::ListValue
* list
= new base::ListValue
;
188 const ResettableSettingsSnapshot::ExtensionList
& extensions
=
189 snapshot
.enabled_extensions();
190 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i
=
191 extensions
.begin(); i
!= extensions
.end(); ++i
) {
192 // Replace "\"" to simplify server-side analysis.
193 std::string ext_name
;
194 base::ReplaceChars(i
->second
, "\"", "\'", &ext_name
);
195 list
->AppendString(i
->first
+ ";" + ext_name
);
197 dict
.Set(kEnabledExtensions
, list
);
200 if (field_mask
& ResettableSettingsSnapshot::SHORTCUTS
) {
201 base::ListValue
* list
= new base::ListValue
;
202 const std::vector
<ShortcutCommand
>& shortcuts
= snapshot
.shortcuts();
203 for (std::vector
<ShortcutCommand
>::const_iterator i
= shortcuts
.begin();
204 i
!= shortcuts
.end(); ++i
) {
205 base::string16 arguments
;
206 // Replace "\"" to simplify server-side analysis.
207 base::ReplaceChars(i
->second
, base::ASCIIToUTF16("\""),
208 base::ASCIIToUTF16("\'"), &arguments
);
209 list
->AppendString(arguments
);
211 dict
.Set(kShortcuts
, list
);
214 static_assert(ResettableSettingsSnapshot::ALL_FIELDS
== 31,
215 "new field needs to be serialized here");
218 base::JSONWriter::Write(&dict
, &json
);
222 void SendSettingsFeedback(const std::string
& report
,
224 SnapshotCaller caller
) {
225 scoped_refptr
<FeedbackData
> feedback_data
= new FeedbackData();
228 case PROFILE_RESET_WEBUI
:
229 bucket
= kProfileResetWebUIBucket
;
231 case PROFILE_RESET_PROMPT
:
232 bucket
= kProfileResetPromptBucket
;
235 feedback_data
->set_category_tag(bucket
);
236 feedback_data
->set_description(report
);
238 feedback_data
->set_image(make_scoped_ptr(new std::string
));
239 feedback_data
->set_context(profile
);
241 feedback_data
->set_page_url("");
242 feedback_data
->set_user_email("");
244 feedback_util::SendReport(feedback_data
);
247 scoped_ptr
<base::ListValue
> GetReadableFeedbackForSnapshot(
249 const ResettableSettingsSnapshot
& snapshot
) {
251 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
252 scoped_ptr
<base::ListValue
> list(new base::ListValue
);
254 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE
),
255 g_browser_process
->GetApplicationLocale());
257 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT
),
259 chrome::VersionInfo version_info
;
260 std::string version
= version_info
.Version();
261 version
+= chrome::VersionInfo::GetVersionStringModifier();
263 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
266 // Add snapshot data.
267 const std::vector
<GURL
>& urls
= snapshot
.startup_urls();
268 std::string startup_urls
;
269 for (std::vector
<GURL
>::const_iterator i
= urls
.begin();
270 i
!= urls
.end(); ++i
) {
271 if (!startup_urls
.empty())
273 startup_urls
+= i
->host();
275 if (!startup_urls
.empty()) {
277 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS
),
281 base::string16 startup_type
;
282 switch (snapshot
.startup_type()) {
283 case SessionStartupPref::DEFAULT
:
284 startup_type
= l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB
);
286 case SessionStartupPref::LAST
:
287 startup_type
= l10n_util::GetStringUTF16(
288 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION
);
290 case SessionStartupPref::URLS
:
291 startup_type
= l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES
);
297 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE
),
300 if (!snapshot
.homepage().empty()) {
302 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE
),
303 snapshot
.homepage());
306 int is_ntp_message_id
= snapshot
.homepage_is_ntp() ?
307 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE
:
308 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE
;
310 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP
),
311 l10n_util::GetStringUTF16(is_ntp_message_id
));
313 int show_home_button_id
= snapshot
.show_home_button() ?
314 IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_TRUE
:
315 IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON_FALSE
;
318 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON
),
319 l10n_util::GetStringUTF16(show_home_button_id
));
321 TemplateURLService
* service
=
322 TemplateURLServiceFactory::GetForProfile(profile
);
324 TemplateURL
* dse
= service
->GetDefaultSearchProvider();
327 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE
),
328 dse
->GenerateSearchURL(service
->search_terms_data()).host());
331 if (snapshot
.shortcuts_determined()) {
332 base::string16 shortcut_targets
;
333 const std::vector
<ShortcutCommand
>& shortcuts
= snapshot
.shortcuts();
334 for (std::vector
<ShortcutCommand
>::const_iterator i
=
335 shortcuts
.begin(); i
!= shortcuts
.end(); ++i
) {
336 if (!shortcut_targets
.empty())
337 shortcut_targets
+= base::ASCIIToUTF16("\n");
338 shortcut_targets
+= base::ASCIIToUTF16("chrome.exe ");
339 shortcut_targets
+= i
->second
;
341 if (!shortcut_targets
.empty()) {
343 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS
),
348 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS
),
349 l10n_util::GetStringUTF16(
350 IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS
));
353 const ResettableSettingsSnapshot::ExtensionList
& extensions
=
354 snapshot
.enabled_extensions();
355 std::string extension_names
;
356 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i
=
357 extensions
.begin(); i
!= extensions
.end(); ++i
) {
358 if (!extension_names
.empty())
359 extension_names
+= '\n';
360 extension_names
+= i
->second
;
362 if (!extension_names
.empty()) {
364 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS
),