root/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. BuildSubTreeFromTemplateURL
  2. ExtractLoadedModuleNameDigests
  3. resettable_aspects_
  4. EnumerateLoadedModulesIfNeeded
  5. RequestCallbackWhenLoadedModulesAreEnumerated
  6. LoadTemplateURLServiceIfNeeded
  7. RequestCallbackWhenTemplateURLServiceIsLoaded
  8. FetchBrandcodedDefaultSettingsIfNeeded
  9. RequestCallbackWhenBrandcodedDefaultsAreFetched
  10. GetLoadedModuleNameDigests
  11. GetDefaultSearchProviderDetails
  12. IsDefaultSearchProviderManaged
  13. GetPrepopulatedSearchProvidersDetails
  14. TriggerPrompt
  15. TriggerProfileSettingsReset
  16. OnTemplateURLServiceChanged
  17. DismissPrompt
  18. Observe
  19. SendFeedback
  20. RunProfileSettingsReset
  21. OnBrandcodedDefaultsFetched
  22. OnProfileSettingsResetCompleted

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"

#include <string>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/md5.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
#include "chrome/browser/profile_resetter/profile_reset_global_error.h"
#include "chrome/browser/profile_resetter/profile_resetter.h"
#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"

#if defined(OS_WIN)
#include "chrome/browser/enumerate_modules_model_win.h"
#endif

namespace {

scoped_ptr<base::DictionaryValue> BuildSubTreeFromTemplateURL(
    const TemplateURL* template_url) {
  scoped_ptr<base::DictionaryValue> tree(new base::DictionaryValue);
  tree->SetString("search_url", template_url->url());
  tree->SetString("search_terms_replacement_key",
                  template_url->search_terms_replacement_key());
  tree->SetString("suggest_url", template_url->suggestions_url());
  tree->SetString("instant_url", template_url->instant_url());
  tree->SetString("image_url", template_url->image_url());
  tree->SetString("new_tab_url", template_url->new_tab_url());
  tree->SetString("search_url_post_params",
                  template_url->search_url_post_params());
  tree->SetString("suggest_url_post_params",
                  template_url->suggestions_url_post_params());
  tree->SetString("instant_url_post_params",
                  template_url->instant_url_post_params());
  tree->SetString("image_url_post_params",
                  template_url->image_url_post_params());
  tree->SetString("icon_url", template_url->favicon_url().spec());
  tree->SetString("name", template_url->short_name());
  tree->SetString("keyword", template_url->keyword());
  base::ListValue* input_encodings = new base::ListValue;
  input_encodings->AppendStrings(template_url->input_encodings());
  tree->Set("encodings", input_encodings);
  tree->SetString("id", base::Int64ToString(template_url->id()));
  tree->SetString("prepopulate_id",
                  base::IntToString(template_url->prepopulate_id()));
  base::ListValue* alternate_urls = new base::ListValue;
  alternate_urls->AppendStrings(template_url->alternate_urls());
  tree->Set("alternate_urls", alternate_urls);
  return tree.Pass();
}

#if defined(OS_WIN)
void ExtractLoadedModuleNameDigests(
    const base::ListValue& module_list,
    base::ListValue* module_name_digests) {
  DCHECK(module_name_digests);

  // EnumerateModulesModel produces a list of dictionaries.
  // Each dictionary corresponds to a module and exposes a number of properties.
  // We care only about 'type' and 'name'.
  for (size_t i = 0; i < module_list.GetSize(); ++i) {
    const base::DictionaryValue* module_dictionary = NULL;
    if (!module_list.GetDictionary(i, &module_dictionary))
      continue;
    ModuleEnumerator::ModuleType module_type =
        ModuleEnumerator::LOADED_MODULE;
    if (!module_dictionary->GetInteger(
            "type", reinterpret_cast<int*>(&module_type)) ||
        module_type != ModuleEnumerator::LOADED_MODULE) {
      continue;
    }
    std::string module_name;
    if (!module_dictionary->GetString("name", &module_name))
      continue;
    StringToLowerASCII(&module_name);
    module_name_digests->AppendString(base::MD5String(module_name));
  }
}
#endif

}  // namespace


// AutomaticProfileResetterDelegateImpl --------------------------------------

AutomaticProfileResetterDelegateImpl::AutomaticProfileResetterDelegateImpl(
    Profile* profile,
    ProfileResetter::ResettableFlags resettable_aspects)
    : profile_(profile),
      global_error_service_(GlobalErrorServiceFactory::GetForProfile(profile_)),
      template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)),
      resettable_aspects_(resettable_aspects) {
  DCHECK(profile_);
  if (template_url_service_) {
    template_url_service_->AddObserver(this);
    // Needed so that |template_url_service_ready_event_| will be signaled even
    // when TemplateURLService had been already initialized before this point.
    OnTemplateURLServiceChanged();
  }

#if defined(OS_WIN)
  module_list_.reset(EnumerateModulesModel::GetInstance()->GetModuleList());
#endif
  if (module_list_) {
    // Having a non-empty module list proves that enumeration had been already
    // performed before this point.
    modules_have_been_enumerated_event_.Signal();
  }
  registrar_.Add(this,
                 chrome::NOTIFICATION_MODULE_LIST_ENUMERATED,
                 content::NotificationService::AllSources());
}

AutomaticProfileResetterDelegateImpl::~AutomaticProfileResetterDelegateImpl() {
  if (template_url_service_)
    template_url_service_->RemoveObserver(this);
}

void AutomaticProfileResetterDelegateImpl::EnumerateLoadedModulesIfNeeded() {
  if (!modules_have_been_enumerated_event_.is_signaled()) {
#if defined(OS_WIN)
    EnumerateModulesModel::GetInstance()->ScanNow();
#else
    modules_have_been_enumerated_event_.Signal();
#endif
  }
}

void AutomaticProfileResetterDelegateImpl::
    RequestCallbackWhenLoadedModulesAreEnumerated(
    const base::Closure& ready_callback) const {
  DCHECK(!ready_callback.is_null());
  modules_have_been_enumerated_event_.Post(FROM_HERE, ready_callback);
}

void AutomaticProfileResetterDelegateImpl::LoadTemplateURLServiceIfNeeded() {
  DCHECK(template_url_service_);
  template_url_service_->Load();  // Safe to call even if it has loaded already.
}

void AutomaticProfileResetterDelegateImpl::
    RequestCallbackWhenTemplateURLServiceIsLoaded(
    const base::Closure& ready_callback) const {
  DCHECK(!ready_callback.is_null());
  template_url_service_ready_event_.Post(FROM_HERE, ready_callback);
}

void AutomaticProfileResetterDelegateImpl::
    FetchBrandcodedDefaultSettingsIfNeeded() {
  if (brandcoded_config_fetcher_ ||
      brandcoded_defaults_fetched_event_.is_signaled())
    return;

  std::string brandcode;
  google_util::GetBrand(&brandcode);
  if (brandcode.empty()) {
    brandcoded_defaults_.reset(new BrandcodedDefaultSettings);
    brandcoded_defaults_fetched_event_.Signal();
  } else {
    brandcoded_config_fetcher_.reset(new BrandcodeConfigFetcher(
        base::Bind(
            &AutomaticProfileResetterDelegateImpl::OnBrandcodedDefaultsFetched,
            base::Unretained(this)),
        GURL("https://tools.google.com/service/update2"),
        brandcode));
  }
}

void AutomaticProfileResetterDelegateImpl::
    RequestCallbackWhenBrandcodedDefaultsAreFetched(
    const base::Closure& ready_callback) const {
  DCHECK(!ready_callback.is_null());
  brandcoded_defaults_fetched_event_.Post(FROM_HERE, ready_callback);
}

scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl::
    GetLoadedModuleNameDigests() const {
  DCHECK(modules_have_been_enumerated_event_.is_signaled());
  scoped_ptr<base::ListValue> result(new base::ListValue);
#if defined(OS_WIN)
  if (module_list_)
    ExtractLoadedModuleNameDigests(*module_list_, result.get());
#endif
  return result.Pass();
}

scoped_ptr<base::DictionaryValue> AutomaticProfileResetterDelegateImpl::
    GetDefaultSearchProviderDetails() const {
  DCHECK(template_url_service_);
  DCHECK(template_url_service_->loaded());

  const TemplateURL* default_search_provider =
      template_url_service_->GetDefaultSearchProvider();

  // Having a NULL default search provider is due to either:
  //  1.) default search providers being disabled by policy,
  //  2.) directly tampering with the Preferences and/or the SQLite DBs.
  // In this state, Omnibox non-keyword search functionality is disabled.
  return default_search_provider ?
      BuildSubTreeFromTemplateURL(default_search_provider) :
      scoped_ptr<base::DictionaryValue>(new base::DictionaryValue);
}

bool AutomaticProfileResetterDelegateImpl::
    IsDefaultSearchProviderManaged() const {
  DCHECK(template_url_service_);
  DCHECK(template_url_service_->loaded());
  return template_url_service_->is_default_search_managed();
}

scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl::
    GetPrepopulatedSearchProvidersDetails() const {
  size_t default_search_index = 0;
  ScopedVector<TemplateURL> engines(
      TemplateURLPrepopulateData::GetPrepopulatedEngines(
          template_url_service_->profile(), &default_search_index));
  scoped_ptr<base::ListValue> engines_details_list(new base::ListValue);
  for (ScopedVector<TemplateURL>::const_iterator it = engines.begin();
       it != engines.end(); ++it)
    engines_details_list->Append(BuildSubTreeFromTemplateURL(*it).release());
  return engines_details_list.Pass();
}

bool AutomaticProfileResetterDelegateImpl::TriggerPrompt() {
  DCHECK(global_error_service_);

  if (!ProfileResetGlobalError::IsSupportedOnPlatform())
    return false;

  ProfileResetGlobalError* global_error = new ProfileResetGlobalError(profile_);
  global_error_service_->AddGlobalError(global_error);

  // Do not try to show bubble if another GlobalError is already showing one.
  const GlobalErrorService::GlobalErrorList& global_errors(
      global_error_service_->errors());
  GlobalErrorService::GlobalErrorList::const_iterator it;
  for (it = global_errors.begin(); it != global_errors.end(); ++it) {
    if ((*it)->GetBubbleView())
      break;
  }
  if (it == global_errors.end()) {
    Browser* browser = chrome::FindTabbedBrowser(
        profile_,
        false /*match_original_profiles*/,
        chrome::GetActiveDesktop());
    if (browser)
      global_error->ShowBubbleView(browser);
  }
  return true;
}

void AutomaticProfileResetterDelegateImpl::TriggerProfileSettingsReset(
    bool send_feedback,
    const base::Closure& completion) {
  DCHECK(!profile_resetter_);
  DCHECK(!completion.is_null());

  profile_resetter_.reset(new ProfileResetter(profile_));
  FetchBrandcodedDefaultSettingsIfNeeded();
  RequestCallbackWhenBrandcodedDefaultsAreFetched(base::Bind(
      &AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset,
      AsWeakPtr(),
      send_feedback,
      completion));
}

void AutomaticProfileResetterDelegateImpl::OnTemplateURLServiceChanged() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK(template_url_service_);
  if (template_url_service_->loaded() &&
      !template_url_service_ready_event_.is_signaled())
    template_url_service_ready_event_.Signal();
}

void AutomaticProfileResetterDelegateImpl::DismissPrompt() {
  DCHECK(global_error_service_);
  GlobalError* global_error =
      global_error_service_->GetGlobalErrorByMenuItemCommandID(
          IDC_SHOW_SETTINGS_RESET_BUBBLE);
  if (global_error) {
    // This will also close/destroy the Bubble UI if it is currently shown.
    global_error_service_->RemoveGlobalError(global_error);
    delete global_error;
  }
}

void AutomaticProfileResetterDelegateImpl::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  if (type == chrome::NOTIFICATION_MODULE_LIST_ENUMERATED &&
      !modules_have_been_enumerated_event_.is_signaled()) {
#if defined(OS_WIN)
    module_list_.reset(EnumerateModulesModel::GetInstance()->GetModuleList());
#endif
    modules_have_been_enumerated_event_.Signal();
  }
}

void AutomaticProfileResetterDelegateImpl::SendFeedback(
    const std::string& report) const {
  SendSettingsFeedback(report, profile_, PROFILE_RESET_PROMPT);
}

void AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset(
    bool send_feedback,
    const base::Closure& completion) {
  DCHECK(brandcoded_defaults_);
  scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot;
  if (send_feedback) {
    old_settings_snapshot.reset(new ResettableSettingsSnapshot(profile_));
    old_settings_snapshot->RequestShortcuts(base::Closure());
  }
  profile_resetter_->Reset(
      resettable_aspects_,
      brandcoded_defaults_.Pass(),
      base::Bind(&AutomaticProfileResetterDelegateImpl::
                     OnProfileSettingsResetCompleted,
                 AsWeakPtr(),
                 completion,
                 base::Passed(&old_settings_snapshot)));
}

void AutomaticProfileResetterDelegateImpl::
    OnBrandcodedDefaultsFetched() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK(brandcoded_config_fetcher_);
  DCHECK(!brandcoded_config_fetcher_->IsActive());
  brandcoded_defaults_ = brandcoded_config_fetcher_->GetSettings();
  if (!brandcoded_defaults_)
    brandcoded_defaults_.reset(new BrandcodedDefaultSettings);
  brandcoded_defaults_fetched_event_.Signal();
}

void AutomaticProfileResetterDelegateImpl::OnProfileSettingsResetCompleted(
    const base::Closure& user_callback,
    scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  if (old_settings_snapshot) {
    ResettableSettingsSnapshot new_settings_snapshot(profile_);
    int difference =
        old_settings_snapshot->FindDifferentFields(new_settings_snapshot);
    if (difference) {
      old_settings_snapshot->Subtract(new_settings_snapshot);
      std::string report =
          SerializeSettingsReport(*old_settings_snapshot, difference);
      SendFeedback(report);
    }
  }
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE, user_callback);
}

/* [<][>][^][v][top][bottom][index][help] */