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