Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / profile_resetter / automatic_profile_resetter.cc
blob4af4cd63bf6a5e8f64962374dd61b201b1b8233a
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.
5 #include "chrome/browser/profile_resetter/automatic_profile_resetter.h"
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/prefs/pref_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/task_runner.h"
17 #include "base/task_runner_util.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/time/time.h"
20 #include "base/timer/elapsed_timer.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"
24 #include "chrome/browser/profile_resetter/jtl_interpreter.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/search_engines/template_url_service.h"
27 #include "chrome/browser/search_engines/template_url_service_factory.h"
28 #include "components/variations/variations_associated_data.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "grit/browser_resources.h"
31 #include "ui/base/resource/resource_bundle.h"
34 // Helpers -------------------------------------------------------------------
36 namespace {
38 // Name constants for the field trial behind which we enable this feature.
39 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset";
40 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun";
41 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled";
42 #if defined(GOOGLE_CHROME_BUILD)
43 const char kAutomaticProfileResetStudyProgramParameterName[] = "program";
44 const char kAutomaticProfileResetStudyHashSeedParameterName[] = "hash_seed";
45 #endif
47 // How long to wait after start-up before unleashing the evaluation flow.
48 const int64 kEvaluationFlowDelayInSeconds = 55;
50 // Keys used in the input dictionary of the program.
51 const char kDefaultSearchProviderKey[] = "default_search_provider";
52 const char kDefaultSearchProviderIsUserControlledKey[] =
53 "default_search_provider_iuc";
54 const char kLoadedModuleDigestsKey[] = "loaded_modules";
55 const char kLocalStateKey[] = "local_state";
56 const char kLocalStateIsUserControlledKey[] = "local_state_iuc";
57 const char kSearchProvidersKey[] = "search_providers";
58 const char kUserPreferencesKey[] = "preferences";
59 const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc";
61 // Keys used in the output dictionary of the program.
62 const char kCombinedStatusMaskKeyPrefix[] = "combined_status_mask_bit";
63 const char kHadPromptedAlreadyKey[] = "had_prompted_already";
64 const char kShouldPromptKey[] = "should_prompt";
65 const char kSatisfiedCriteriaMaskKeyPrefix[] = "satisfied_criteria_mask_bit";
67 // Keys used in both the input and output dictionary of the program.
68 const char kMementoValueInFileKey[] = "memento_value_in_file";
69 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state";
70 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs";
72 // Number of bits, and maximum value (exclusive) for the mask whose bits
73 // indicate which of reset criteria were satisfied.
74 const size_t kSatisfiedCriteriaMaskNumberOfBits = 5u;
75 const uint32 kSatisfiedCriteriaMaskMaximumValue =
76 (1u << kSatisfiedCriteriaMaskNumberOfBits);
78 // Number of bits, and maximum value (exclusive) for the mask whose bits
79 // indicate if any of reset criteria were satisfied, and which of the mementos
80 // were already present.
81 const size_t kCombinedStatusMaskNumberOfBits = 4u;
82 const uint32 kCombinedStatusMaskMaximumValue =
83 (1u << kCombinedStatusMaskNumberOfBits);
85 // Returns whether or not a dry-run shall be performed.
86 bool ShouldPerformDryRun() {
87 return StartsWithASCII(
88 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
89 kAutomaticProfileResetStudyDryRunGroupName, true);
92 // Returns whether or not a live-run shall be performed.
93 bool ShouldPerformLiveRun() {
94 return StartsWithASCII(
95 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName),
96 kAutomaticProfileResetStudyEnabledGroupName, true);
99 // If the currently active experiment group prescribes a |program| and
100 // |hash_seed| to use instead of the baked-in ones, retrieves those and returns
101 // true. Otherwise, returns false.
102 bool GetProgramAndHashSeedOverridesFromExperiment(std::string* program,
103 std::string* hash_seed) {
104 DCHECK(program);
105 DCHECK(hash_seed);
106 #if defined(GOOGLE_CHROME_BUILD)
107 std::map<std::string, std::string> params;
108 chrome_variations::GetVariationParams(kAutomaticProfileResetStudyName,
109 &params);
110 if (params.count(kAutomaticProfileResetStudyProgramParameterName) &&
111 params.count(kAutomaticProfileResetStudyHashSeedParameterName)) {
112 program->swap(params[kAutomaticProfileResetStudyProgramParameterName]);
113 hash_seed->swap(params[kAutomaticProfileResetStudyHashSeedParameterName]);
114 return true;
116 #endif
117 return false;
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());
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()));
151 target_dictionary->Set(value_tree_key, value_tree);
152 target_dictionary->Set(is_user_controlled_tree_key, is_user_controlled_tree);
155 } // namespace
158 // AutomaticProfileResetter::InputBuilder ------------------------------------
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.
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;
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() {}
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;
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()));
204 // Each task will post the next one. Just trigger the chain reaction.
205 PostNextTask();
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()));
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();
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)));
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());
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());
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());
275 scoped_ptr<base::ListValue> search_providers_details(
276 delegate_->GetPrepopulatedSearchProvidersDetails());
277 data_->Set(kSearchProvidersKey, search_providers_details.release());
279 data_->SetBoolean(kDefaultSearchProviderIsUserControlledKey,
280 !delegate_->IsDefaultSearchProviderManaged());
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());
290 // -------------------------------------------------------------------------
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);
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();
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));
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();
329 content::BrowserThread::PostTask(
330 content::BrowserThread::UI, FROM_HERE, next_task);
333 Profile* profile_;
334 AutomaticProfileResetterDelegate* delegate_;
335 ProgramInputCallback callback_;
337 PreferenceHostedPromptMemento memento_in_prefs_;
338 LocalStateHostedPromptMemento memento_in_local_state_;
339 FileHostedPromptMemento memento_in_file_;
341 scoped_ptr<base::DictionaryValue> data_;
342 std::queue<base::Closure> task_queue_;
344 DISALLOW_COPY_AND_ASSIGN(InputBuilder);
348 // AutomaticProfileResetter::EvaluationResults -------------------------------
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) {}
358 std::string memento_value_in_prefs;
359 std::string memento_value_in_local_state;
360 std::string memento_value_in_file;
362 bool should_prompt;
363 bool had_prompted_already;
364 uint32 satisfied_criteria_mask;
365 uint32 combined_status_mask;
369 // AutomaticProfileResetter --------------------------------------------------
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_);
382 AutomaticProfileResetter::~AutomaticProfileResetter() {}
384 void AutomaticProfileResetter::Initialize() {
385 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
386 DCHECK_EQ(state_, STATE_UNINITIALIZED);
388 if (!ShouldPerformDryRun() && !ShouldPerformLiveRun()) {
389 state_ = STATE_DISABLED;
390 return;
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();
408 delegate_.reset(new AutomaticProfileResetterDelegateImpl(
409 profile_, ProfileResetter::ALL));
410 task_runner_for_waiting_ =
411 content::BrowserThread::GetMessageLoopProxyForThread(
412 content::BrowserThread::UI);
414 state_ = STATE_INITIALIZED;
417 void AutomaticProfileResetter::Activate() {
418 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
419 DCHECK(state_ == STATE_INITIALIZED || state_ == STATE_DISABLED);
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;
437 void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback) {
438 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
439 DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);
441 state_ = STATE_PERFORMING_RESET;
442 should_show_reset_banner_ = false;
444 ReportPromptResult(PROMPT_ACTION_RESET);
445 delegate_->TriggerProfileSettingsReset(
446 send_feedback,
447 base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted,
448 weak_ptr_factory_.GetWeakPtr()));
451 void AutomaticProfileResetter::SkipProfileReset() {
452 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
453 DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE);
455 should_show_reset_banner_ = false;
457 ReportPromptResult(PROMPT_ACTION_NO_RESET);
458 delegate_->DismissPrompt();
459 FinishResetPromptFlow();
462 bool AutomaticProfileResetter::IsResetPromptFlowActive() const {
463 return state_ == STATE_HAS_TRIGGERED_PROMPT ||
464 state_ == STATE_HAS_SHOWN_BUBBLE;
467 bool AutomaticProfileResetter::ShouldShowResetBanner() const {
468 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
469 return should_show_reset_banner_ && ShouldPerformLiveRun();
472 void AutomaticProfileResetter::NotifyDidShowResetBubble() {
473 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
474 DCHECK_EQ(state_, STATE_HAS_TRIGGERED_PROMPT);
476 state_ = STATE_HAS_SHOWN_BUBBLE;
478 PersistMementos();
479 ReportPromptResult(PROMPT_SHOWN_BUBBLE);
482 void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() {
483 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
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();
494 void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog(
495 bool performed_reset) {
496 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
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);
514 FinishResetPromptFlow();
518 void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() {
519 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
520 should_show_reset_banner_ = false;
523 void AutomaticProfileResetter::SetProgramForTesting(
524 const std::string& program) {
525 program_ = program;
528 void AutomaticProfileResetter::SetHashSeedForTesting(
529 const std::string& hash_key) {
530 hash_seed_ = hash_key;
533 void AutomaticProfileResetter::SetDelegateForTesting(
534 scoped_ptr<AutomaticProfileResetterDelegate> delegate) {
535 delegate_ = delegate.Pass();
538 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
539 const scoped_refptr<base::TaskRunner>& task_runner) {
540 task_runner_for_waiting_ = task_runner;
543 void AutomaticProfileResetter::Shutdown() {
544 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
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.
550 state_ = STATE_DISABLED;
552 weak_ptr_factory_.InvalidateWeakPtrs();
553 delegate_.reset();
556 void AutomaticProfileResetter::PrepareEvaluationFlow() {
557 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
558 DCHECK_EQ(state_, STATE_INITIALIZED);
560 state_ = STATE_WAITING_ON_DEPENDENCIES;
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();
572 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
573 template_url_service_ready_ = true;
574 OnDependencyIsReady();
577 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
578 enumeration_of_loaded_modules_ready_ = true;
579 OnDependencyIsReady();
582 void AutomaticProfileResetter::OnDependencyIsReady() {
583 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
584 DCHECK_EQ(state_, STATE_WAITING_ON_DEPENDENCIES);
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()));
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_);
602 state_ = STATE_EVALUATING_CONDITIONS;
604 input_builder_.reset(new InputBuilder(profile_, delegate_.get()));
605 input_builder_->BuildEvaluatorProgramInput(
606 base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow,
607 weak_ptr_factory_.GetWeakPtr()));
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);
615 input_builder_.reset();
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);
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()));
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);
646 // In each case below, the respective field in result originally contains the
647 // default, so if the getter fails, we still have the correct value there.
648 scoped_ptr<EvaluationResults> results(new EvaluationResults);
649 interpreter.GetOutputBoolean(kShouldPromptKey, &results->should_prompt);
650 interpreter.GetOutputBoolean(kHadPromptedAlreadyKey,
651 &results->had_prompted_already);
652 interpreter.GetOutputString(kMementoValueInPrefsKey,
653 &results->memento_value_in_prefs);
654 interpreter.GetOutputString(kMementoValueInLocalStateKey,
655 &results->memento_value_in_local_state);
656 interpreter.GetOutputString(kMementoValueInFileKey,
657 &results->memento_value_in_file);
658 for (size_t i = 0; i < kCombinedStatusMaskNumberOfBits; ++i) {
659 bool flag = false;
660 std::string mask_i_th_bit_key =
661 kCombinedStatusMaskKeyPrefix + base::IntToString(i + 1);
662 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
663 results->combined_status_mask |= (1 << i);
665 for (size_t i = 0; i < kSatisfiedCriteriaMaskNumberOfBits; ++i) {
666 bool flag = false;
667 std::string mask_i_th_bit_key =
668 kSatisfiedCriteriaMaskKeyPrefix + base::IntToString(i + 1);
669 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag)
670 results->satisfied_criteria_mask |= (1 << i);
672 return results.Pass();
675 void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask,
676 uint32 combined_status_mask) {
677 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask",
678 satisfied_criteria_mask,
679 kSatisfiedCriteriaMaskMaximumValue);
680 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask",
681 combined_status_mask,
682 kCombinedStatusMaskMaximumValue);
685 void AutomaticProfileResetter::FinishEvaluationFlow(
686 scoped_ptr<EvaluationResults> results) {
687 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
688 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);
690 ReportStatistics(results->satisfied_criteria_mask,
691 results->combined_status_mask);
693 if (results->should_prompt)
694 should_show_reset_banner_ = true;
696 if (results->should_prompt && !results->had_prompted_already) {
697 evaluation_results_ = results.Pass();
698 BeginResetPromptFlow();
699 } else {
700 state_ = STATE_DONE;
704 void AutomaticProfileResetter::BeginResetPromptFlow() {
705 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
706 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS);
708 state_ = STATE_HAS_TRIGGERED_PROMPT;
710 if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) {
711 // Start fetching the brandcoded default settings speculatively in the
712 // background, so as to reduce waiting time if the user chooses to go
713 // through with the reset.
714 delegate_->FetchBrandcodedDefaultSettingsIfNeeded();
715 } else {
716 PersistMementos();
717 ReportPromptResult(PROMPT_NOT_TRIGGERED);
718 FinishResetPromptFlow();
722 void AutomaticProfileResetter::OnProfileSettingsResetCompleted() {
723 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
724 DCHECK_EQ(state_, STATE_PERFORMING_RESET);
726 delegate_->DismissPrompt();
727 FinishResetPromptFlow();
730 void AutomaticProfileResetter::ReportPromptResult(PromptResult result) {
731 UMA_HISTOGRAM_ENUMERATION(
732 "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX);
735 void AutomaticProfileResetter::PersistMementos() {
736 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
737 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
738 state_ == STATE_HAS_SHOWN_BUBBLE);
739 DCHECK(evaluation_results_);
741 PreferenceHostedPromptMemento memento_in_prefs(profile_);
742 LocalStateHostedPromptMemento memento_in_local_state(profile_);
743 FileHostedPromptMemento memento_in_file(profile_);
745 memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs);
746 memento_in_local_state.StoreValue(
747 evaluation_results_->memento_value_in_local_state);
748 memento_in_file.StoreValue(evaluation_results_->memento_value_in_file);
750 evaluation_results_.reset();
753 void AutomaticProfileResetter::FinishResetPromptFlow() {
754 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
755 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT ||
756 state_ == STATE_HAS_SHOWN_BUBBLE ||
757 state_ == STATE_PERFORMING_RESET);
758 DCHECK(!evaluation_results_);
760 state_ = STATE_DONE;