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/values.h" 11 #include "chrome/browser/browser_process.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/feedback/feedback_data.h" 14 #include "chrome/browser/feedback/feedback_util.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/search_engines/template_url_service.h" 17 #include "chrome/browser/search_engines/template_url_service_factory.h" 18 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/pref_names.h" 20 #include "grit/generated_resources.h" 21 #include "grit/google_chrome_strings.h" 22 #include "ui/base/l10n/l10n_util.h" 23 24 namespace { 25 26 // Feedback bucket labels. 27 const char kProfileResetPromptBucket[] = "SamplingOfSettingsResetPrompt"; 28 const char kProfileResetWebUIBucket[] = "ProfileResetReport"; 29 30 // Dictionary keys for feedback report. 31 const char kDefaultSearchEnginePath[] = "default_search_engine"; 32 const char kEnabledExtensions[] = "enabled_extensions"; 33 const char kHomepageIsNewTabPage[] = "homepage_is_ntp"; 34 const char kHomepagePath[] = "homepage"; 35 const char kStartupTypePath[] = "startup_type"; 36 const char kStartupURLPath[] = "startup_urls"; 37 38 template <class StringType> 39 void AddPair(ListValue* list, 40 const base::string16& key, 41 const StringType& value) { 42 DictionaryValue* results = new DictionaryValue(); 43 results->SetString("key", key); 44 results->SetString("value", value); 45 list->Append(results); 46 } 47 48 } // namespace 49 50 ResettableSettingsSnapshot::ResettableSettingsSnapshot(Profile* profile) 51 : startup_(SessionStartupPref::GetStartupPref(profile)) { 52 // URLs are always stored sorted. 53 std::sort(startup_.urls.begin(), startup_.urls.end()); 54 55 PrefService* prefs = profile->GetPrefs(); 56 DCHECK(prefs); 57 homepage_ = prefs->GetString(prefs::kHomePage); 58 homepage_is_ntp_ = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); 59 60 TemplateURLService* service = 61 TemplateURLServiceFactory::GetForProfile(profile); 62 DCHECK(service); 63 TemplateURL* dse = service->GetDefaultSearchProvider(); 64 if (dse) 65 dse_url_ = dse->url(); 66 67 ExtensionService* extension_service = profile->GetExtensionService(); 68 DCHECK(extension_service); 69 const ExtensionSet* enabled_ext = extension_service->extensions(); 70 enabled_extensions_.reserve(enabled_ext->size()); 71 72 for (ExtensionSet::const_iterator it = enabled_ext->begin(); 73 it != enabled_ext->end(); ++it) 74 enabled_extensions_.push_back(std::make_pair((*it)->id(), (*it)->name())); 75 76 // ExtensionSet is sorted but it seems to be an implementation detail. 77 std::sort(enabled_extensions_.begin(), enabled_extensions_.end()); 78 } 79 80 ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {} 81 82 void ResettableSettingsSnapshot::Subtract( 83 const ResettableSettingsSnapshot& snapshot) { 84 ExtensionList extensions = base::STLSetDifference<ExtensionList>( 85 enabled_extensions_, snapshot.enabled_extensions_); 86 enabled_extensions_.swap(extensions); 87 } 88 89 int ResettableSettingsSnapshot::FindDifferentFields( 90 const ResettableSettingsSnapshot& snapshot) const { 91 int bit_mask = 0; 92 93 if (startup_.type != snapshot.startup_.type || 94 startup_.urls != snapshot.startup_.urls) 95 bit_mask |= STARTUP_MODE; 96 97 if (homepage_is_ntp_ != snapshot.homepage_is_ntp_ || 98 homepage_ != snapshot.homepage_) 99 bit_mask |= HOMEPAGE; 100 101 if (dse_url_ != snapshot.dse_url_) 102 bit_mask |= DSE_URL; 103 104 if (enabled_extensions_ != snapshot.enabled_extensions_) 105 bit_mask |= EXTENSIONS; 106 107 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15, 108 add_new_field_here); 109 110 return bit_mask; 111 } 112 113 std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot, 114 int field_mask) { 115 DictionaryValue dict; 116 117 if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) { 118 ListValue* list = new ListValue; 119 const std::vector<GURL>& urls = snapshot.startup_urls(); 120 for (std::vector<GURL>::const_iterator i = urls.begin(); 121 i != urls.end(); ++i) 122 list->AppendString(i->spec()); 123 dict.Set(kStartupURLPath, list); 124 dict.SetInteger(kStartupTypePath, snapshot.startup_type()); 125 } 126 127 if (field_mask & ResettableSettingsSnapshot::HOMEPAGE) { 128 dict.SetString(kHomepagePath, snapshot.homepage()); 129 dict.SetBoolean(kHomepageIsNewTabPage, snapshot.homepage_is_ntp()); 130 } 131 132 if (field_mask & ResettableSettingsSnapshot::DSE_URL) 133 dict.SetString(kDefaultSearchEnginePath, snapshot.dse_url()); 134 135 if (field_mask & ResettableSettingsSnapshot::EXTENSIONS) { 136 ListValue* list = new ListValue; 137 const ResettableSettingsSnapshot::ExtensionList& extensions = 138 snapshot.enabled_extensions(); 139 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = 140 extensions.begin(); i != extensions.end(); ++i) { 141 // Replace "\"" to simplify server-side analysis. 142 std::string ext_name; 143 base::ReplaceChars(i->second, "\"", "\'", &ext_name); 144 list->AppendString(i->first + ";" + ext_name); 145 } 146 dict.Set(kEnabledExtensions, list); 147 } 148 149 COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15, 150 serialize_new_field_here); 151 152 std::string json; 153 base::JSONWriter::Write(&dict, &json); 154 return json; 155 } 156 157 void SendSettingsFeedback(const std::string& report, 158 Profile* profile, 159 SnapshotCaller caller) { 160 scoped_refptr<FeedbackData> feedback_data = new FeedbackData(); 161 std::string bucket; 162 switch (caller) { 163 case PROFILE_RESET_WEBUI: 164 bucket = kProfileResetWebUIBucket; 165 break; 166 case PROFILE_RESET_PROMPT: 167 bucket = kProfileResetPromptBucket; 168 break; 169 } 170 feedback_data->set_category_tag(bucket); 171 feedback_data->set_description(report); 172 173 feedback_data->set_image(scoped_ptr<std::string>(new std::string)); 174 feedback_data->set_profile(profile); 175 176 feedback_data->set_page_url(""); 177 feedback_data->set_user_email(""); 178 179 feedback_util::SendReport(feedback_data); 180 } 181 182 ListValue* GetReadableFeedback(Profile* profile) { 183 DCHECK(profile); 184 ListValue* list = new ListValue; 185 AddPair(list, l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE), 186 g_browser_process->GetApplicationLocale()); 187 AddPair(list, 188 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT), 189 content::GetUserAgent(GURL())); 190 chrome::VersionInfo version_info; 191 std::string version = version_info.Version(); 192 version += chrome::VersionInfo::GetVersionStringModifier(); 193 AddPair(list, 194 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 195 version); 196 197 // Add snapshot data. 198 ResettableSettingsSnapshot snapshot(profile); 199 const std::vector<GURL>& urls = snapshot.startup_urls(); 200 std::string startup_urls; 201 for (std::vector<GURL>::const_iterator i = urls.begin(); 202 i != urls.end(); ++i) { 203 (startup_urls += i->host()) += ' '; 204 } 205 if (!startup_urls.empty()) { 206 startup_urls.erase(startup_urls.end() - 1); 207 AddPair(list, 208 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS), 209 startup_urls); 210 } 211 212 base::string16 startup_type; 213 switch (snapshot.startup_type()) { 214 case SessionStartupPref::DEFAULT: 215 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB); 216 break; 217 case SessionStartupPref::LAST: 218 startup_type = l10n_util::GetStringUTF16( 219 IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION); 220 break; 221 case SessionStartupPref::URLS: 222 startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES); 223 break; 224 default: 225 break; 226 } 227 AddPair(list, 228 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE), 229 startup_type); 230 231 if (!snapshot.homepage().empty()) { 232 AddPair(list, 233 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE), 234 snapshot.homepage()); 235 } 236 237 int is_ntp_message_id = snapshot.homepage_is_ntp() ? 238 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE : 239 IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE; 240 AddPair(list, 241 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP), 242 l10n_util::GetStringUTF16(is_ntp_message_id)); 243 244 TemplateURLService* service = 245 TemplateURLServiceFactory::GetForProfile(profile); 246 DCHECK(service); 247 TemplateURL* dse = service->GetDefaultSearchProvider(); 248 if (dse) { 249 AddPair(list, 250 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE), 251 TemplateURLService::GenerateSearchURL(dse).host()); 252 } 253 254 const ResettableSettingsSnapshot::ExtensionList& extensions = 255 snapshot.enabled_extensions(); 256 std::string extension_names; 257 for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = 258 extensions.begin(); i != extensions.end(); ++i) { 259 (extension_names += i->second) += '\n'; 260 } 261 if (!extension_names.empty()) { 262 extension_names.erase(extension_names.end() - 1); 263 AddPair(list, 264 l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS), 265 extension_names); 266 } 267 return list; 268 } 269