Chromium Code Reviews| Index: chrome/browser/profile_resetter/automatic_profile_resetter.cc |
| diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter.cc b/chrome/browser/profile_resetter/automatic_profile_resetter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d691440fd86d23997110272dd72188ce0f7ee19b |
| --- /dev/null |
| +++ b/chrome/browser/profile_resetter/automatic_profile_resetter.cc |
| @@ -0,0 +1,322 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/profile_resetter/automatic_profile_resetter.h" |
| + |
| +#include "base/bind_helpers.h" |
| +#include "base/json/json_reader.h" |
| +#include "base/metrics/field_trial.h" |
| +#include "base/metrics/histogram.h" |
| +#include "base/prefs/pref_service.h" |
| +#include "base/task_runner.h" |
| +#include "base/threading/sequenced_worker_pool.h" |
| +#include "chrome/browser/profile_resetter/automatic_profile_resetter_mementos.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "grit/browser_resources.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| + |
| +namespace profile_resetter { |
| +namespace { |
| + |
| +// Number of bits, and maximum value (exclusive) for the mask consisting of |
| +// flags that tell which of reset criteria were satisfied. |
| +const size_t kSatisfiedCriteriaMaskBits = 2; |
| +const uint32 kSatisfiedCriteriaMaskMaximumValue = |
| + (1 << kSatisfiedCriteriaMaskBits); |
| + |
| +// Number of bits, and maximum value (exclusive) for the mask consisting of |
| +// flags that tell if any of reset criteria were satisfied and whether we had |
| +// already shown the prompt. |
| +const size_t kCombinedStatusMaskBits = 4; |
| +const uint32 kCombinedStatusMaskMaximumValue = (1 << kCombinedStatusMaskBits); |
| + |
| +// Name constants for the field trial behind which we enable this feature. |
| +const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset"; |
| +const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun"; |
| +const char kAutomaticProfileResetStudyEnabledroupName[] = "Enabled"; |
| + |
| +// Keys used in the input dictionary of the program. |
| +// TODO(engedy): Add these on an as-needed basis. |
| + |
| +// Keys used in the output dictionary of the program. |
| +const char kHadPromptedAlreadyKey[] = "had_prompted_already"; |
| +const char kSatisfiedCriteriaMaskKeys[][29] = {"satisfied_criteria_mask_bit1", |
| + "satisfied_criteria_mask_bit2"}; |
| +const char kCombinedStatusMaskKeys[][26] = { |
| + "combined_status_mask_bit1", "combined_status_mask_bit2", |
| + "combined_status_mask_bit3", "combined_status_mask_bit4"}; |
| + |
| +// Keys used in the input *and* output dictionary of the program. |
| +const char kMementoValueInPrefsKey[] = "memento_value_in_prefs"; |
| +const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state"; |
| +const char kMementoValueInFileKey[] = "memento_value_in_file"; |
| + |
| +COMPILE_ASSERT( |
| + arraysize(kSatisfiedCriteriaMaskKeys) == kSatisfiedCriteriaMaskBits, |
| + satisfied_criteria_mask_bits_mismatch); |
| +COMPILE_ASSERT(arraysize(kCombinedStatusMaskKeys) == kCombinedStatusMaskBits, |
| + combined_status_mask_bits_mismatch); |
| + |
| +// Implementation detail classes --------------------------------------------- |
| + |
| +// Mostly trivial AutomaticProfileResetterDelegate implementation. |
|
robertshield
2013/09/25 20:18:12
Nit: delete the above line, or explain how the imp
engedy
2013/09/27 23:27:34
Done.
|
| +class AutomaticProfileResetterDelegateImpl |
| + : public AutomaticProfileResetterDelegate { |
| + public: |
| + AutomaticProfileResetterDelegateImpl() {} |
| + virtual ~AutomaticProfileResetterDelegateImpl() {} |
| + |
| + // AutomaticProfileResetterDelegate overrides: |
| + |
| + virtual void ShowPrompt() OVERRIDE { |
| + // TODO(engedy): Call the UI from here once we have it. |
| + } |
| + |
| + virtual void ReportStatistics(uint32 satisfied_criteria_mask, |
| + uint32 combined_status_mask) OVERRIDE { |
| + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", |
| + satisfied_criteria_mask, |
| + kSatisfiedCriteriaMaskMaximumValue); |
| + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", |
| + combined_status_mask, |
| + kCombinedStatusMaskMaximumValue); |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateImpl); |
| +}; |
| + |
| +// A fake interpreter that enables writing EvaluateConditionsOnWorkerPoolThread. |
|
robertshield
2013/09/25 20:18:12
s/enables writing/used by/
Also mention that this
engedy
2013/09/27 23:27:34
I have wired in the real thing. PTAL below.
|
| +class FakeInterpreter { |
| + public: |
| + FakeInterpreter() {} |
| + ~FakeInterpreter() {} |
| + |
| + bool GetOutputBoolean(const std::string& key, bool* target) { return false; } |
| + |
| + bool GetOutputString(const std::string& key, std::string* target) { |
| + return false; |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(FakeInterpreter); |
| +}; |
| + |
| +// Enumeration of the possible outcomes of showing the profile reset prompt. |
| +enum PromptResult { |
| + // Prompt was not shown because only a dry-run was performed. |
| + PROMPT_NOT_SHOWN, |
| + PROMPT_ACTION_RESET, |
| + PROMPT_ACTION_NO_RESET, |
| + PROMPT_DISMISSED, |
| + // Prompt was still shown (not dismissed by the user) when Chrome was closed. |
| + PROMPT_IGNORED, |
| + PROMPT_RESULT_MAX |
| +}; |
| + |
| +} // namespace |
| + |
| +// AutomaticProfileResetter::EvaluationResults ------------------------------- |
| + |
| +// Encapsulates the output values extracted from the evaluator program. |
| +struct AutomaticProfileResetter::EvaluationResults { |
| + EvaluationResults() |
| + : had_prompted_already(false), |
| + satisfied_criteria_mask(0), |
| + combined_status_mask(0) {} |
| + |
| + std::string memento_value_in_prefs; |
| + std::string memento_value_in_local_state; |
| + std::string memento_value_in_file; |
| + |
| + bool had_prompted_already; |
| + uint32 satisfied_criteria_mask; |
| + uint32 combined_status_mask; |
| +}; |
| + |
| +// AutomaticProfileResetter -------------------------------------------------- |
| + |
| +AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile) |
| + : profile_(profile), |
| + state_(STATE_UNINITIALIZED), |
| + memento_in_prefs_(profile_), |
| + memento_in_local_state_(profile_), |
| + memento_in_file_(profile_), |
| + weak_ptr_factory_(this) { |
| + DCHECK(profile_); |
| +} |
| + |
| +AutomaticProfileResetter::~AutomaticProfileResetter() {} |
| + |
| +void AutomaticProfileResetter::Initialize() { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK_EQ(state_, STATE_UNINITIALIZED); |
| + |
| + if (IsDryRun() || IsLiveRun()) { |
| + ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance()); |
| + if (IsLiveRun()) { |
| + program_ = |
| + resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES); |
| + hash_seed_ = |
| + resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED); |
| + } else { |
| + program_ = |
| + resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY); |
| + hash_seed_ = resources.GetRawDataResource( |
| + IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY); |
| + } |
| + delegate_.reset(new AutomaticProfileResetterDelegateImpl()); |
| + state_ = STATE_READY; |
| + |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + } else { |
| + state_ = STATE_DISABLED; |
| + } |
| +} |
| + |
| +bool AutomaticProfileResetter::IsDryRun() const { |
| + return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == |
| + kAutomaticProfileResetStudyDryRunGroupName; |
| +} |
| + |
| +bool AutomaticProfileResetter::IsLiveRun() const { |
| + return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == |
| + kAutomaticProfileResetStudyEnabledroupName; |
|
robertshield
2013/09/25 20:18:12
Looks like there is a G missing before the "roup"
engedy
2013/09/27 23:27:34
Done.
|
| +} |
| + |
| +void AutomaticProfileResetter::BeginEvaluationFlow() { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK_EQ(state_, STATE_READY); |
| + |
| + state_ = STATE_WORKING; |
| + |
| + memento_in_file_.ReadValue( |
| + base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +scoped_ptr<base::DictionaryValue> |
| +AutomaticProfileResetter::BuildEvaluatorProgramInput( |
| + const std::string& memento_value_in_file) { |
| + // TODO(engedy): Add any additional state that is needed here. |
| + scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); |
| + input->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue()); |
| + input->SetString(kMementoValueInLocalStateKey, |
| + memento_in_local_state_.ReadValue()); |
| + input->SetString(kMementoValueInFileKey, memento_value_in_file); |
| + return input.Pass(); |
| +} |
| + |
| +void AutomaticProfileResetter::ContinueWithEvaluationFlow( |
| + const std::string& memento_value_in_file) { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK_EQ(state_, STATE_WORKING); |
| + PrefService* prefs = profile_->GetPrefs(); |
| + DCHECK(prefs); |
| + |
| + scoped_ptr<base::DictionaryValue> input( |
| + BuildEvaluatorProgramInput(memento_value_in_file)); |
| + |
| + base::SequencedWorkerPool* blocking_pool = |
| + content::BrowserThread::GetBlockingPool(); |
| + scoped_refptr<base::TaskRunner> task_runner = |
| + blocking_pool->GetTaskRunnerWithShutdownBehavior( |
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
| + |
| + base::PostTaskAndReplyWithResult( |
| + task_runner.get(), |
| + FROM_HERE, |
| + base::Bind(&EvaluateConditionsOnWorkerPoolThread, |
| + hash_seed_.as_string(), |
| + program_.as_string(), |
| + base::Passed(&input)), |
| + base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +scoped_ptr<AutomaticProfileResetter::EvaluationResults> |
|
battre
2013/09/25 09:43:26
add "// static" before this line
or in my opinion
engedy
2013/09/27 23:27:34
I am not sure I understand: how would this be any
|
| +AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread( |
| + const std::string& hash_key, |
| + const std::string& program, |
| + scoped_ptr<base::DictionaryValue> program_input) { |
| + // TODO(engedy): Once implemented, call real interpreter from here. |
| + scoped_ptr<EvaluationResults> results(new EvaluationResults()); |
| + FakeInterpreter interpreter; |
| + interpreter.GetOutputBoolean(kHadPromptedAlreadyKey, |
| + &results->had_prompted_already); |
| + interpreter.GetOutputString(kMementoValueInPrefsKey, |
| + &results->memento_value_in_prefs); |
| + interpreter.GetOutputString(kMementoValueInLocalStateKey, |
| + &results->memento_value_in_local_state); |
| + interpreter.GetOutputString(kMementoValueInFileKey, |
| + &results->memento_value_in_file); |
| + for (size_t i = 0; i < arraysize(kCombinedStatusMaskKeys); ++i) { |
| + bool flag = false; // Default value. |
| + interpreter.GetOutputBoolean(kCombinedStatusMaskKeys[i], &flag); |
| + if (flag) |
| + results->combined_status_mask |= (1 << i); |
| + } |
| + for (size_t i = 0; i < arraysize(kSatisfiedCriteriaMaskKeys); ++i) { |
| + bool flag = false; // Default value. |
| + interpreter.GetOutputBoolean(kSatisfiedCriteriaMaskKeys[i], &flag); |
| + if (flag) |
| + results->satisfied_criteria_mask |= (1 << i); |
| + } |
| + |
| + return results.Pass(); |
| +} |
| + |
| +void AutomaticProfileResetter::FinishEvaluationFlow( |
| + scoped_ptr<EvaluationResults> results) { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK_EQ(state_, STATE_WORKING); |
| + |
| + delegate_->ReportStatistics(results->satisfied_criteria_mask, |
| + results->combined_status_mask); |
| + |
| + if (results->satisfied_criteria_mask != 0 && !results->had_prompted_already) { |
| + memento_in_prefs_.StoreValue(results->memento_value_in_prefs); |
| + memento_in_local_state_.StoreValue(results->memento_value_in_local_state); |
| + memento_in_file_.StoreValue(results->memento_value_in_file); |
| + |
| + if (IsLiveRun()) |
| + delegate_->ShowPrompt(); |
| + else |
|
robertshield
2013/09/25 20:18:12
style nit: multi line statements should be wrapped
engedy
2013/09/27 23:27:34
Done.
|
| + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult", |
| + PROMPT_NOT_SHOWN, |
| + PROMPT_RESULT_MAX); |
| + } |
| + |
| + state_ = STATE_DONE; |
| +} |
| + |
| +void AutomaticProfileResetter::SetHashSeedForTesting( |
| + const std::string& hash_key) { |
| + hash_seed_ = hash_key; |
|
battre
2013/09/25 09:43:26
This looks incorrect. If he passed hash_key parame
engedy
2013/09/25 16:56:59
Good point. I played around too much with StringPi
engedy
2013/09/27 23:27:34
Done.
|
| +} |
| + |
| +void AutomaticProfileResetter::SetProgramForTesting( |
| + const std::string& program) { |
| + program_ = program; |
| +} |
| + |
| +void AutomaticProfileResetter::SetDelegateForTesting( |
| + AutomaticProfileResetterDelegate* delegate) { |
| + delegate_.reset(delegate); |
| +} |
| + |
| +void AutomaticProfileResetter::Shutdown() { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + |
| + state_ = STATE_DISABLED; |
| + delegate_.reset(); |
| + weak_ptr_factory_.InvalidateWeakPtrs(); |
| +} |
| + |
| +} // namespace profile_resetter |