root/chrome/browser/profile_resetter/automatic_profile_resetter.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ShouldPerformDryRun
  2. ShouldPerformLiveRun
  3. GetProgramAndHashSeedOverridesFromExperiment
  4. BuildSubTreesFromPreferences
  5. memento_in_file_
  6. InputBuilder
  7. BuildEvaluatorProgramInput
  8. IncludeMementoValues
  9. IncludeFileBasedMementoCallback
  10. IncludeUserPreferences
  11. IncludeUserPreferencesPartTwo
  12. IncludeLocalState
  13. IncludeSearchEngines
  14. IncludeLoadedModules
  15. AddAsyncTask
  16. SynchronousTaskWrapper
  17. AddTask
  18. PostNextTask
  19. weak_ptr_factory_
  20. Initialize
  21. Activate
  22. TriggerProfileReset
  23. SkipProfileReset
  24. IsResetPromptFlowActive
  25. ShouldShowResetBanner
  26. NotifyDidShowResetBubble
  27. NotifyDidOpenWebUIResetDialog
  28. NotifyDidCloseWebUIResetDialog
  29. NotifyDidCloseWebUIResetBanner
  30. SetProgramForTesting
  31. SetHashSeedForTesting
  32. SetDelegateForTesting
  33. SetTaskRunnerForWaitingForTesting
  34. Shutdown
  35. PrepareEvaluationFlow
  36. OnTemplateURLServiceIsLoaded
  37. OnLoadedModulesAreEnumerated
  38. OnDependencyIsReady
  39. BeginEvaluationFlow
  40. ContinueWithEvaluationFlow
  41. EvaluateConditionsOnWorkerPoolThread
  42. ReportStatistics
  43. FinishEvaluationFlow
  44. BeginResetPromptFlow
  45. OnProfileSettingsResetCompleted
  46. ReportPromptResult
  47. PersistMementos
  48. FinishResetPromptFlow

// 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.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"
#include "chrome/browser/profile_resetter/jtl_interpreter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
#include "grit/browser_resources.h"
#include "ui/base/resource/resource_bundle.h"


// Helpers -------------------------------------------------------------------

namespace {

// Name constants for the field trial behind which we enable this feature.
const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset";
const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun";
const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled";
#if defined(GOOGLE_CHROME_BUILD)
const char kAutomaticProfileResetStudyProgramParameterName[] = "program";
const char kAutomaticProfileResetStudyHashSeedParameterName[] = "hash_seed";
#endif

// How long to wait after start-up before unleashing the evaluation flow.
const int64 kEvaluationFlowDelayInSeconds = 55;

// Keys used in the input dictionary of the program.
const char kDefaultSearchProviderKey[] = "default_search_provider";
const char kDefaultSearchProviderIsUserControlledKey[] =
    "default_search_provider_iuc";
const char kLoadedModuleDigestsKey[] = "loaded_modules";
const char kLocalStateKey[] = "local_state";
const char kLocalStateIsUserControlledKey[] = "local_state_iuc";
const char kSearchProvidersKey[] = "search_providers";
const char kUserPreferencesKey[] = "preferences";
const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc";

// Keys used in the output dictionary of the program.
const char kCombinedStatusMaskKeyPrefix[] = "combined_status_mask_bit";
const char kHadPromptedAlreadyKey[] = "had_prompted_already";
const char kShouldPromptKey[] = "should_prompt";
const char kSatisfiedCriteriaMaskKeyPrefix[] = "satisfied_criteria_mask_bit";

// Keys used in both the input and output dictionary of the program.
const char kMementoValueInFileKey[] = "memento_value_in_file";
const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state";
const char kMementoValueInPrefsKey[] = "memento_value_in_prefs";

// Number of bits, and maximum value (exclusive) for the mask whose bits
// indicate which of reset criteria were satisfied.
const size_t kSatisfiedCriteriaMaskNumberOfBits = 5u;
const uint32 kSatisfiedCriteriaMaskMaximumValue =
    (1u << kSatisfiedCriteriaMaskNumberOfBits);

// Number of bits, and maximum value (exclusive) for the mask whose bits
// indicate if any of reset criteria were satisfied, and which of the mementos
// were already present.
const size_t kCombinedStatusMaskNumberOfBits = 4u;
const uint32 kCombinedStatusMaskMaximumValue =
    (1u << kCombinedStatusMaskNumberOfBits);

// Returns whether or not a dry-run shall be performed.
bool ShouldPerformDryRun() {
  return StartsWithASCII(
      base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
      kAutomaticProfileResetStudyDryRunGroupName, true);
}

// Returns whether or not a live-run shall be performed.
bool ShouldPerformLiveRun() {
  return StartsWithASCII(
      base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
      kAutomaticProfileResetStudyEnabledGroupName, true);
}

// If the currently active experiment group prescribes a |program| and
// |hash_seed| to use instead of the baked-in ones, retrieves those and returns
// true. Otherwise, returns false.
bool GetProgramAndHashSeedOverridesFromExperiment(std::string* program,
                                                  std::string* hash_seed) {
  DCHECK(program);
  DCHECK(hash_seed);
#if defined(GOOGLE_CHROME_BUILD)
  std::map<std::string, std::string> params;
  chrome_variations::GetVariationParams(kAutomaticProfileResetStudyName,
                                        &params);
  if (params.count(kAutomaticProfileResetStudyProgramParameterName) &&
      params.count(kAutomaticProfileResetStudyHashSeedParameterName)) {
    program->swap(params[kAutomaticProfileResetStudyProgramParameterName]);
    hash_seed->swap(params[kAutomaticProfileResetStudyHashSeedParameterName]);
    return true;
  }
#endif
  return false;
}

// Takes |pref_name_to_value_map|, which shall be a deep-copy of all preferences
// in |source| without path expansion; and (1.) creates a sub-tree from it named
// |value_tree_key| in |target_dictionary| with path expansion, and (2.) also
// creates an isomorphic sub-tree under the key |is_user_controlled_tree_key|
// that contains only Boolean values indicating whether or not the corresponding
// preference is coming from the 'user' PrefStore.
void BuildSubTreesFromPreferences(
    scoped_ptr<base::DictionaryValue> pref_name_to_value_map,
    const PrefService* source,
    const char* value_tree_key,
    const char* is_user_controlled_tree_key,
    base::DictionaryValue* target_dictionary) {
  std::vector<std::string> pref_names;
  pref_names.reserve(pref_name_to_value_map->size());
  for (base::DictionaryValue::Iterator it(*pref_name_to_value_map);
       !it.IsAtEnd(); it.Advance())
    pref_names.push_back(it.key());

  base::DictionaryValue* value_tree = new base::DictionaryValue;
  base::DictionaryValue* is_user_controlled_tree = new base::DictionaryValue;
  for (std::vector<std::string>::const_iterator it = pref_names.begin();
       it != pref_names.end(); ++it) {
    scoped_ptr<base::Value> pref_value_owned;
    if (pref_name_to_value_map->RemoveWithoutPathExpansion(*it,
                                                           &pref_value_owned)) {
      value_tree->Set(*it, pref_value_owned.release());
      const PrefService::Preference* pref = source->FindPreference(it->c_str());
      is_user_controlled_tree->Set(
          *it, new base::FundamentalValue(pref->IsUserControlled()));
    }
  }
  target_dictionary->Set(value_tree_key, value_tree);
  target_dictionary->Set(is_user_controlled_tree_key, is_user_controlled_tree);
}

}  // namespace


// AutomaticProfileResetter::InputBuilder ------------------------------------

// Collects all the information that is required by the evaluator program to
// assess whether or not the conditions for showing the reset prompt are met.
//
// This necessitates a lot of work that has to be performed on the UI thread,
// such as: accessing the Preferences, Local State, and TemplateURLService.
// In order to keep the browser responsive, the UI thread shall not be blocked
// for long consecutive periods of time. Unfortunately, we cannot reduce the
// total amount of work. Instead, what this class does is to split the work into
// shorter tasks that are posted one-at-a-time to the UI thread in a serial
// fashion, so as to give a chance to run other tasks that have accumulated in
// the meantime.
class AutomaticProfileResetter::InputBuilder
    : public base::SupportsWeakPtr<InputBuilder> {
 public:
  typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)>
      ProgramInputCallback;

  // The dependencies must have been initialized through |delegate|, i.e. the
  // RequestCallback[...] methods must have already fired before calling this.
  InputBuilder(Profile* profile, AutomaticProfileResetterDelegate* delegate)
      : profile_(profile),
        delegate_(delegate),
        memento_in_prefs_(profile_),
        memento_in_local_state_(profile_),
        memento_in_file_(profile_) {}
  ~InputBuilder() {}

  // Assembles the data required by the evaluator program into a dictionary
  // format, and posts it back to the UI thread with |callback| once ready. In
  // order not to block the UI thread for long consecutive periods of time, the
  // work is divided into smaller tasks, see class comment above for details.
  // It is safe to destroy |this| immediately from within the |callback|.
  void BuildEvaluatorProgramInput(const ProgramInputCallback& callback) {
    DCHECK(!data_);
    DCHECK(!callback.is_null());
    data_.reset(new base::DictionaryValue);
    callback_ = callback;

    AddAsyncTask(base::Bind(&InputBuilder::IncludeMementoValues, AsWeakPtr()));
    AddTask(base::Bind(&InputBuilder::IncludeUserPreferences, AsWeakPtr()));
    AddTask(base::Bind(&InputBuilder::IncludeLocalState, AsWeakPtr()));
    AddTask(base::Bind(&InputBuilder::IncludeSearchEngines, AsWeakPtr()));
    AddTask(base::Bind(&InputBuilder::IncludeLoadedModules, AsWeakPtr()));

    // Each task will post the next one. Just trigger the chain reaction.
    PostNextTask();
  }

 private:
  // Asynchronous task that includes memento values (or empty strings in case
  // mementos are not there).
  void IncludeMementoValues() {
    data_->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue());
    data_->SetString(kMementoValueInLocalStateKey,
                     memento_in_local_state_.ReadValue());
    memento_in_file_.ReadValue(base::Bind(
        &InputBuilder::IncludeFileBasedMementoCallback, AsWeakPtr()));
  }

  // Called back by |memento_in_file_| once the |memento_value| has been read.
  void IncludeFileBasedMementoCallback(const std::string& memento_value) {
    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    data_->SetString(kMementoValueInFileKey, memento_value);
    // As an asynchronous task, we need to take care of posting the next task.
    PostNextTask();
  }

  // Task that includes all user (i.e. profile-specific) preferences, along with
  // information about whether the value is coming from the 'user' PrefStore.
  // This is the most expensive operation, so it is itself split into two parts.
  void IncludeUserPreferences() {
    PrefService* prefs = profile_->GetPrefs();
    DCHECK(prefs);
    scoped_ptr<base::DictionaryValue> pref_name_to_value_map(
        prefs->GetPreferenceValuesWithoutPathExpansion());
    AddTask(base::Bind(&InputBuilder::IncludeUserPreferencesPartTwo,
                       AsWeakPtr(),
                       base::Passed(&pref_name_to_value_map)));
  }

  // Second part to above.
  void IncludeUserPreferencesPartTwo(
      scoped_ptr<base::DictionaryValue> pref_name_to_value_map) {
    PrefService* prefs = profile_->GetPrefs();
    DCHECK(prefs);
    BuildSubTreesFromPreferences(
        pref_name_to_value_map.Pass(),
        prefs,
        kUserPreferencesKey,
        kUserPreferencesIsUserControlledKey,
        data_.get());
  }

  // Task that includes all local state (i.e. shared) preferences, along with
  // information about whether the value is coming from the 'user' PrefStore.
  void IncludeLocalState() {
    PrefService* local_state = g_browser_process->local_state();
    DCHECK(local_state);
    scoped_ptr<base::DictionaryValue> pref_name_to_value_map(
        local_state->GetPreferenceValuesWithoutPathExpansion());
    BuildSubTreesFromPreferences(
        pref_name_to_value_map.Pass(),
        local_state,
        kLocalStateKey,
        kLocalStateIsUserControlledKey,
        data_.get());
  }

  // Task that includes all information related to search engines.
  void IncludeSearchEngines() {
    scoped_ptr<base::DictionaryValue> default_search_provider_details(
        delegate_->GetDefaultSearchProviderDetails());
    data_->Set(kDefaultSearchProviderKey,
               default_search_provider_details.release());

    scoped_ptr<base::ListValue> search_providers_details(
        delegate_->GetPrepopulatedSearchProvidersDetails());
    data_->Set(kSearchProvidersKey, search_providers_details.release());

    data_->SetBoolean(kDefaultSearchProviderIsUserControlledKey,
                      !delegate_->IsDefaultSearchProviderManaged());
  }

  // Task that includes information about loaded modules.
  void IncludeLoadedModules() {
    scoped_ptr<base::ListValue> loaded_module_digests(
        delegate_->GetLoadedModuleNameDigests());
    data_->Set(kLoadedModuleDigestsKey, loaded_module_digests.release());
  }

  // -------------------------------------------------------------------------

  // Adds a |task| that can do as much asynchronous processing as it wants, but
  // will need to finally call PostNextTask() on the UI thread when done.
  void AddAsyncTask(const base::Closure& task) {
    task_queue_.push(task);
  }

  // Convenience wrapper for synchronous tasks.
  void SynchronousTaskWrapper(const base::Closure& task) {
    base::ElapsedTimer timer;
    task.Run();
    UMA_HISTOGRAM_CUSTOM_TIMES(
        "AutomaticProfileReset.InputBuilder.TaskDuration",
        timer.Elapsed(),
        base::TimeDelta::FromMilliseconds(1),
        base::TimeDelta::FromSeconds(2),
        50);
    PostNextTask();
  }

  // Adds a task that needs to finish synchronously. In exchange, PostNextTask()
  // is called automatically when the |task| returns, and execution time is
  // measured.
  void AddTask(const base::Closure& task) {
    task_queue_.push(
        base::Bind(&InputBuilder::SynchronousTaskWrapper, AsWeakPtr(), task));
  }

  // Posts the next task from the |task_queue_|, unless it is exhausted, in
  // which case it posts |callback_| to return with the results.
  void PostNextTask() {
    base::Closure next_task;
    if (task_queue_.empty()) {
      next_task = base::Bind(callback_, base::Passed(&data_));
    } else {
      next_task = task_queue_.front();
      task_queue_.pop();
    }
    content::BrowserThread::PostTask(
        content::BrowserThread::UI, FROM_HERE, next_task);
  }

  Profile* profile_;
  AutomaticProfileResetterDelegate* delegate_;
  ProgramInputCallback callback_;

  PreferenceHostedPromptMemento memento_in_prefs_;
  LocalStateHostedPromptMemento memento_in_local_state_;
  FileHostedPromptMemento memento_in_file_;

  scoped_ptr<base::DictionaryValue> data_;
  std::queue<base::Closure> task_queue_;

  DISALLOW_COPY_AND_ASSIGN(InputBuilder);
};


// AutomaticProfileResetter::EvaluationResults -------------------------------

// Encapsulates the output values extracted from the evaluator program.
struct AutomaticProfileResetter::EvaluationResults {
  EvaluationResults()
      : should_prompt(false),
        had_prompted_already(false),
        satisfied_criteria_mask(0),
        combined_status_mask(0) {}

  std::string memento_value_in_prefs;
  std::string memento_value_in_local_state;
  std::string memento_value_in_file;

  bool should_prompt;
  bool had_prompted_already;
  uint32 satisfied_criteria_mask;
  uint32 combined_status_mask;
};


// AutomaticProfileResetter --------------------------------------------------

AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile)
    : profile_(profile),
      state_(STATE_UNINITIALIZED),
      enumeration_of_loaded_modules_ready_(false),
      template_url_service_ready_(false),
      has_already_dismissed_prompt_(false),
      should_show_reset_banner_(false),
      weak_ptr_factory_(this) {
  DCHECK(profile_);
}

AutomaticProfileResetter::~AutomaticProfileResetter() {}

void AutomaticProfileResetter::Initialize() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_UNINITIALIZED);

  if (!ShouldPerformDryRun() && !ShouldPerformLiveRun()) {
    state_ = STATE_DISABLED;
    return;
  }

  if (!GetProgramAndHashSeedOverridesFromExperiment(&program_, &hash_seed_)) {
    ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance());
    if (ShouldPerformLiveRun()) {
      program_ = resources.GetRawDataResource(
          IDR_AUTOMATIC_PROFILE_RESET_RULES).as_string();
      hash_seed_ = resources.GetRawDataResource(
          IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED).as_string();
    } else {  // ShouldPerformDryRun()
      program_ = resources.GetRawDataResource(
          IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY).as_string();
      hash_seed_ = resources.GetRawDataResource(
          IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY).as_string();
    }
  }

  delegate_.reset(new AutomaticProfileResetterDelegateImpl(
      profile_, ProfileResetter::ALL));
  task_runner_for_waiting_ =
      content::BrowserThread::GetMessageLoopProxyForThread(
          content::BrowserThread::UI);

  state_ = STATE_INITIALIZED;
}

void AutomaticProfileResetter::Activate() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK(state_ == STATE_INITIALIZED || state_ == STATE_DISABLED);

  if (state_ == STATE_INITIALIZED) {
    if (!program_.empty()) {
      // Some steps in the flow (e.g. loaded modules, file-based memento) are
      // IO-intensive, so defer execution until some time later.
      task_runner_for_waiting_->PostDelayedTask(
          FROM_HERE,
          base::Bind(&AutomaticProfileResetter::PrepareEvaluationFlow,
                     weak_ptr_factory_.GetWeakPtr()),
          base::TimeDelta::FromSeconds(kEvaluationFlowDelayInSeconds));
    } else {
      // Terminate early if there is no program included (nor set by tests).
      state_ = STATE_DISABLED;
    }
  }
}

void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);

  state_ = STATE_PERFORMING_RESET;
  should_show_reset_banner_ = false;

  ReportPromptResult(PROMPT_ACTION_RESET);
  delegate_->TriggerProfileSettingsReset(
      send_feedback,
      base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted,
                 weak_ptr_factory_.GetWeakPtr()));
}

void AutomaticProfileResetter::SkipProfileReset() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);

  should_show_reset_banner_ = false;

  ReportPromptResult(PROMPT_ACTION_NO_RESET);
  delegate_->DismissPrompt();
  FinishResetPromptFlow();
}

bool AutomaticProfileResetter::IsResetPromptFlowActive() const {
  return state_ == STATE_HAS_TRIGGERED_PROMPT ||
      state_ == STATE_HAS_SHOWN_BUBBLE;
}

bool AutomaticProfileResetter::ShouldShowResetBanner() const {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  return should_show_reset_banner_ && ShouldPerformLiveRun();
}

void AutomaticProfileResetter::NotifyDidShowResetBubble() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_HAS_TRIGGERED_PROMPT);

  state_ = STATE_HAS_SHOWN_BUBBLE;

  PersistMementos();
  ReportPromptResult(PROMPT_SHOWN_BUBBLE);
}

void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  // This notification is invoked unconditionally by the WebUI, only care about
  // it when the prompt flow is currently active (and not yet resetting).
  if (state_ == STATE_HAS_TRIGGERED_PROMPT ||
      state_ == STATE_HAS_SHOWN_BUBBLE) {
    has_already_dismissed_prompt_ = true;
    delegate_->DismissPrompt();
  }
}

void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog(
    bool performed_reset) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  // This notification is invoked unconditionally by the WebUI, only care about
  // it when the prompt flow is currently active (and not yet resetting).
  if (state_ == STATE_HAS_TRIGGERED_PROMPT ||
      state_ == STATE_HAS_SHOWN_BUBBLE) {
    if (!has_already_dismissed_prompt_)
      delegate_->DismissPrompt();
    if (state_ == STATE_HAS_TRIGGERED_PROMPT) {
      PersistMementos();
      ReportPromptResult(performed_reset ?
          PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET :
          PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET);
    } else {  // if (state_ == STATE_HAS_SHOWN_PROMPT)
      ReportPromptResult(performed_reset ?
          PROMPT_FOLLOWED_BY_WEBUI_RESET :
          PROMPT_FOLLOWED_BY_WEBUI_NO_RESET);
    }
    FinishResetPromptFlow();
  }
}

void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  should_show_reset_banner_ = false;
}

void AutomaticProfileResetter::SetProgramForTesting(
    const std::string& program) {
  program_ = program;
}

void AutomaticProfileResetter::SetHashSeedForTesting(
    const std::string& hash_key) {
  hash_seed_ = hash_key;
}

void AutomaticProfileResetter::SetDelegateForTesting(
    scoped_ptr<AutomaticProfileResetterDelegate> delegate) {
  delegate_ = delegate.Pass();
}

void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
    const scoped_refptr<base::TaskRunner>& task_runner) {
  task_runner_for_waiting_ = task_runner;
}

void AutomaticProfileResetter::Shutdown() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  // We better not do anything substantial at this point. The metrics service
  // has already been shut down; and local state has already been commited to
  // file (in the regular fashion) for the last time.

  state_ = STATE_DISABLED;

  weak_ptr_factory_.InvalidateWeakPtrs();
  delegate_.reset();
}

void AutomaticProfileResetter::PrepareEvaluationFlow() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_INITIALIZED);

  state_ = STATE_WAITING_ON_DEPENDENCIES;

  delegate_->RequestCallbackWhenTemplateURLServiceIsLoaded(
      base::Bind(&AutomaticProfileResetter::OnTemplateURLServiceIsLoaded,
                 weak_ptr_factory_.GetWeakPtr()));
  delegate_->RequestCallbackWhenLoadedModulesAreEnumerated(
      base::Bind(&AutomaticProfileResetter::OnLoadedModulesAreEnumerated,
                 weak_ptr_factory_.GetWeakPtr()));
  delegate_->LoadTemplateURLServiceIfNeeded();
  delegate_->EnumerateLoadedModulesIfNeeded();
}

void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
  template_url_service_ready_ = true;
  OnDependencyIsReady();
}

void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
  enumeration_of_loaded_modules_ready_ = true;
  OnDependencyIsReady();
}

void AutomaticProfileResetter::OnDependencyIsReady() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_WAITING_ON_DEPENDENCIES);

  if (template_url_service_ready_ && enumeration_of_loaded_modules_ready_) {
    state_ = STATE_READY;
    content::BrowserThread::PostTask(
        content::BrowserThread::UI,
        FROM_HERE,
        base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow,
                   weak_ptr_factory_.GetWeakPtr()));
  }
}

void AutomaticProfileResetter::BeginEvaluationFlow() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_READY);
  DCHECK(!program_.empty());
  DCHECK(!input_builder_);

  state_ = STATE_EVALUATING_CONDITIONS;

  input_builder_.reset(new InputBuilder(profile_, delegate_.get()));
  input_builder_->BuildEvaluatorProgramInput(
      base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow,
                 weak_ptr_factory_.GetWeakPtr()));
}

void AutomaticProfileResetter::ContinueWithEvaluationFlow(
    scoped_ptr<base::DictionaryValue> program_input) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);

  input_builder_.reset();

  base::SequencedWorkerPool* blocking_pool =
      content::BrowserThread::GetBlockingPool();
  scoped_refptr<base::TaskRunner> task_runner =
      blocking_pool->GetTaskRunnerWithShutdownBehavior(
          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);

  base::PostTaskAndReplyWithResult(
      task_runner.get(),
      FROM_HERE,
      base::Bind(&EvaluateConditionsOnWorkerPoolThread,
                 hash_seed_,
                 program_,
                 base::Passed(&program_input)),
      base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow,
                 weak_ptr_factory_.GetWeakPtr()));
}

// static
scoped_ptr<AutomaticProfileResetter::EvaluationResults>
    AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread(
    const std::string& hash_seed,
    const std::string& program,
    scoped_ptr<base::DictionaryValue> program_input) {
  JtlInterpreter interpreter(hash_seed, program, program_input.get());
  interpreter.Execute();
  UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult",
                            interpreter.result(),
                            JtlInterpreter::RESULT_MAX);
  UMA_HISTOGRAM_SPARSE_SLOWLY("AutomaticProfileReset.ProgramChecksum",
                              interpreter.CalculateProgramChecksum());

  // In each case below, the respective field in result originally contains the
  // default, so if the getter fails, we still have the correct value there.
  scoped_ptr<EvaluationResults> results(new EvaluationResults);
  interpreter.GetOutputBoolean(kShouldPromptKey, &results->should_prompt);
  interpreter.GetOutputBoolean(kHadPromptedAlreadyKey,
                               &results->had_prompted_already);
  interpreter.GetOutputString(kMementoValueInPrefsKey,
                              &results->memento_value_in_prefs);
  interpreter.GetOutputString(kMementoValueInLocalStateKey,
                              &results->memento_value_in_local_state);
  interpreter.GetOutputString(kMementoValueInFileKey,
                              &results->memento_value_in_file);
  for (size_t i = 0; i < kCombinedStatusMaskNumberOfBits; ++i) {
    bool flag = false;
    std::string mask_i_th_bit_key =
        kCombinedStatusMaskKeyPrefix + base::IntToString(i + 1);
    if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
      results->combined_status_mask |= (1 << i);
  }
  for (size_t i = 0; i < kSatisfiedCriteriaMaskNumberOfBits; ++i) {
    bool flag = false;
    std::string mask_i_th_bit_key =
        kSatisfiedCriteriaMaskKeyPrefix + base::IntToString(i + 1);
    if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
      results->satisfied_criteria_mask |= (1 << i);
  }
  return results.Pass();
}

void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask,
                                                uint32 combined_status_mask) {
  UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask",
                            satisfied_criteria_mask,
                            kSatisfiedCriteriaMaskMaximumValue);
  UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask",
                            combined_status_mask,
                            kCombinedStatusMaskMaximumValue);
}

void AutomaticProfileResetter::FinishEvaluationFlow(
    scoped_ptr<EvaluationResults> results) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);

  ReportStatistics(results->satisfied_criteria_mask,
                   results->combined_status_mask);

  if (results->should_prompt)
    should_show_reset_banner_ = true;

  if (results->should_prompt && !results->had_prompted_already) {
    evaluation_results_ = results.Pass();
    BeginResetPromptFlow();
  } else {
    state_ = STATE_DONE;
  }
}

void AutomaticProfileResetter::BeginResetPromptFlow() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);

  state_ = STATE_HAS_TRIGGERED_PROMPT;

  if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) {
    // Start fetching the brandcoded default settings speculatively in the
    // background, so as to reduce waiting time if the user chooses to go
    // through with the reset.
    delegate_->FetchBrandcodedDefaultSettingsIfNeeded();
  } else {
    PersistMementos();
    ReportPromptResult(PROMPT_NOT_TRIGGERED);
    FinishResetPromptFlow();
  }
}

void AutomaticProfileResetter::OnProfileSettingsResetCompleted() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK_EQ(state_, STATE_PERFORMING_RESET);

  delegate_->DismissPrompt();
  FinishResetPromptFlow();
}

void AutomaticProfileResetter::ReportPromptResult(PromptResult result) {
  UMA_HISTOGRAM_ENUMERATION(
      "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX);
}

void AutomaticProfileResetter::PersistMementos() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
         state_ == STATE_HAS_SHOWN_BUBBLE);
  DCHECK(evaluation_results_);

  PreferenceHostedPromptMemento memento_in_prefs(profile_);
  LocalStateHostedPromptMemento memento_in_local_state(profile_);
  FileHostedPromptMemento memento_in_file(profile_);

  memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs);
  memento_in_local_state.StoreValue(
      evaluation_results_->memento_value_in_local_state);
  memento_in_file.StoreValue(evaluation_results_->memento_value_in_file);

  evaluation_results_.reset();
}

void AutomaticProfileResetter::FinishResetPromptFlow() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
         state_ == STATE_HAS_SHOWN_BUBBLE ||
         state_ == STATE_PERFORMING_RESET);
  DCHECK(!evaluation_results_);

  state_ = STATE_DONE;
}

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