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 |