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