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