• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ShouldPerformDryRun()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.
ShouldPerformLiveRun()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.
GetProgramAndHashSeedOverridesFromExperiment(std::string * program,std::string * hash_seed)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.
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)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.
InputBuilder(Profile * profile,AutomaticProfileResetterDelegate * delegate)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_) {}
~InputBuilder()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|.
BuildEvaluatorProgramInput(const ProgramInputCallback & 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).
IncludeMementoValues()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.
IncludeFileBasedMementoCallback(const std::string & memento_value)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.
IncludeUserPreferences()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.
IncludeUserPreferencesPartTwo(scoped_ptr<base::DictionaryValue> pref_name_to_value_map)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.
IncludeLocalState()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.
IncludeSearchEngines()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.
IncludeLoadedModules()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.
AddAsyncTask(const base::Closure & task)295   void AddAsyncTask(const base::Closure& task) {
296     task_queue_.push(task);
297   }
298 
299   // Convenience wrapper for synchronous tasks.
SynchronousTaskWrapper(const base::Closure & task)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.
AddTask(const base::Closure & task)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.
PostNextTask()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 {
EvaluationResultsAutomaticProfileResetter::EvaluationResults353   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 
AutomaticProfileResetter(Profile * profile)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 
~AutomaticProfileResetter()383 AutomaticProfileResetter::~AutomaticProfileResetter() {}
384 
Initialize()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 
Activate()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 
TriggerProfileReset(bool send_feedback)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 
SkipProfileReset()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 
IsResetPromptFlowActive() const463 bool AutomaticProfileResetter::IsResetPromptFlowActive() const {
464   return state_ == STATE_HAS_TRIGGERED_PROMPT ||
465       state_ == STATE_HAS_SHOWN_BUBBLE;
466 }
467 
ShouldShowResetBanner() const468 bool AutomaticProfileResetter::ShouldShowResetBanner() const {
469   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
470   return should_show_reset_banner_ && ShouldPerformLiveRun();
471 }
472 
NotifyDidShowResetBubble()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 
NotifyDidOpenWebUIResetDialog()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 
NotifyDidCloseWebUIResetDialog(bool performed_reset)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 
NotifyDidCloseWebUIResetBanner()519 void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() {
520   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
521   should_show_reset_banner_ = false;
522 }
523 
SetProgramForTesting(const std::string & program)524 void AutomaticProfileResetter::SetProgramForTesting(
525     const std::string& program) {
526   program_ = program;
527 }
528 
SetHashSeedForTesting(const std::string & hash_key)529 void AutomaticProfileResetter::SetHashSeedForTesting(
530     const std::string& hash_key) {
531   hash_seed_ = hash_key;
532 }
533 
SetDelegateForTesting(scoped_ptr<AutomaticProfileResetterDelegate> delegate)534 void AutomaticProfileResetter::SetDelegateForTesting(
535     scoped_ptr<AutomaticProfileResetterDelegate> delegate) {
536   delegate_ = delegate.Pass();
537 }
538 
SetTaskRunnerForWaitingForTesting(const scoped_refptr<base::TaskRunner> & task_runner)539 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
540     const scoped_refptr<base::TaskRunner>& task_runner) {
541   task_runner_for_waiting_ = task_runner;
542 }
543 
Shutdown()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 
PrepareEvaluationFlow()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 
OnTemplateURLServiceIsLoaded()573 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
574   template_url_service_ready_ = true;
575   OnDependencyIsReady();
576 }
577 
OnLoadedModulesAreEnumerated()578 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
579   enumeration_of_loaded_modules_ready_ = true;
580   OnDependencyIsReady();
581 }
582 
OnDependencyIsReady()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 
BeginEvaluationFlow()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 
ContinueWithEvaluationFlow(scoped_ptr<base::DictionaryValue> program_input)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>
EvaluateConditionsOnWorkerPoolThread(const std::string & hash_seed,const std::string & program,scoped_ptr<base::DictionaryValue> program_input)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 
ReportStatistics(uint32 satisfied_criteria_mask,uint32 combined_status_mask)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 
FinishEvaluationFlow(scoped_ptr<EvaluationResults> results)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 
BeginResetPromptFlow()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 
OnProfileSettingsResetCompleted()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 
ReportPromptResult(PromptResult result)733 void AutomaticProfileResetter::ReportPromptResult(PromptResult result) {
734   UMA_HISTOGRAM_ENUMERATION(
735       "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX);
736 }
737 
PersistMementos()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 
FinishResetPromptFlow()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