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/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/profiles/profile.h" 15 #include "chrome/browser/search_engines/template_url_service.h" 16 #include "chrome/browser/search_engines/template_url_service_factory.h" 17 #include "chrome/common/chrome_content_client.h" 18 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/pref_names.h" 20 #include "components/feedback/feedback_data.h" 21 #include "components/feedback/feedback_util.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "grit/generated_resources.h" 24 #include "grit/google_chrome_strings.h" 25 #include "ui/base/l10n/l10n_util.h" 26 27 using feedback::FeedbackData; 28 29 namespace { 30 31 // Feedback bucket labels. 32 const char kProfileResetPromptBucket[] = "SamplingOfSettingsResetPrompt"; 33 const char kProfileResetWebUIBucket[] = "ProfileResetReport"; 34 35 // Dictionary keys for feedback report. 36 const char kDefaultSearchEnginePath[] = "default_search_engine"; 37 const char kEnabledExtensions[] = "enabled_extensions"; 38 const char kHomepageIsNewTabPage[] = "homepage_is_ntp"; 39 const char kHomepagePath[] = "homepage"; 40 const char kShortcuts[] = "shortcuts"; 41 const char kStartupTypePath[] = "startup_type"; 42 const char kStartupURLPath[] = "startup_urls"; 43 44 template <class StringType> 45 void AddPair(base::ListValue* list, 46 const base::string16& key, 47 const StringType& value) { 48 base::DictionaryValue* results = new base::DictionaryValue(); 49 results->SetString("key", key); 50 results->SetString("value", value); 51 list->Append(results); 52 } 53 54 } // namespace 55 56 ResettableSettingsSnapshot::ResettableSettingsSnapshot( 57 Profile* profile) 58 : startup_(SessionStartupPref::GetStartupPref(profile)), 59 shortcuts_determined_(false), 60 weak_ptr_factory_(this) { 61 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 62 // URLs are always stored sorted. 63 std::sort(startup_.urls.begin(), startup_.urls.end()); 64 65 PrefService* prefs = profile->GetPrefs(); 66 DCHECK(prefs); 67 homepage_ = prefs->GetString(prefs::kHomePage); 68 homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); 69 70 TemplateURLService* service = 71 TemplateURLServiceFactory::GetForProfile(profile); 72 DCHECK(service); 73 TemplateURL* dse = service->GetDefaultSearchProvider(); 74 if (dse) 75 dse_url_ = dse->url(); 76 77 ExtensionService* extension_service = profile->GetExtensionService(); 78 DCHECK(extension_service); 79 const extensions::ExtensionSet* enabled_ext = extension_service->extensions(); 80 enabled_extensions_.reserve(enabled_ext->size()); 81 82 for (extensions::ExtensionSet::const_iterator it = enabled_ext->begin(); 83 it != enabled_ext->end(); ++it) 84 enabled_extensions_.push_back(std::make_pair((*it)->id(), (*it)->name())); 85 86 // ExtensionSet is sorted but it seems to be an implementation detail. 87 std::sort(enabled_extensions_.begin(), enabled_extensions_.end()); 88 } 89 90 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() { 91 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 92 if (cancellation_flag_) 93 cancellation_flag_->data.Set(); 94 } 95 96 void ResettableSettingsSnapshot::Subtract( 97 const ResettableSettingsSnapshot& snapshot) { 98 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 99 ExtensionList extensions = base::STLSetDifference<ExtensionList>( 100 enabled_extensions_, snapshot.enabled_extensions_); 101 enabled_extensions_.swap(extensions); 102 } 103 104 int ResettableSettingsSnapshot::FindDifferentFields( 105 const ResettableSettingsSnapshot& snapshot) const { 106 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 107 int bit_mask = 0; 108 109 if (startup_.type != snapshot.startup_.type || 110 startup_.urls != snapshot.startup_.urls) 111 bit_mask |= STARTUP_MODE; 112 113 if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ || 114 homepage_ != snapshot.homepage_) 115 bit_mask |= HOMEPAGE; 116 117 if (dse_url_ != snapshot.dse_url_) 118 bit_mask |= DSE_URL; 119 120 if (enabled_extensions_ != snapshot.enabled_extensions_) 121 bit_mask |= EXTENSIONS; 122 123 if (shortcuts_ != snapshot.shortcuts_) 124 bit_mask |= SHORTCUTS; 125 126 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, 127 add_new_field_here); 128 129 return bit_mask; 130 } 131 132 void ResettableSettingsSnapshot::RequestShortcuts( 133 const base::Closure& callback) { 134 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 135 DCHECK(!cancellation_flag_ && !shortcuts_determined()); 136 137 cancellation_flag_ = new SharedCancellationFlag; 138 content::BrowserThread::PostTaskAndReplyWithResult( 139 content::BrowserThread::FILE, 140 FROM_HERE, 141 base::Bind(&GetChromeLaunchShortcuts, cancellation_flag_), 142 base::Bind(&ResettableSettingsSnapshot::SetShortcutsAndReport, 143 weak_ptr_factory_.GetWeakPtr(), 144 callback)); 145 } 146 147 void ResettableSettingsSnapshot::SetShortcutsAndReport( 148 const base::Closure& callback, 149 const std::vector<ShortcutCommand>& shortcuts) { 150 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 151 shortcuts_ = shortcuts; 152 shortcuts_determined_ = true; 153 cancellation_flag_ = NULL; 154 155 if (!callback.is_null()) 156 callback.Run(); 157 } 158 159 std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot, 160 int field_mask) { 161 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 162 base::DictionaryValue dict; 163 164 if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) { 165 base::ListValue* list = new base::ListValue; 166 const std::vector<GURL>& urls = snapshot.startup_urls(); 167 for (std::vector<GURL>::const_iterator i = urls.begin(); 168 i != urls.end(); ++i) 169 list->AppendString(i->spec()); 170 dict.Set(kStartupURLPath, list); 171 dict.SetInteger(kStartupTypePath, snapshot.startup_type()); 172 } 173 174 if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) { 175 dict.SetString(kHomepagePath, snapshot.homepage()); 176 dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp()); 177 } 178 179 if (field_mask & ResettableSettingsSnapshot::DSE_URL) 180 dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url()); 181 182 if (field_mask & ResettableSettingsSnapshot::EXTENSIONS) { 183 base::ListValue* list = new base::ListValue; 184 const ResettableSettingsSnapshot::ExtensionList& extensions = 185 snapshot.enabled_extensions(); 186 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = 187 extensions.begin(); i != extensions.end(); ++i) { 188 // Replace "\"" to simplify server-side analysis. 189 std::string ext_name; 190 base::ReplaceChars(i->second, "\"", "\'", &ext_name); 191 list->AppendString(i->first + ";" + ext_name); 192 } 193 dict.Set(kEnabledExtensions, list); 194 } 195 196 if (field_mask & ResettableSettingsSnapshot::SHORTCUTS) { 197 base::ListValue* list = new base::ListValue; 198 const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts(); 199 for (std::vector<ShortcutCommand>::const_iterator i = shortcuts.begin(); 200 i != shortcuts.end(); ++i) { 201 base::string16 arguments; 202 // Replace "\"" to simplify server-side analysis. 203 base::ReplaceChars(i->second, base::ASCIIToUTF16("\""), 204 base::ASCIIToUTF16("\'"), &arguments); 205 list->AppendString(arguments); 206 } 207 dict.Set(kShortcuts, list); 208 } 209 210 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, 211 serialize_new_field_here); 212 213 std::string json; 214 base::JSONWriter::Write(&dict, &json); 215 return json; 216 } 217 218 void SendSettingsFeedback(const std::string& report, 219 Profile* profile, 220 SnapshotCaller caller) { 221 scoped_refptr<FeedbackData> feedback_data = new FeedbackData(); 222 std::string bucket; 223 switch (caller) { 224 case PROFILE_RESET_WEBUI: 225 bucket = kProfileResetWebUIBucket; 226 break; 227 case PROFILE_RESET_PROMPT: 228 bucket = kProfileResetPromptBucket; 229 break; 230 } 231 feedback_data->set_category_tag(bucket); 232 feedback_data->set_description(report); 233 234 feedback_data->set_image(make_scoped_ptr(new std::string)); 235 feedback_data->set_context(profile); 236 237 feedback_data->set_page_url(""); 238 feedback_data->set_user_email(""); 239 240 feedback_util::SendReport(feedback_data); 241 } 242 243 scoped_ptr<base::ListValue> GetReadableFeedbackForSnapshot( 244 Profile* profile, 245 const ResettableSettingsSnapshot& snapshot) { 246 DCHECK(profile); 247 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 248 scoped_ptr<base::ListValue> list(new base::ListValue); 249 AddPair(list.get(), 250 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE), 251 g_browser_process->GetApplicationLocale()); 252 AddPair(list.get(), 253 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT), 254 GetUserAgent()); 255 chrome::VersionInfo version_info; 256 std::string version = version_info.Version(); 257 version += chrome::VersionInfo::GetVersionStringModifier(); 258 AddPair(list.get(), 259 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 260 version); 261 262 // Add snapshot data. 263 const std::vector<GURL>& urls = snapshot.startup_urls(); 264 std::string startup_urls; 265 for (std::vector<GURL>::const_iterator i = urls.begin(); 266 i != urls.end(); ++i) { 267 if (!startup_urls.empty()) 268 startup_urls += ' '; 269 startup_urls += i->host(); 270 } 271 if (!startup_urls.empty()) { 272 AddPair(list.get(), 273 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS), 274 startup_urls); 275 } 276 277 base::string16 startup_type; 278 switch (snapshot.startup_type()) { 279 case SessionStartupPref::DEFAULT: 280 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB); 281 break; 282 case SessionStartupPref::LAST: 283 startup_type = l10n_util::GetStringUTF16( 284 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION); 285 break; 286 case SessionStartupPref::URLS: 287 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES); 288 break; 289 default: 290 break; 291 } 292 AddPair(list.get(), 293 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE), 294 startup_type); 295 296 if (!snapshot.homepage().empty()) { 297 AddPair(list.get(), 298 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE), 299 snapshot.homepage()); 300 } 301 302 int is_ntp_message_id = snapshot.homepage_is_ntp() ? 303 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE : 304 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE; 305 AddPair(list.get(), 306 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP), 307 l10n_util::GetStringUTF16(is_ntp_message_id)); 308 309 TemplateURLService* service = 310 TemplateURLServiceFactory::GetForProfile(profile); 311 DCHECK(service); 312 TemplateURL* dse = service->GetDefaultSearchProvider(); 313 if (dse) { 314 AddPair(list.get(), 315 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE), 316 dse->GenerateSearchURL(service->search_terms_data()).host()); 317 } 318 319 if (snapshot.shortcuts_determined()) { 320 base::string16 shortcut_targets; 321 const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts(); 322 for (std::vector<ShortcutCommand>::const_iterator i = 323 shortcuts.begin(); i != shortcuts.end(); ++i) { 324 if (!shortcut_targets.empty()) 325 shortcut_targets += base::ASCIIToUTF16("\n"); 326 shortcut_targets += base::ASCIIToUTF16("chrome.exe "); 327 shortcut_targets += i->second; 328 } 329 if (!shortcut_targets.empty()) { 330 AddPair(list.get(), 331 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), 332 shortcut_targets); 333 } 334 } else { 335 AddPair(list.get(), 336 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), 337 l10n_util::GetStringUTF16( 338 IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS)); 339 } 340 341 const ResettableSettingsSnapshot::ExtensionList& extensions = 342 snapshot.enabled_extensions(); 343 std::string extension_names; 344 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = 345 extensions.begin(); i != extensions.end(); ++i) { 346 if (!extension_names.empty()) 347 extension_names += '\n'; 348 extension_names += i->second; 349 } 350 if (!extension_names.empty()) { 351 AddPair(list.get(), 352 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS), 353 extension_names); 354 } 355 return list.Pass(); 356 } 357