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"
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"
35 // Helpers -------------------------------------------------------------------
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";
48 // How long to wait after start-up before unleashing the evaluation flow.
49 const int64 kEvaluationFlowDelayInSeconds
= 55;
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";
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";
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";
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
);
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
);
86 // Returns whether or not a dry-run shall be performed.
87 bool ShouldPerformDryRun() {
88 return base::StartsWith(
89 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName
),
90 kAutomaticProfileResetStudyDryRunGroupName
, base::CompareCase::SENSITIVE
);
93 // Returns whether or not a live-run shall be performed.
94 bool ShouldPerformLiveRun() {
95 return base::StartsWith(
96 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName
),
97 kAutomaticProfileResetStudyEnabledGroupName
,
98 base::CompareCase::SENSITIVE
);
101 // If the currently active experiment group prescribes a |program| and
102 // |hash_seed| to use instead of the baked-in ones, retrieves those and returns
103 // true. Otherwise, returns false.
104 bool GetProgramAndHashSeedOverridesFromExperiment(std::string
* program
,
105 std::string
* hash_seed
) {
108 #if defined(GOOGLE_CHROME_BUILD)
109 std::map
<std::string
, std::string
> params
;
110 variations::GetVariationParams(kAutomaticProfileResetStudyName
, ¶ms
);
111 if (params
.count(kAutomaticProfileResetStudyProgramParameterName
) &&
112 params
.count(kAutomaticProfileResetStudyHashSeedParameterName
)) {
113 program
->swap(params
[kAutomaticProfileResetStudyProgramParameterName
]);
114 hash_seed
->swap(params
[kAutomaticProfileResetStudyHashSeedParameterName
]);
121 // Takes |pref_name_to_value_map|, which shall be a deep-copy of all preferences
122 // in |source| without path expansion; and (1.) creates a sub-tree from it named
123 // |value_tree_key| in |target_dictionary| with path expansion, and (2.) also
124 // creates an isomorphic sub-tree under the key |is_user_controlled_tree_key|
125 // that contains only Boolean values indicating whether or not the corresponding
126 // preference is coming from the 'user' PrefStore.
127 void BuildSubTreesFromPreferences(
128 scoped_ptr
<base::DictionaryValue
> pref_name_to_value_map
,
129 const PrefService
* source
,
130 const char* value_tree_key
,
131 const char* is_user_controlled_tree_key
,
132 base::DictionaryValue
* target_dictionary
) {
133 std::vector
<std::string
> pref_names
;
134 pref_names
.reserve(pref_name_to_value_map
->size());
135 for (base::DictionaryValue::Iterator
it(*pref_name_to_value_map
);
136 !it
.IsAtEnd(); it
.Advance())
137 pref_names
.push_back(it
.key());
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()));
152 target_dictionary
->Set(value_tree_key
, value_tree
);
153 target_dictionary
->Set(is_user_controlled_tree_key
, is_user_controlled_tree
);
159 // AutomaticProfileResetter::InputBuilder ------------------------------------
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.
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
172 class AutomaticProfileResetter::InputBuilder
173 : public base::SupportsWeakPtr
<InputBuilder
> {
175 typedef base::Callback
<void(scoped_ptr
<base::DictionaryValue
>)>
176 ProgramInputCallback
;
178 // The dependencies must have been initialized through |delegate|, i.e. the
179 // RequestCallback[...] methods must have already fired before calling this.
180 InputBuilder(Profile
* profile
, AutomaticProfileResetterDelegate
* delegate
)
183 memento_in_prefs_(profile_
),
184 memento_in_local_state_(profile_
),
185 memento_in_file_(profile_
) {}
188 // Assembles the data required by the evaluator program into a dictionary
189 // format, and posts it back to the UI thread with |callback| once ready. In
190 // order not to block the UI thread for long consecutive periods of time, the
191 // work is divided into smaller tasks, see class comment above for details.
192 // It is safe to destroy |this| immediately from within the |callback|.
193 void BuildEvaluatorProgramInput(const ProgramInputCallback
& callback
) {
195 DCHECK(!callback
.is_null());
196 data_
.reset(new base::DictionaryValue
);
197 callback_
= callback
;
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()));
205 // Each task will post the next one. Just trigger the chain reaction.
210 // Asynchronous task that includes memento values (or empty strings in case
211 // mementos are not there).
212 void IncludeMementoValues() {
213 data_
->SetString(kMementoValueInPrefsKey
, memento_in_prefs_
.ReadValue());
214 data_
->SetString(kMementoValueInLocalStateKey
,
215 memento_in_local_state_
.ReadValue());
216 memento_in_file_
.ReadValue(base::Bind(
217 &InputBuilder::IncludeFileBasedMementoCallback
, AsWeakPtr()));
220 // Called back by |memento_in_file_| once the |memento_value| has been read.
221 void IncludeFileBasedMementoCallback(const std::string
& memento_value
) {
222 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
223 data_
->SetString(kMementoValueInFileKey
, memento_value
);
224 // As an asynchronous task, we need to take care of posting the next task.
228 // Task that includes all user (i.e. profile-specific) preferences, along with
229 // information about whether the value is coming from the 'user' PrefStore.
230 // This is the most expensive operation, so it is itself split into two parts.
231 void IncludeUserPreferences() {
232 PrefService
* prefs
= profile_
->GetPrefs();
234 scoped_ptr
<base::DictionaryValue
> pref_name_to_value_map(
235 prefs
->GetPreferenceValuesWithoutPathExpansion());
236 AddTask(base::Bind(&InputBuilder::IncludeUserPreferencesPartTwo
,
238 base::Passed(&pref_name_to_value_map
)));
241 // Second part to above.
242 void IncludeUserPreferencesPartTwo(
243 scoped_ptr
<base::DictionaryValue
> pref_name_to_value_map
) {
244 PrefService
* prefs
= profile_
->GetPrefs();
246 BuildSubTreesFromPreferences(
247 pref_name_to_value_map
.Pass(),
250 kUserPreferencesIsUserControlledKey
,
254 // Task that includes all local state (i.e. shared) preferences, along with
255 // information about whether the value is coming from the 'user' PrefStore.
256 void IncludeLocalState() {
257 PrefService
* local_state
= g_browser_process
->local_state();
259 scoped_ptr
<base::DictionaryValue
> pref_name_to_value_map(
260 local_state
->GetPreferenceValuesWithoutPathExpansion());
261 BuildSubTreesFromPreferences(
262 pref_name_to_value_map
.Pass(),
265 kLocalStateIsUserControlledKey
,
269 // Task that includes all information related to search engines.
270 void IncludeSearchEngines() {
271 scoped_ptr
<base::DictionaryValue
> default_search_provider_details(
272 delegate_
->GetDefaultSearchProviderDetails());
273 data_
->Set(kDefaultSearchProviderKey
,
274 default_search_provider_details
.release());
276 scoped_ptr
<base::ListValue
> search_providers_details(
277 delegate_
->GetPrepopulatedSearchProvidersDetails());
278 data_
->Set(kSearchProvidersKey
, search_providers_details
.release());
280 data_
->SetBoolean(kDefaultSearchProviderIsUserControlledKey
,
281 !delegate_
->IsDefaultSearchProviderManaged());
284 // Task that includes information about loaded modules.
285 void IncludeLoadedModules() {
286 scoped_ptr
<base::ListValue
> loaded_module_digests(
287 delegate_
->GetLoadedModuleNameDigests());
288 data_
->Set(kLoadedModuleDigestsKey
, loaded_module_digests
.release());
291 // -------------------------------------------------------------------------
293 // Adds a |task| that can do as much asynchronous processing as it wants, but
294 // will need to finally call PostNextTask() on the UI thread when done.
295 void AddAsyncTask(const base::Closure
& task
) {
296 task_queue_
.push(task
);
299 // Convenience wrapper for synchronous tasks.
300 void SynchronousTaskWrapper(const base::Closure
& task
) {
301 base::ElapsedTimer timer
;
303 UMA_HISTOGRAM_CUSTOM_TIMES(
304 "AutomaticProfileReset.InputBuilder.TaskDuration",
306 base::TimeDelta::FromMilliseconds(1),
307 base::TimeDelta::FromSeconds(2),
312 // Adds a task that needs to finish synchronously. In exchange, PostNextTask()
313 // is called automatically when the |task| returns, and execution time is
315 void AddTask(const base::Closure
& task
) {
317 base::Bind(&InputBuilder::SynchronousTaskWrapper
, AsWeakPtr(), task
));
320 // Posts the next task from the |task_queue_|, unless it is exhausted, in
321 // which case it posts |callback_| to return with the results.
322 void PostNextTask() {
323 base::Closure next_task
;
324 if (task_queue_
.empty()) {
325 next_task
= base::Bind(callback_
, base::Passed(&data_
));
327 next_task
= task_queue_
.front();
330 content::BrowserThread::PostTask(
331 content::BrowserThread::UI
, FROM_HERE
, next_task
);
335 AutomaticProfileResetterDelegate
* delegate_
;
336 ProgramInputCallback callback_
;
338 PreferenceHostedPromptMemento memento_in_prefs_
;
339 LocalStateHostedPromptMemento memento_in_local_state_
;
340 FileHostedPromptMemento memento_in_file_
;
342 scoped_ptr
<base::DictionaryValue
> data_
;
343 std::queue
<base::Closure
> task_queue_
;
345 DISALLOW_COPY_AND_ASSIGN(InputBuilder
);
349 // AutomaticProfileResetter::EvaluationResults -------------------------------
351 // Encapsulates the output values extracted from the evaluator program.
352 struct AutomaticProfileResetter::EvaluationResults
{
354 : should_prompt(false),
355 had_prompted_already(false),
356 satisfied_criteria_mask(0),
357 combined_status_mask(0) {}
359 std::string memento_value_in_prefs
;
360 std::string memento_value_in_local_state
;
361 std::string memento_value_in_file
;
364 bool had_prompted_already
;
365 uint32 satisfied_criteria_mask
;
366 uint32 combined_status_mask
;
370 // AutomaticProfileResetter --------------------------------------------------
372 AutomaticProfileResetter::AutomaticProfileResetter(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) {
383 AutomaticProfileResetter::~AutomaticProfileResetter() {}
385 void AutomaticProfileResetter::Initialize() {
386 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
387 DCHECK_EQ(state_
, STATE_UNINITIALIZED
);
389 if (!ShouldPerformDryRun() && !ShouldPerformLiveRun()) {
390 state_
= STATE_DISABLED
;
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();
409 delegate_
.reset(new AutomaticProfileResetterDelegateImpl(
410 profile_
, ProfileResetter::ALL
));
411 task_runner_for_waiting_
=
412 content::BrowserThread::GetMessageLoopProxyForThread(
413 content::BrowserThread::UI
);
415 state_
= STATE_INITIALIZED
;
418 void AutomaticProfileResetter::Activate() {
419 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
420 DCHECK(state_
== STATE_INITIALIZED
|| state_
== STATE_DISABLED
);
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(
428 base::Bind(&AutomaticProfileResetter::PrepareEvaluationFlow
,
429 weak_ptr_factory_
.GetWeakPtr()),
430 base::TimeDelta::FromSeconds(kEvaluationFlowDelayInSeconds
));
432 // Terminate early if there is no program included (nor set by tests).
433 state_
= STATE_DISABLED
;
438 void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback
) {
439 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
440 DCHECK_EQ(state_
, STATE_HAS_SHOWN_BUBBLE
);
442 state_
= STATE_PERFORMING_RESET
;
443 should_show_reset_banner_
= false;
445 ReportPromptResult(PROMPT_ACTION_RESET
);
446 delegate_
->TriggerProfileSettingsReset(
448 base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted
,
449 weak_ptr_factory_
.GetWeakPtr()));
452 void AutomaticProfileResetter::SkipProfileReset() {
453 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
454 DCHECK_EQ(state_
, STATE_HAS_SHOWN_BUBBLE
);
456 should_show_reset_banner_
= false;
458 ReportPromptResult(PROMPT_ACTION_NO_RESET
);
459 delegate_
->DismissPrompt();
460 FinishResetPromptFlow();
463 bool AutomaticProfileResetter::IsResetPromptFlowActive() const {
464 return state_
== STATE_HAS_TRIGGERED_PROMPT
||
465 state_
== STATE_HAS_SHOWN_BUBBLE
;
468 bool AutomaticProfileResetter::ShouldShowResetBanner() const {
469 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
470 return should_show_reset_banner_
&& ShouldPerformLiveRun();
473 void AutomaticProfileResetter::NotifyDidShowResetBubble() {
474 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
475 DCHECK_EQ(state_
, STATE_HAS_TRIGGERED_PROMPT
);
477 state_
= STATE_HAS_SHOWN_BUBBLE
;
480 ReportPromptResult(PROMPT_SHOWN_BUBBLE
);
483 void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() {
484 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
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();
495 void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog(
496 bool performed_reset
) {
497 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
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
) {
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
);
515 FinishResetPromptFlow();
519 void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() {
520 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
521 should_show_reset_banner_
= false;
524 void AutomaticProfileResetter::SetProgramForTesting(
525 const std::string
& program
) {
529 void AutomaticProfileResetter::SetHashSeedForTesting(
530 const std::string
& hash_key
) {
531 hash_seed_
= hash_key
;
534 void AutomaticProfileResetter::SetDelegateForTesting(
535 scoped_ptr
<AutomaticProfileResetterDelegate
> delegate
) {
536 delegate_
= delegate
.Pass();
539 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
540 const scoped_refptr
<base::TaskRunner
>& task_runner
) {
541 task_runner_for_waiting_
= task_runner
;
544 void AutomaticProfileResetter::Shutdown() {
545 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
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.
551 state_
= STATE_DISABLED
;
553 weak_ptr_factory_
.InvalidateWeakPtrs();
557 void AutomaticProfileResetter::PrepareEvaluationFlow() {
558 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
559 DCHECK_EQ(state_
, STATE_INITIALIZED
);
561 state_
= STATE_WAITING_ON_DEPENDENCIES
;
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();
573 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
574 template_url_service_ready_
= true;
575 OnDependencyIsReady();
578 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
579 enumeration_of_loaded_modules_ready_
= true;
580 OnDependencyIsReady();
583 void AutomaticProfileResetter::OnDependencyIsReady() {
584 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
585 DCHECK_EQ(state_
, STATE_WAITING_ON_DEPENDENCIES
);
587 if (template_url_service_ready_
&& enumeration_of_loaded_modules_ready_
) {
588 state_
= STATE_READY
;
589 content::BrowserThread::PostTask(
590 content::BrowserThread::UI
,
592 base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow
,
593 weak_ptr_factory_
.GetWeakPtr()));
597 void AutomaticProfileResetter::BeginEvaluationFlow() {
598 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
599 DCHECK_EQ(state_
, STATE_READY
);
600 DCHECK(!program_
.empty());
601 DCHECK(!input_builder_
);
603 state_
= STATE_EVALUATING_CONDITIONS
;
605 input_builder_
.reset(new InputBuilder(profile_
, delegate_
.get()));
606 input_builder_
->BuildEvaluatorProgramInput(
607 base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow
,
608 weak_ptr_factory_
.GetWeakPtr()));
611 void AutomaticProfileResetter::ContinueWithEvaluationFlow(
612 scoped_ptr
<base::DictionaryValue
> program_input
) {
613 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
614 DCHECK_EQ(state_
, STATE_EVALUATING_CONDITIONS
);
616 input_builder_
.reset();
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
);
624 base::PostTaskAndReplyWithResult(
627 base::Bind(&EvaluateConditionsOnWorkerPoolThread
,
630 base::Passed(&program_input
)),
631 base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow
,
632 weak_ptr_factory_
.GetWeakPtr()));
636 scoped_ptr
<AutomaticProfileResetter::EvaluationResults
>
637 AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread(
638 const std::string
& hash_seed
,
639 const std::string
& program
,
640 scoped_ptr
<base::DictionaryValue
> program_input
) {
641 JtlInterpreter
interpreter(hash_seed
, program
, program_input
.get());
642 interpreter
.Execute();
643 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult",
644 interpreter
.result(),
645 JtlInterpreter::RESULT_MAX
);
646 UMA_HISTOGRAM_SPARSE_SLOWLY("AutomaticProfileReset.ProgramChecksum",
647 interpreter
.CalculateProgramChecksum());
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
) {
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
);
668 for (size_t i
= 0; i
< kSatisfiedCriteriaMaskNumberOfBits
; ++i
) {
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
);
675 return results
.Pass();
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
);
688 void AutomaticProfileResetter::FinishEvaluationFlow(
689 scoped_ptr
<EvaluationResults
> results
) {
690 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
691 DCHECK_EQ(state_
, STATE_EVALUATING_CONDITIONS
);
693 ReportStatistics(results
->satisfied_criteria_mask
,
694 results
->combined_status_mask
);
696 if (results
->should_prompt
)
697 should_show_reset_banner_
= true;
699 if (results
->should_prompt
&& !results
->had_prompted_already
) {
700 evaluation_results_
= results
.Pass();
701 BeginResetPromptFlow();
707 void AutomaticProfileResetter::BeginResetPromptFlow() {
708 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
709 DCHECK_EQ(state_
, STATE_EVALUATING_CONDITIONS
);
711 state_
= STATE_HAS_TRIGGERED_PROMPT
;
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();
720 ReportPromptResult(PROMPT_NOT_TRIGGERED
);
721 FinishResetPromptFlow();
725 void AutomaticProfileResetter::OnProfileSettingsResetCompleted() {
726 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
727 DCHECK_EQ(state_
, STATE_PERFORMING_RESET
);
729 delegate_
->DismissPrompt();
730 FinishResetPromptFlow();
733 void AutomaticProfileResetter::ReportPromptResult(PromptResult result
) {
734 UMA_HISTOGRAM_ENUMERATION(
735 "AutomaticProfileReset.PromptResult", result
, PROMPT_RESULT_MAX
);
738 void AutomaticProfileResetter::PersistMementos() {
739 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
740 DCHECK(state_
== STATE_HAS_TRIGGERED_PROMPT
||
741 state_
== STATE_HAS_SHOWN_BUBBLE
);
742 DCHECK(evaluation_results_
);
744 PreferenceHostedPromptMemento
memento_in_prefs(profile_
);
745 LocalStateHostedPromptMemento
memento_in_local_state(profile_
);
746 FileHostedPromptMemento
memento_in_file(profile_
);
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
);
753 evaluation_results_
.reset();
756 void AutomaticProfileResetter::FinishResetPromptFlow() {
757 DCHECK_CURRENTLY_ON(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_
);