1 // Copyright 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/automatic_profile_resetter_delegate.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/callback.h" 12 #include "base/logging.h" 13 #include "base/md5.h" 14 #include "base/memory/scoped_vector.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_util.h" 17 #include "base/values.h" 18 #include "chrome/app/chrome_command_ids.h" 19 #include "chrome/browser/chrome_notification_types.h" 20 #include "chrome/browser/google/google_util.h" 21 #include "chrome/browser/profile_resetter/brandcode_config_fetcher.h" 22 #include "chrome/browser/profile_resetter/profile_reset_global_error.h" 23 #include "chrome/browser/profile_resetter/profile_resetter.h" 24 #include "chrome/browser/profile_resetter/resettable_settings_snapshot.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" 27 #include "chrome/browser/search_engines/template_url_service.h" 28 #include "chrome/browser/search_engines/template_url_service_factory.h" 29 #include "chrome/browser/ui/browser.h" 30 #include "chrome/browser/ui/browser_finder.h" 31 #include "chrome/browser/ui/global_error/global_error_service.h" 32 #include "chrome/browser/ui/global_error/global_error_service_factory.h" 33 #include "content/public/browser/browser_thread.h" 34 #include "content/public/browser/notification_service.h" 35 36 #if defined(OS_WIN) 37 #include "chrome/browser/enumerate_modules_model_win.h" 38 #endif 39 40 namespace { 41 42 scoped_ptr<base::DictionaryValue> BuildSubTreeFromTemplateURL( 43 const TemplateURL* template_url) { 44 scoped_ptr<base::DictionaryValue> tree(new base::DictionaryValue); 45 tree->SetString("search_url", template_url->url()); 46 tree->SetString("search_terms_replacement_key", 47 template_url->search_terms_replacement_key()); 48 tree->SetString("suggest_url", template_url->suggestions_url()); 49 tree->SetString("instant_url", template_url->instant_url()); 50 tree->SetString("image_url", template_url->image_url()); 51 tree->SetString("new_tab_url", template_url->new_tab_url()); 52 tree->SetString("search_url_post_params", 53 template_url->search_url_post_params()); 54 tree->SetString("suggest_url_post_params", 55 template_url->suggestions_url_post_params()); 56 tree->SetString("instant_url_post_params", 57 template_url->instant_url_post_params()); 58 tree->SetString("image_url_post_params", 59 template_url->image_url_post_params()); 60 tree->SetString("icon_url", template_url->favicon_url().spec()); 61 tree->SetString("name", template_url->short_name()); 62 tree->SetString("keyword", template_url->keyword()); 63 base::ListValue* input_encodings = new base::ListValue; 64 input_encodings->AppendStrings(template_url->input_encodings()); 65 tree->Set("encodings", input_encodings); 66 tree->SetString("id", base::Int64ToString(template_url->id())); 67 tree->SetString("prepopulate_id", 68 base::IntToString(template_url->prepopulate_id())); 69 base::ListValue* alternate_urls = new base::ListValue; 70 alternate_urls->AppendStrings(template_url->alternate_urls()); 71 tree->Set("alternate_urls", alternate_urls); 72 return tree.Pass(); 73 } 74 75 #if defined(OS_WIN) 76 void ExtractLoadedModuleNameDigests( 77 const base::ListValue& module_list, 78 base::ListValue* module_name_digests) { 79 DCHECK(module_name_digests); 80 81 // EnumerateModulesModel produces a list of dictionaries. 82 // Each dictionary corresponds to a module and exposes a number of properties. 83 // We care only about 'type' and 'name'. 84 for (size_t i = 0; i < module_list.GetSize(); ++i) { 85 const base::DictionaryValue* module_dictionary = NULL; 86 if (!module_list.GetDictionary(i, &module_dictionary)) 87 continue; 88 ModuleEnumerator::ModuleType module_type = 89 ModuleEnumerator::LOADED_MODULE; 90 if (!module_dictionary->GetInteger( 91 "type", reinterpret_cast<int*>(&module_type)) || 92 module_type != ModuleEnumerator::LOADED_MODULE) { 93 continue; 94 } 95 std::string module_name; 96 if (!module_dictionary->GetString("name", &module_name)) 97 continue; 98 StringToLowerASCII(&module_name); 99 module_name_digests->AppendString(base::MD5String(module_name)); 100 } 101 } 102 #endif 103 104 } // namespace 105 106 107 // AutomaticProfileResetterDelegateImpl -------------------------------------- 108 109 AutomaticProfileResetterDelegateImpl::AutomaticProfileResetterDelegateImpl( 110 Profile* profile, 111 ProfileResetter::ResettableFlags resettable_aspects) 112 : profile_(profile), 113 global_error_service_(GlobalErrorServiceFactory::GetForProfile(profile_)), 114 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)), 115 resettable_aspects_(resettable_aspects) { 116 DCHECK(profile_); 117 if (template_url_service_) { 118 template_url_service_->AddObserver(this); 119 // Needed so that |template_url_service_ready_event_| will be signaled even 120 // when TemplateURLService had been already initialized before this point. 121 OnTemplateURLServiceChanged(); 122 } 123 124 #if defined(OS_WIN) 125 module_list_.reset(EnumerateModulesModel::GetInstance()->GetModuleList()); 126 #endif 127 if (module_list_) { 128 // Having a non-empty module list proves that enumeration had been already 129 // performed before this point. 130 modules_have_been_enumerated_event_.Signal(); 131 } 132 registrar_.Add(this, 133 chrome::NOTIFICATION_MODULE_LIST_ENUMERATED, 134 content::NotificationService::AllSources()); 135 } 136 137 AutomaticProfileResetterDelegateImpl::~AutomaticProfileResetterDelegateImpl() { 138 if (template_url_service_) 139 template_url_service_->RemoveObserver(this); 140 } 141 142 void AutomaticProfileResetterDelegateImpl::EnumerateLoadedModulesIfNeeded() { 143 if (!modules_have_been_enumerated_event_.is_signaled()) { 144 #if defined(OS_WIN) 145 EnumerateModulesModel::GetInstance()->ScanNow(); 146 #else 147 modules_have_been_enumerated_event_.Signal(); 148 #endif 149 } 150 } 151 152 void AutomaticProfileResetterDelegateImpl:: 153 RequestCallbackWhenLoadedModulesAreEnumerated( 154 const base::Closure& ready_callback) const { 155 DCHECK(!ready_callback.is_null()); 156 modules_have_been_enumerated_event_.Post(FROM_HERE, ready_callback); 157 } 158 159 void AutomaticProfileResetterDelegateImpl::LoadTemplateURLServiceIfNeeded() { 160 DCHECK(template_url_service_); 161 template_url_service_->Load(); // Safe to call even if it has loaded already. 162 } 163 164 void AutomaticProfileResetterDelegateImpl:: 165 RequestCallbackWhenTemplateURLServiceIsLoaded( 166 const base::Closure& ready_callback) const { 167 DCHECK(!ready_callback.is_null()); 168 template_url_service_ready_event_.Post(FROM_HERE, ready_callback); 169 } 170 171 void AutomaticProfileResetterDelegateImpl:: 172 FetchBrandcodedDefaultSettingsIfNeeded() { 173 if (brandcoded_config_fetcher_ || 174 brandcoded_defaults_fetched_event_.is_signaled()) 175 return; 176 177 std::string brandcode; 178 google_util::GetBrand(&brandcode); 179 if (brandcode.empty()) { 180 brandcoded_defaults_.reset(new BrandcodedDefaultSettings); 181 brandcoded_defaults_fetched_event_.Signal(); 182 } else { 183 brandcoded_config_fetcher_.reset(new BrandcodeConfigFetcher( 184 base::Bind( 185 &AutomaticProfileResetterDelegateImpl::OnBrandcodedDefaultsFetched, 186 base::Unretained(this)), 187 GURL("https://tools.google.com/service/update2"), 188 brandcode)); 189 } 190 } 191 192 void AutomaticProfileResetterDelegateImpl:: 193 RequestCallbackWhenBrandcodedDefaultsAreFetched( 194 const base::Closure& ready_callback) const { 195 DCHECK(!ready_callback.is_null()); 196 brandcoded_defaults_fetched_event_.Post(FROM_HERE, ready_callback); 197 } 198 199 scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl:: 200 GetLoadedModuleNameDigests() const { 201 DCHECK(modules_have_been_enumerated_event_.is_signaled()); 202 scoped_ptr<base::ListValue> result(new base::ListValue); 203 #if defined(OS_WIN) 204 if (module_list_) 205 ExtractLoadedModuleNameDigests(*module_list_, result.get()); 206 #endif 207 return result.Pass(); 208 } 209 210 scoped_ptr<base::DictionaryValue> AutomaticProfileResetterDelegateImpl:: 211 GetDefaultSearchProviderDetails() const { 212 DCHECK(template_url_service_); 213 DCHECK(template_url_service_->loaded()); 214 215 const TemplateURL* default_search_provider = 216 template_url_service_->GetDefaultSearchProvider(); 217 218 // Having a NULL default search provider is due to either: 219 // 1.) default search providers being disabled by policy, 220 // 2.) directly tampering with the Preferences and/or the SQLite DBs. 221 // In this state, Omnibox non-keyword search functionality is disabled. 222 return default_search_provider ? 223 BuildSubTreeFromTemplateURL(default_search_provider) : 224 scoped_ptr<base::DictionaryValue>(new base::DictionaryValue); 225 } 226 227 bool AutomaticProfileResetterDelegateImpl:: 228 IsDefaultSearchProviderManaged() const { 229 DCHECK(template_url_service_); 230 DCHECK(template_url_service_->loaded()); 231 return template_url_service_->is_default_search_managed(); 232 } 233 234 scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl:: 235 GetPrepopulatedSearchProvidersDetails() const { 236 size_t default_search_index = 0; 237 ScopedVector<TemplateURL> engines( 238 TemplateURLPrepopulateData::GetPrepopulatedEngines( 239 template_url_service_->profile(), &default_search_index)); 240 scoped_ptr<base::ListValue> engines_details_list(new base::ListValue); 241 for (ScopedVector<TemplateURL>::const_iterator it = engines.begin(); 242 it != engines.end(); ++it) 243 engines_details_list->Append(BuildSubTreeFromTemplateURL(*it).release()); 244 return engines_details_list.Pass(); 245 } 246 247 bool AutomaticProfileResetterDelegateImpl::TriggerPrompt() { 248 DCHECK(global_error_service_); 249 250 if (!ProfileResetGlobalError::IsSupportedOnPlatform()) 251 return false; 252 253 ProfileResetGlobalError* global_error = new ProfileResetGlobalError(profile_); 254 global_error_service_->AddGlobalError(global_error); 255 256 // Do not try to show bubble if another GlobalError is already showing one. 257 const GlobalErrorService::GlobalErrorList& global_errors( 258 global_error_service_->errors()); 259 GlobalErrorService::GlobalErrorList::const_iterator it; 260 for (it = global_errors.begin(); it != global_errors.end(); ++it) { 261 if ((*it)->GetBubbleView()) 262 break; 263 } 264 if (it == global_errors.end()) { 265 Browser* browser = chrome::FindTabbedBrowser( 266 profile_, 267 false /*match_original_profiles*/, 268 chrome::GetActiveDesktop()); 269 if (browser) 270 global_error->ShowBubbleView(browser); 271 } 272 return true; 273 } 274 275 void AutomaticProfileResetterDelegateImpl::TriggerProfileSettingsReset( 276 bool send_feedback, 277 const base::Closure& completion) { 278 DCHECK(!profile_resetter_); 279 DCHECK(!completion.is_null()); 280 281 profile_resetter_.reset(new ProfileResetter(profile_)); 282 FetchBrandcodedDefaultSettingsIfNeeded(); 283 RequestCallbackWhenBrandcodedDefaultsAreFetched(base::Bind( 284 &AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset, 285 AsWeakPtr(), 286 send_feedback, 287 completion)); 288 } 289 290 void AutomaticProfileResetterDelegateImpl::OnTemplateURLServiceChanged() { 291 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 292 DCHECK(template_url_service_); 293 if (template_url_service_->loaded() && 294 !template_url_service_ready_event_.is_signaled()) 295 template_url_service_ready_event_.Signal(); 296 } 297 298 void AutomaticProfileResetterDelegateImpl::DismissPrompt() { 299 DCHECK(global_error_service_); 300 GlobalError* global_error = 301 global_error_service_->GetGlobalErrorByMenuItemCommandID( 302 IDC_SHOW_SETTINGS_RESET_BUBBLE); 303 if (global_error) { 304 // This will also close/destroy the Bubble UI if it is currently shown. 305 global_error_service_->RemoveGlobalError(global_error); 306 delete global_error; 307 } 308 } 309 310 void AutomaticProfileResetterDelegateImpl::Observe( 311 int type, 312 const content::NotificationSource& source, 313 const content::NotificationDetails& details) { 314 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 315 if (type == chrome::NOTIFICATION_MODULE_LIST_ENUMERATED && 316 !modules_have_been_enumerated_event_.is_signaled()) { 317 #if defined(OS_WIN) 318 module_list_.reset(EnumerateModulesModel::GetInstance()->GetModuleList()); 319 #endif 320 modules_have_been_enumerated_event_.Signal(); 321 } 322 } 323 324 void AutomaticProfileResetterDelegateImpl::SendFeedback( 325 const std::string& report) const { 326 SendSettingsFeedback(report, profile_, PROFILE_RESET_PROMPT); 327 } 328 329 void AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset( 330 bool send_feedback, 331 const base::Closure& completion) { 332 DCHECK(brandcoded_defaults_); 333 scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot( 334 send_feedback ? new ResettableSettingsSnapshot(profile_) : NULL); 335 profile_resetter_->Reset( 336 resettable_aspects_, 337 brandcoded_defaults_.Pass(), 338 base::Bind(&AutomaticProfileResetterDelegateImpl:: 339 OnProfileSettingsResetCompleted, 340 AsWeakPtr(), 341 completion, 342 base::Passed(&old_settings_snapshot))); 343 } 344 345 void AutomaticProfileResetterDelegateImpl:: 346 OnBrandcodedDefaultsFetched() { 347 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 348 DCHECK(brandcoded_config_fetcher_); 349 DCHECK(!brandcoded_config_fetcher_->IsActive()); 350 brandcoded_defaults_ = brandcoded_config_fetcher_->GetSettings(); 351 if (!brandcoded_defaults_) 352 brandcoded_defaults_.reset(new BrandcodedDefaultSettings); 353 brandcoded_defaults_fetched_event_.Signal(); 354 } 355 356 void AutomaticProfileResetterDelegateImpl::OnProfileSettingsResetCompleted( 357 const base::Closure& user_callback, 358 scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot) { 359 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 360 if (old_settings_snapshot) { 361 ResettableSettingsSnapshot new_settings_snapshot(profile_); 362 int difference = 363 old_settings_snapshot->FindDifferentFields(new_settings_snapshot); 364 if (difference) { 365 old_settings_snapshot->Subtract(new_settings_snapshot); 366 std::string report = 367 SerializeSettingsReport(*old_settings_snapshot, difference); 368 SendFeedback(report); 369 } 370 } 371 content::BrowserThread::PostTask( 372 content::BrowserThread::UI, FROM_HERE, user_callback); 373 } 374