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.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/logging.h"
     10 #include "base/memory/ref_counted.h"
     11 #include "base/metrics/field_trial.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/metrics/sparse_histogram.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/task_runner.h"
     18 #include "base/task_runner_util.h"
     19 #include "base/threading/sequenced_worker_pool.h"
     20 #include "base/time/time.h"
     21 #include "base/timer/elapsed_timer.h"
     22 #include "base/values.h"
     23 #include "chrome/browser/browser_process.h"
     24 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"
     25 #include "chrome/browser/profile_resetter/jtl_interpreter.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/browser/search_engines/template_url_service.h"
     28 #include "chrome/browser/search_engines/template_url_service_factory.h"
     29 #include "components/variations/variations_associated_data.h"
     30 #include "content/public/browser/browser_thread.h"
     31 #include "grit/browser_resources.h"
     32 #include "ui/base/resource/resource_bundle.h"
     33 
     34 
     35 // Helpers -------------------------------------------------------------------
     36 
     37 namespace {
     38 
     39 // Name constants for the field trial behind which we enable this feature.
     40 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset";
     41 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun";
     42 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled";
     43 #if defined(GOOGLE_CHROME_BUILD)
     44 const char kAutomaticProfileResetStudyProgramParameterName[] = "program";
     45 const char kAutomaticProfileResetStudyHashSeedParameterName[] = "hash_seed";
     46 #endif
     47 
     48 // How long to wait after start-up before unleashing the evaluation flow.
     49 const int64 kEvaluationFlowDelayInSeconds = 55;
     50 
     51 // Keys used in the input dictionary of the program.
     52 const char kDefaultSearchProviderKey[] = "default_search_provider";
     53 const char kDefaultSearchProviderIsUserControlledKey[] =
     54     "default_search_provider_iuc";
     55 const char kLoadedModuleDigestsKey[] = "loaded_modules";
     56 const char kLocalStateKey[] = "local_state";
     57 const char kLocalStateIsUserControlledKey[] = "local_state_iuc";
     58 const char kSearchProvidersKey[] = "search_providers";
     59 const char kUserPreferencesKey[] = "preferences";
     60 const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc";
     61 
     62 // Keys used in the output dictionary of the program.
     63 const char kCombinedStatusMaskKeyPrefix[] = "combined_status_mask_bit";
     64 const char kHadPromptedAlreadyKey[] = "had_prompted_already";
     65 const char kShouldPromptKey[] = "should_prompt";
     66 const char kSatisfiedCriteriaMaskKeyPrefix[] = "satisfied_criteria_mask_bit";
     67 
     68 // Keys used in both the input and output dictionary of the program.
     69 const char kMementoValueInFileKey[] = "memento_value_in_file";
     70 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state";
     71 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs";
     72 
     73 // Number of bits, and maximum value (exclusive) for the mask whose bits
     74 // indicate which of reset criteria were satisfied.
     75 const size_t kSatisfiedCriteriaMaskNumberOfBits = 5u;
     76 const uint32 kSatisfiedCriteriaMaskMaximumValue =
     77     (1u << kSatisfiedCriteriaMaskNumberOfBits);
     78 
     79 // Number of bits, and maximum value (exclusive) for the mask whose bits
     80 // indicate if any of reset criteria were satisfied, and which of the mementos
     81 // were already present.
     82 const size_t kCombinedStatusMaskNumberOfBits = 4u;
     83 const uint32 kCombinedStatusMaskMaximumValue =
     84     (1u << kCombinedStatusMaskNumberOfBits);
     85 
     86 // Returns whether or not a dry-run shall be performed.
     87 bool ShouldPerformDryRun() {
     88   return StartsWithASCII(
     89       base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
     90       kAutomaticProfileResetStudyDryRunGroupName, true);
     91 }
     92 
     93 // Returns whether or not a live-run shall be performed.
     94 bool ShouldPerformLiveRun() {
     95   return StartsWithASCII(
     96       base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
     97       kAutomaticProfileResetStudyEnabledGroupName, true);
     98 }
     99 
    100 // If the currently active experiment group prescribes a |program| and
    101 // |hash_seed| to use instead of the baked-in ones, retrieves those and returns
    102 // true. Otherwise, returns false.
    103 bool GetProgramAndHashSeedOverridesFromExperiment(std::string* program,
    104                                                   std::string* hash_seed) {
    105   DCHECK(program);
    106   DCHECK(hash_seed);
    107 #if defined(GOOGLE_CHROME_BUILD)
    108   std::map<std::string, std::string> params;
    109   chrome_variations::GetVariationParams(kAutomaticProfileResetStudyName,
    110                                         &params);
    111   if (params.count(kAutomaticProfileResetStudyProgramParameterName) &&
    112       params.count(kAutomaticProfileResetStudyHashSeedParameterName)) {
    113     program->swap(params[kAutomaticProfileResetStudyProgramParameterName]);
    114     hash_seed->swap(params[kAutomaticProfileResetStudyHashSeedParameterName]);
    115     return true;
    116   }
    117 #endif
    118   return false;
    119 }
    120 
    121 // Takes |pref_name_to_value_map|, which shall be a deep-copy of all preferences
    122 // in |source| without path expansion; and (1.) creates a sub-tree from it named
    123 // |value_tree_key| in |target_dictionary| with path expansion, and (2.) also
    124 // creates an isomorphic sub-tree under the key |is_user_controlled_tree_key|
    125 // that contains only Boolean values indicating whether or not the corresponding
    126 // preference is coming from the 'user' PrefStore.
    127 void BuildSubTreesFromPreferences(
    128     scoped_ptr<base::DictionaryValue> pref_name_to_value_map,
    129     const PrefService* source,
    130     const char* value_tree_key,
    131     const char* is_user_controlled_tree_key,
    132     base::DictionaryValue* target_dictionary) {
    133   std::vector<std::string> pref_names;
    134   pref_names.reserve(pref_name_to_value_map->size());
    135   for (base::DictionaryValue::Iterator it(*pref_name_to_value_map);
    136        !it.IsAtEnd(); it.Advance())
    137     pref_names.push_back(it.key());
    138 
    139   base::DictionaryValue* value_tree = new base::DictionaryValue;
    140   base::DictionaryValue* is_user_controlled_tree = new base::DictionaryValue;
    141   for (std::vector<std::string>::const_iterator it = pref_names.begin();
    142        it != pref_names.end(); ++it) {
    143     scoped_ptr<base::Value> pref_value_owned;
    144     if (pref_name_to_value_map->RemoveWithoutPathExpansion(*it,
    145                                                            &pref_value_owned)) {
    146       value_tree->Set(*it, pref_value_owned.release());
    147       const PrefService::Preference* pref = source->FindPreference(it->c_str());
    148       is_user_controlled_tree->Set(
    149           *it, new base::FundamentalValue(pref->IsUserControlled()));
    150     }
    151   }
    152   target_dictionary->Set(value_tree_key, value_tree);
    153   target_dictionary->Set(is_user_controlled_tree_key, is_user_controlled_tree);
    154 }
    155 
    156 }  // namespace
    157 
    158 
    159 // AutomaticProfileResetter::InputBuilder ------------------------------------
    160 
    161 // Collects all the information that is required by the evaluator program to
    162 // assess whether or not the conditions for showing the reset prompt are met.
    163 //
    164 // This necessitates a lot of work that has to be performed on the UI thread,
    165 // such as: accessing the Preferences, Local State, and TemplateURLService.
    166 // In order to keep the browser responsive, the UI thread shall not be blocked
    167 // for long consecutive periods of time. Unfortunately, we cannot reduce the
    168 // total amount of work. Instead, what this class does is to split the work into
    169 // shorter tasks that are posted one-at-a-time to the UI thread in a serial
    170 // fashion, so as to give a chance to run other tasks that have accumulated in
    171 // the meantime.
    172 class AutomaticProfileResetter::InputBuilder
    173     : public base::SupportsWeakPtr<InputBuilder> {
    174  public:
    175   typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)>
    176       ProgramInputCallback;
    177 
    178   // The dependencies must have been initialized through |delegate|, i.e. the
    179   // RequestCallback[...] methods must have already fired before calling this.
    180   InputBuilder(Profile* profile, AutomaticProfileResetterDelegate* delegate)
    181       : profile_(profile),
    182         delegate_(delegate),
    183         memento_in_prefs_(profile_),
    184         memento_in_local_state_(profile_),
    185         memento_in_file_(profile_) {}
    186   ~InputBuilder() {}
    187 
    188   // Assembles the data required by the evaluator program into a dictionary
    189   // format, and posts it back to the UI thread with |callback| once ready. In
    190   // order not to block the UI thread for long consecutive periods of time, the
    191   // work is divided into smaller tasks, see class comment above for details.
    192   // It is safe to destroy |this| immediately from within the |callback|.
    193   void BuildEvaluatorProgramInput(const ProgramInputCallback& callback) {
    194     DCHECK(!data_);
    195     DCHECK(!callback.is_null());
    196     data_.reset(new base::DictionaryValue);
    197     callback_ = callback;
    198 
    199     AddAsyncTask(base::Bind(&InputBuilder::IncludeMementoValues, AsWeakPtr()));
    200     AddTask(base::Bind(&InputBuilder::IncludeUserPreferences, AsWeakPtr()));
    201     AddTask(base::Bind(&InputBuilder::IncludeLocalState, AsWeakPtr()));
    202     AddTask(base::Bind(&InputBuilder::IncludeSearchEngines, AsWeakPtr()));
    203     AddTask(base::Bind(&InputBuilder::IncludeLoadedModules, AsWeakPtr()));
    204 
    205     // Each task will post the next one. Just trigger the chain reaction.
    206     PostNextTask();
    207   }
    208 
    209  private:
    210   // Asynchronous task that includes memento values (or empty strings in case
    211   // mementos are not there).
    212   void IncludeMementoValues() {
    213     data_->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue());
    214     data_->SetString(kMementoValueInLocalStateKey,
    215                      memento_in_local_state_.ReadValue());
    216     memento_in_file_.ReadValue(base::Bind(
    217         &InputBuilder::IncludeFileBasedMementoCallback, AsWeakPtr()));
    218   }
    219 
    220   // Called back by |memento_in_file_| once the |memento_value| has been read.
    221   void IncludeFileBasedMementoCallback(const std::string& memento_value) {
    222     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    223     data_->SetString(kMementoValueInFileKey, memento_value);
    224     // As an asynchronous task, we need to take care of posting the next task.
    225     PostNextTask();
    226   }
    227 
    228   // Task that includes all user (i.e. profile-specific) preferences, along with
    229   // information about whether the value is coming from the 'user' PrefStore.
    230   // This is the most expensive operation, so it is itself split into two parts.
    231   void IncludeUserPreferences() {
    232     PrefService* prefs = profile_->GetPrefs();
    233     DCHECK(prefs);
    234     scoped_ptr<base::DictionaryValue> pref_name_to_value_map(
    235         prefs->GetPreferenceValuesWithoutPathExpansion());
    236     AddTask(base::Bind(&InputBuilder::IncludeUserPreferencesPartTwo,
    237                        AsWeakPtr(),
    238                        base::Passed(&pref_name_to_value_map)));
    239   }
    240 
    241   // Second part to above.
    242   void IncludeUserPreferencesPartTwo(
    243       scoped_ptr<base::DictionaryValue> pref_name_to_value_map) {
    244     PrefService* prefs = profile_->GetPrefs();
    245     DCHECK(prefs);
    246     BuildSubTreesFromPreferences(
    247         pref_name_to_value_map.Pass(),
    248         prefs,
    249         kUserPreferencesKey,
    250         kUserPreferencesIsUserControlledKey,
    251         data_.get());
    252   }
    253 
    254   // Task that includes all local state (i.e. shared) preferences, along with
    255   // information about whether the value is coming from the 'user' PrefStore.
    256   void IncludeLocalState() {
    257     PrefService* local_state = g_browser_process->local_state();
    258     DCHECK(local_state);
    259     scoped_ptr<base::DictionaryValue> pref_name_to_value_map(
    260         local_state->GetPreferenceValuesWithoutPathExpansion());
    261     BuildSubTreesFromPreferences(
    262         pref_name_to_value_map.Pass(),
    263         local_state,
    264         kLocalStateKey,
    265         kLocalStateIsUserControlledKey,
    266         data_.get());
    267   }
    268 
    269   // Task that includes all information related to search engines.
    270   void IncludeSearchEngines() {
    271     scoped_ptr<base::DictionaryValue> default_search_provider_details(
    272         delegate_->GetDefaultSearchProviderDetails());
    273     data_->Set(kDefaultSearchProviderKey,
    274                default_search_provider_details.release());
    275 
    276     scoped_ptr<base::ListValue> search_providers_details(
    277         delegate_->GetPrepopulatedSearchProvidersDetails());
    278     data_->Set(kSearchProvidersKey, search_providers_details.release());
    279 
    280     data_->SetBoolean(kDefaultSearchProviderIsUserControlledKey,
    281                       !delegate_->IsDefaultSearchProviderManaged());
    282   }
    283 
    284   // Task that includes information about loaded modules.
    285   void IncludeLoadedModules() {
    286     scoped_ptr<base::ListValue> loaded_module_digests(
    287         delegate_->GetLoadedModuleNameDigests());
    288     data_->Set(kLoadedModuleDigestsKey, loaded_module_digests.release());
    289   }
    290 
    291   // -------------------------------------------------------------------------
    292 
    293   // Adds a |task| that can do as much asynchronous processing as it wants, but
    294   // will need to finally call PostNextTask() on the UI thread when done.
    295   void AddAsyncTask(const base::Closure& task) {
    296     task_queue_.push(task);
    297   }
    298 
    299   // Convenience wrapper for synchronous tasks.
    300   void SynchronousTaskWrapper(const base::Closure& task) {
    301     base::ElapsedTimer timer;
    302     task.Run();
    303     UMA_HISTOGRAM_CUSTOM_TIMES(
    304         "AutomaticProfileReset.InputBuilder.TaskDuration",
    305         timer.Elapsed(),
    306         base::TimeDelta::FromMilliseconds(1),
    307         base::TimeDelta::FromSeconds(2),
    308         50);
    309     PostNextTask();
    310   }
    311 
    312   // Adds a task that needs to finish synchronously. In exchange, PostNextTask()
    313   // is called automatically when the |task| returns, and execution time is
    314   // measured.
    315   void AddTask(const base::Closure& task) {
    316     task_queue_.push(
    317         base::Bind(&InputBuilder::SynchronousTaskWrapper, AsWeakPtr(), task));
    318   }
    319 
    320   // Posts the next task from the |task_queue_|, unless it is exhausted, in
    321   // which case it posts |callback_| to return with the results.
    322   void PostNextTask() {
    323     base::Closure next_task;
    324     if (task_queue_.empty()) {
    325       next_task = base::Bind(callback_, base::Passed(&data_));
    326     } else {
    327       next_task = task_queue_.front();
    328       task_queue_.pop();
    329     }
    330     content::BrowserThread::PostTask(
    331         content::BrowserThread::UI, FROM_HERE, next_task);
    332   }
    333 
    334   Profile* profile_;
    335   AutomaticProfileResetterDelegate* delegate_;
    336   ProgramInputCallback callback_;
    337 
    338   PreferenceHostedPromptMemento memento_in_prefs_;
    339   LocalStateHostedPromptMemento memento_in_local_state_;
    340   FileHostedPromptMemento memento_in_file_;
    341 
    342   scoped_ptr<base::DictionaryValue> data_;
    343   std::queue<base::Closure> task_queue_;
    344 
    345   DISALLOW_COPY_AND_ASSIGN(InputBuilder);
    346 };
    347 
    348 
    349 // AutomaticProfileResetter::EvaluationResults -------------------------------
    350 
    351 // Encapsulates the output values extracted from the evaluator program.
    352 struct AutomaticProfileResetter::EvaluationResults {
    353   EvaluationResults()
    354       : should_prompt(false),
    355         had_prompted_already(false),
    356         satisfied_criteria_mask(0),
    357         combined_status_mask(0) {}
    358 
    359   std::string memento_value_in_prefs;
    360   std::string memento_value_in_local_state;
    361   std::string memento_value_in_file;
    362 
    363   bool should_prompt;
    364   bool had_prompted_already;
    365   uint32 satisfied_criteria_mask;
    366   uint32 combined_status_mask;
    367 };
    368 
    369 
    370 // AutomaticProfileResetter --------------------------------------------------
    371 
    372 AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile)
    373     : profile_(profile),
    374       state_(STATE_UNINITIALIZED),
    375       enumeration_of_loaded_modules_ready_(false),
    376       template_url_service_ready_(false),
    377       has_already_dismissed_prompt_(false),
    378       should_show_reset_banner_(false),
    379       weak_ptr_factory_(this) {
    380   DCHECK(profile_);
    381 }
    382 
    383 AutomaticProfileResetter::~AutomaticProfileResetter() {}
    384 
    385 void AutomaticProfileResetter::Initialize() {
    386   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    387   DCHECK_EQ(state_, STATE_UNINITIALIZED);
    388 
    389   if (!ShouldPerformDryRun() && !ShouldPerformLiveRun()) {
    390     state_ = STATE_DISABLED;
    391     return;
    392   }
    393 
    394   if (!GetProgramAndHashSeedOverridesFromExperiment(&program_, &hash_seed_)) {
    395     ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance());
    396     if (ShouldPerformLiveRun()) {
    397       program_ = resources.GetRawDataResource(
    398           IDR_AUTOMATIC_PROFILE_RESET_RULES).as_string();
    399       hash_seed_ = resources.GetRawDataResource(
    400           IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED).as_string();
    401     } else {  // ShouldPerformDryRun()
    402       program_ = resources.GetRawDataResource(
    403           IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY).as_string();
    404       hash_seed_ = resources.GetRawDataResource(
    405           IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY).as_string();
    406     }
    407   }
    408 
    409   delegate_.reset(new AutomaticProfileResetterDelegateImpl(
    410       profile_, ProfileResetter::ALL));
    411   task_runner_for_waiting_ =
    412       content::BrowserThread::GetMessageLoopProxyForThread(
    413           content::BrowserThread::UI);
    414 
    415   state_ = STATE_INITIALIZED;
    416 }
    417 
    418 void AutomaticProfileResetter::Activate() {
    419   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    420   DCHECK(state_ == STATE_INITIALIZED || state_ == STATE_DISABLED);
    421 
    422   if (state_ == STATE_INITIALIZED) {
    423     if (!program_.empty()) {
    424       // Some steps in the flow (e.g. loaded modules, file-based memento) are
    425       // IO-intensive, so defer execution until some time later.
    426       task_runner_for_waiting_->PostDelayedTask(
    427           FROM_HERE,
    428           base::Bind(&AutomaticProfileResetter::PrepareEvaluationFlow,
    429                      weak_ptr_factory_.GetWeakPtr()),
    430           base::TimeDelta::FromSeconds(kEvaluationFlowDelayInSeconds));
    431     } else {
    432       // Terminate early if there is no program included (nor set by tests).
    433       state_ = STATE_DISABLED;
    434     }
    435   }
    436 }
    437 
    438 void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback) {
    439   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    440   DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);
    441 
    442   state_ = STATE_PERFORMING_RESET;
    443   should_show_reset_banner_ = false;
    444 
    445   ReportPromptResult(PROMPT_ACTION_RESET);
    446   delegate_->TriggerProfileSettingsReset(
    447       send_feedback,
    448       base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted,
    449                  weak_ptr_factory_.GetWeakPtr()));
    450 }
    451 
    452 void AutomaticProfileResetter::SkipProfileReset() {
    453   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    454   DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);
    455 
    456   should_show_reset_banner_ = false;
    457 
    458   ReportPromptResult(PROMPT_ACTION_NO_RESET);
    459   delegate_->DismissPrompt();
    460   FinishResetPromptFlow();
    461 }
    462 
    463 bool AutomaticProfileResetter::IsResetPromptFlowActive() const {
    464   return state_ == STATE_HAS_TRIGGERED_PROMPT ||
    465       state_ == STATE_HAS_SHOWN_BUBBLE;
    466 }
    467 
    468 bool AutomaticProfileResetter::ShouldShowResetBanner() const {
    469   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    470   return should_show_reset_banner_ && ShouldPerformLiveRun();
    471 }
    472 
    473 void AutomaticProfileResetter::NotifyDidShowResetBubble() {
    474   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    475   DCHECK_EQ(state_, STATE_HAS_TRIGGERED_PROMPT);
    476 
    477   state_ = STATE_HAS_SHOWN_BUBBLE;
    478 
    479   PersistMementos();
    480   ReportPromptResult(PROMPT_SHOWN_BUBBLE);
    481 }
    482 
    483 void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() {
    484   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    485 
    486   // This notification is invoked unconditionally by the WebUI, only care about
    487   // it when the prompt flow is currently active (and not yet resetting).
    488   if (state_ == STATE_HAS_TRIGGERED_PROMPT ||
    489       state_ == STATE_HAS_SHOWN_BUBBLE) {
    490     has_already_dismissed_prompt_ = true;
    491     delegate_->DismissPrompt();
    492   }
    493 }
    494 
    495 void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog(
    496     bool performed_reset) {
    497   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    498 
    499   // This notification is invoked unconditionally by the WebUI, only care about
    500   // it when the prompt flow is currently active (and not yet resetting).
    501   if (state_ == STATE_HAS_TRIGGERED_PROMPT ||
    502       state_ == STATE_HAS_SHOWN_BUBBLE) {
    503     if (!has_already_dismissed_prompt_)
    504       delegate_->DismissPrompt();
    505     if (state_ == STATE_HAS_TRIGGERED_PROMPT) {
    506       PersistMementos();
    507       ReportPromptResult(performed_reset ?
    508           PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET :
    509           PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET);
    510     } else {  // if (state_ == STATE_HAS_SHOWN_PROMPT)
    511       ReportPromptResult(performed_reset ?
    512           PROMPT_FOLLOWED_BY_WEBUI_RESET :
    513           PROMPT_FOLLOWED_BY_WEBUI_NO_RESET);
    514     }
    515     FinishResetPromptFlow();
    516   }
    517 }
    518 
    519 void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() {
    520   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    521   should_show_reset_banner_ = false;
    522 }
    523 
    524 void AutomaticProfileResetter::SetProgramForTesting(
    525     const std::string& program) {
    526   program_ = program;
    527 }
    528 
    529 void AutomaticProfileResetter::SetHashSeedForTesting(
    530     const std::string& hash_key) {
    531   hash_seed_ = hash_key;
    532 }
    533 
    534 void AutomaticProfileResetter::SetDelegateForTesting(
    535     scoped_ptr<AutomaticProfileResetterDelegate> delegate) {
    536   delegate_ = delegate.Pass();
    537 }
    538 
    539 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
    540     const scoped_refptr<base::TaskRunner>& task_runner) {
    541   task_runner_for_waiting_ = task_runner;
    542 }
    543 
    544 void AutomaticProfileResetter::Shutdown() {
    545   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    546 
    547   // We better not do anything substantial at this point. The metrics service
    548   // has already been shut down; and local state has already been commited to
    549   // file (in the regular fashion) for the last time.
    550 
    551   state_ = STATE_DISABLED;
    552 
    553   weak_ptr_factory_.InvalidateWeakPtrs();
    554   delegate_.reset();
    555 }
    556 
    557 void AutomaticProfileResetter::PrepareEvaluationFlow() {
    558   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    559   DCHECK_EQ(state_, STATE_INITIALIZED);
    560 
    561   state_ = STATE_WAITING_ON_DEPENDENCIES;
    562 
    563   delegate_->RequestCallbackWhenTemplateURLServiceIsLoaded(
    564       base::Bind(&AutomaticProfileResetter::OnTemplateURLServiceIsLoaded,
    565                  weak_ptr_factory_.GetWeakPtr()));
    566   delegate_->RequestCallbackWhenLoadedModulesAreEnumerated(
    567       base::Bind(&AutomaticProfileResetter::OnLoadedModulesAreEnumerated,
    568                  weak_ptr_factory_.GetWeakPtr()));
    569   delegate_->LoadTemplateURLServiceIfNeeded();
    570   delegate_->EnumerateLoadedModulesIfNeeded();
    571 }
    572 
    573 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
    574   template_url_service_ready_ = true;
    575   OnDependencyIsReady();
    576 }
    577 
    578 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
    579   enumeration_of_loaded_modules_ready_ = true;
    580   OnDependencyIsReady();
    581 }
    582 
    583 void AutomaticProfileResetter::OnDependencyIsReady() {
    584   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    585   DCHECK_EQ(state_, STATE_WAITING_ON_DEPENDENCIES);
    586 
    587   if (template_url_service_ready_ && enumeration_of_loaded_modules_ready_) {
    588     state_ = STATE_READY;
    589     content::BrowserThread::PostTask(
    590         content::BrowserThread::UI,
    591         FROM_HERE,
    592         base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow,
    593                    weak_ptr_factory_.GetWeakPtr()));
    594   }
    595 }
    596 
    597 void AutomaticProfileResetter::BeginEvaluationFlow() {
    598   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    599   DCHECK_EQ(state_, STATE_READY);
    600   DCHECK(!program_.empty());
    601   DCHECK(!input_builder_);
    602 
    603   state_ = STATE_EVALUATING_CONDITIONS;
    604 
    605   input_builder_.reset(new InputBuilder(profile_, delegate_.get()));
    606   input_builder_->BuildEvaluatorProgramInput(
    607       base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow,
    608                  weak_ptr_factory_.GetWeakPtr()));
    609 }
    610 
    611 void AutomaticProfileResetter::ContinueWithEvaluationFlow(
    612     scoped_ptr<base::DictionaryValue> program_input) {
    613   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    614   DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);
    615 
    616   input_builder_.reset();
    617 
    618   base::SequencedWorkerPool* blocking_pool =
    619       content::BrowserThread::GetBlockingPool();
    620   scoped_refptr<base::TaskRunner> task_runner =
    621       blocking_pool->GetTaskRunnerWithShutdownBehavior(
    622           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
    623 
    624   base::PostTaskAndReplyWithResult(
    625       task_runner.get(),
    626       FROM_HERE,
    627       base::Bind(&EvaluateConditionsOnWorkerPoolThread,
    628                  hash_seed_,
    629                  program_,
    630                  base::Passed(&program_input)),
    631       base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow,
    632                  weak_ptr_factory_.GetWeakPtr()));
    633 }
    634 
    635 // static
    636 scoped_ptr<AutomaticProfileResetter::EvaluationResults>
    637     AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread(
    638     const std::string& hash_seed,
    639     const std::string& program,
    640     scoped_ptr<base::DictionaryValue> program_input) {
    641   JtlInterpreter interpreter(hash_seed, program, program_input.get());
    642   interpreter.Execute();
    643   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult",
    644                             interpreter.result(),
    645                             JtlInterpreter::RESULT_MAX);
    646   UMA_HISTOGRAM_SPARSE_SLOWLY("AutomaticProfileReset.ProgramChecksum",
    647                               interpreter.CalculateProgramChecksum());
    648 
    649   // In each case below, the respective field in result originally contains the
    650   // default, so if the getter fails, we still have the correct value there.
    651   scoped_ptr<EvaluationResults> results(new EvaluationResults);
    652   interpreter.GetOutputBoolean(kShouldPromptKey, &results->should_prompt);
    653   interpreter.GetOutputBoolean(kHadPromptedAlreadyKey,
    654                                &results->had_prompted_already);
    655   interpreter.GetOutputString(kMementoValueInPrefsKey,
    656                               &results->memento_value_in_prefs);
    657   interpreter.GetOutputString(kMementoValueInLocalStateKey,
    658                               &results->memento_value_in_local_state);
    659   interpreter.GetOutputString(kMementoValueInFileKey,
    660                               &results->memento_value_in_file);
    661   for (size_t i = 0; i < kCombinedStatusMaskNumberOfBits; ++i) {
    662     bool flag = false;
    663     std::string mask_i_th_bit_key =
    664         kCombinedStatusMaskKeyPrefix + base::IntToString(i + 1);
    665     if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
    666       results->combined_status_mask |= (1 << i);
    667   }
    668   for (size_t i = 0; i < kSatisfiedCriteriaMaskNumberOfBits; ++i) {
    669     bool flag = false;
    670     std::string mask_i_th_bit_key =
    671         kSatisfiedCriteriaMaskKeyPrefix + base::IntToString(i + 1);
    672     if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
    673       results->satisfied_criteria_mask |= (1 << i);
    674   }
    675   return results.Pass();
    676 }
    677 
    678 void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask,
    679                                                 uint32 combined_status_mask) {
    680   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask",
    681                             satisfied_criteria_mask,
    682                             kSatisfiedCriteriaMaskMaximumValue);
    683   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask",
    684                             combined_status_mask,
    685                             kCombinedStatusMaskMaximumValue);
    686 }
    687 
    688 void AutomaticProfileResetter::FinishEvaluationFlow(
    689     scoped_ptr<EvaluationResults> results) {
    690   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    691   DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);
    692 
    693   ReportStatistics(results->satisfied_criteria_mask,
    694                    results->combined_status_mask);
    695 
    696   if (results->should_prompt)
    697     should_show_reset_banner_ = true;
    698 
    699   if (results->should_prompt && !results->had_prompted_already) {
    700     evaluation_results_ = results.Pass();
    701     BeginResetPromptFlow();
    702   } else {
    703     state_ = STATE_DONE;
    704   }
    705 }
    706 
    707 void AutomaticProfileResetter::BeginResetPromptFlow() {
    708   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    709   DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);
    710 
    711   state_ = STATE_HAS_TRIGGERED_PROMPT;
    712 
    713   if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) {
    714     // Start fetching the brandcoded default settings speculatively in the
    715     // background, so as to reduce waiting time if the user chooses to go
    716     // through with the reset.
    717     delegate_->FetchBrandcodedDefaultSettingsIfNeeded();
    718   } else {
    719     PersistMementos();
    720     ReportPromptResult(PROMPT_NOT_TRIGGERED);
    721     FinishResetPromptFlow();
    722   }
    723 }
    724 
    725 void AutomaticProfileResetter::OnProfileSettingsResetCompleted() {
    726   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    727   DCHECK_EQ(state_, STATE_PERFORMING_RESET);
    728 
    729   delegate_->DismissPrompt();
    730   FinishResetPromptFlow();
    731 }
    732 
    733 void AutomaticProfileResetter::ReportPromptResult(PromptResult result) {
    734   UMA_HISTOGRAM_ENUMERATION(
    735       "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX);
    736 }
    737 
    738 void AutomaticProfileResetter::PersistMementos() {
    739   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    740   DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
    741          state_ == STATE_HAS_SHOWN_BUBBLE);
    742   DCHECK(evaluation_results_);
    743 
    744   PreferenceHostedPromptMemento memento_in_prefs(profile_);
    745   LocalStateHostedPromptMemento memento_in_local_state(profile_);
    746   FileHostedPromptMemento memento_in_file(profile_);
    747 
    748   memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs);
    749   memento_in_local_state.StoreValue(
    750       evaluation_results_->memento_value_in_local_state);
    751   memento_in_file.StoreValue(evaluation_results_->memento_value_in_file);
    752 
    753   evaluation_results_.reset();
    754 }
    755 
    756 void AutomaticProfileResetter::FinishResetPromptFlow() {
    757   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    758   DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
    759          state_ == STATE_HAS_SHOWN_BUBBLE ||
    760          state_ == STATE_PERFORMING_RESET);
    761   DCHECK(!evaluation_results_);
    762 
    763   state_ = STATE_DONE;
    764 }
    765