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 "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" 27 28 using feedback::FeedbackData; 29 30 namespace { 31 32 // Feedback bucket labels. 33 const char kProfileResetPromptBucket[] = "SamplingOfSettingsResetPrompt"; 34 const char kProfileResetWebUIBucket[] = "ProfileResetReport"; 35 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"; 45 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); 54 } 55 56 } // namespace 57 58 ResettableSettingsSnapshot::ResettableSettingsSnapshot( 59 Profile* profile) 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()); 66 67 PrefService* prefs = profile->GetPrefs(); 68 DCHECK(prefs); 69 homepage_ = prefs->GetString(prefs::kHomePage); 70 homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); 71 show_home_button_ = prefs->GetBoolean(prefs::kShowHomeButton); 72 73 TemplateURLService* service = 74 TemplateURLServiceFactory::GetForProfile(profile); 75 DCHECK(service); 76 TemplateURL* dse = service->GetDefaultSearchProvider(); 77 if (dse) 78 dse_url_ = dse->url(); 79 80 const extensions::ExtensionSet& enabled_ext = 81 extensions::ExtensionRegistry::Get(profile)->enabled_extensions(); 82 enabled_extensions_.reserve(enabled_ext.size()); 83 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())); 87 88 // ExtensionSet is sorted but it seems to be an implementation detail. 89 std::sort(enabled_extensions_.begin(), enabled_extensions_.end()); 90 } 91 92 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() { 93 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 94 if (cancellation_flag_.get()) 95 cancellation_flag_->data.Set(); 96 } 97 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); 104 } 105 106 int ResettableSettingsSnapshot::FindDifferentFields( 107 const ResettableSettingsSnapshot& snapshot) const { 108 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 109 int bit_mask = 0; 110 111 if (startup_.type != snapshot.startup_.type || 112 startup_.urls != snapshot.startup_.urls) 113 bit_mask |= STARTUP_MODE; 114 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; 119 120 if (dse_url_ != snapshot.dse_url_) 121 bit_mask |= DSE_URL; 122 123 if (enabled_extensions_ != snapshot.enabled_extensions_) 124 bit_mask |= EXTENSIONS; 125 126 if (shortcuts_ != snapshot.shortcuts_) 127 bit_mask |= SHORTCUTS; 128 129 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, 130 add_new_field_here); 131 132 return bit_mask; 133 } 134 135 void ResettableSettingsSnapshot::RequestShortcuts( 136 const base::Closure& callback) { 137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 138 DCHECK(!cancellation_flag_.get() && !shortcuts_determined()); 139 140 cancellation_flag_ = new SharedCancellationFlag; 141 content::BrowserThread::PostTaskAndReplyWithResult( 142 content::BrowserThread::FILE, 143 FROM_HERE, 144 base::Bind(&GetChromeLaunchShortcuts, cancellation_flag_), 145 base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport, 146 weak_ptr_factory_.GetWeakPtr(), 147 callback)); 148 } 149 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; 157 158 if (!callback.is_null()) 159 callback.Run(); 160 } 161 162 std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot, 163 int field_mask) { 164 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 165 base::DictionaryValue dict; 166 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()); 175 } 176 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()); 181 } 182 183 if (field_mask & ResettableSettingsSnapshot::DSE_URL) 184 dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url()); 185 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); 196 } 197 dict.Set(kEnabledExtensions, list); 198 } 199 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); 210 } 211 dict.Set(kShortcuts, list); 212 } 213 214 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, 215 serialize_new_field_here); 216 217 std::string json; 218 base::JSONWriter::Write(&dict, &json); 219 return json; 220 } 221 222 void SendSettingsFeedback(const std::string& report, 223 Profile* profile, 224 SnapshotCaller caller) { 225 scoped_refptr<FeedbackData> feedback_data = new FeedbackData(); 226 std::string bucket; 227 switch (caller) { 228 case PROFILE_RESET_WEBUI: 229 bucket = kProfileResetWebUIBucket; 230 break; 231 case PROFILE_RESET_PROMPT: 232 bucket = kProfileResetPromptBucket; 233 break; 234 } 235 feedback_data->set_category_tag(bucket); 236 feedback_data->set_description(report); 237 238 feedback_data->set_image(make_scoped_ptr(new std::string)); 239 feedback_data->set_context(profile); 240 241 feedback_data->set_page_url(""); 242 feedback_data->set_user_email(""); 243 244 feedback_util::SendReport(feedback_data); 245 } 246 247 scoped_ptr<base::ListValue> GetReadableFeedbackForSnapshot( 248 Profile* profile, 249 const ResettableSettingsSnapshot& snapshot) { 250 DCHECK(profile); 251 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 252 scoped_ptr<base::ListValue> list(new base::ListValue); 253 AddPair(list.get(), 254 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE), 255 g_browser_process->GetApplicationLocale()); 256 AddPair(list.get(), 257 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT), 258 GetUserAgent()); 259 chrome::VersionInfo version_info; 260 std::string version = version_info.Version(); 261 version += chrome::VersionInfo::GetVersionStringModifier(); 262 AddPair(list.get(), 263 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 264 version); 265 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()) 272 startup_urls += ' '; 273 startup_urls += i->host(); 274 } 275 if (!startup_urls.empty()) { 276 AddPair(list.get(), 277 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS), 278 startup_urls); 279 } 280 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); 285 break; 286 case SessionStartupPref::LAST: 287 startup_type = l10n_util::GetStringUTF16( 288 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION); 289 break; 290 case SessionStartupPref::URLS: 291 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES); 292 break; 293 default: 294 break; 295 } 296 AddPair(list.get(), 297 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE), 298 startup_type); 299 300 if (!snapshot.homepage().empty()) { 301 AddPair(list.get(), 302 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE), 303 snapshot.homepage()); 304 } 305 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; 309 AddPair(list.get(), 310 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP), 311 l10n_util::GetStringUTF16(is_ntp_message_id)); 312 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; 316 AddPair( 317 list.get(), 318 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHOW_HOME_BUTTON), 319 l10n_util::GetStringUTF16(show_home_button_id)); 320 321 TemplateURLService* service = 322 TemplateURLServiceFactory::GetForProfile(profile); 323 DCHECK(service); 324 TemplateURL* dse = service->GetDefaultSearchProvider(); 325 if (dse) { 326 AddPair(list.get(), 327 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE), 328 dse->GenerateSearchURL(service->search_terms_data()).host()); 329 } 330 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; 340 } 341 if (!shortcut_targets.empty()) { 342 AddPair(list.get(), 343 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), 344 shortcut_targets); 345 } 346 } else { 347 AddPair(list.get(), 348 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), 349 l10n_util::GetStringUTF16( 350 IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS)); 351 } 352 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; 361 } 362 if (!extension_names.empty()) { 363 AddPair(list.get(), 364 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS), 365 extension_names); 366 } 367 return list.Pass(); 368 } 369