| 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..c0f7d252608a04107ed2dd37500e80b534f7384b
|
| --- /dev/null
|
| +++ b/chrome/browser/profile_resetter/automatic_profile_resetter.cc
|
| @@ -0,0 +1,318 @@
|
| +// 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/logging.h"
|
| +#include "base/memory/ref_counted.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/task_runner_util.h"
|
| +#include "base/threading/sequenced_worker_pool.h"
|
| +#include "chrome/browser/profile_resetter/jtl_interpreter.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 {
|
| +
|
| +// Number of bits, and maximum value (exclusive) for the mask whose bits
|
| +// indicate 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 whose bits
|
| +// indicate if any of reset criteria were satisfied, and which of the mementos
|
| +// were already present.
|
| +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 kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled";
|
| +
|
| +// Keys used in the input dictionary of the program.
|
| +// TODO(engedy): Add these here 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 both 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 ---------------------------------------------
|
| +
|
| +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);
|
| +};
|
| +
|
| +// 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 (ShouldPerformDryRun() || ShouldPerformLiveRun()) {
|
| + ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance());
|
| + if (ShouldPerformLiveRun()) {
|
| + program_ =
|
| + resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES);
|
| + hash_seed_ =
|
| + resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED);
|
| + } else { // ShouldPerformDryRun()
|
| + 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::ShouldPerformDryRun() const {
|
| + return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) ==
|
| + kAutomaticProfileResetStudyDryRunGroupName;
|
| +}
|
| +
|
| +bool AutomaticProfileResetter::ShouldPerformLiveRun() const {
|
| + return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) ==
|
| + kAutomaticProfileResetStudyEnabledGroupName;
|
| +}
|
| +
|
| +void AutomaticProfileResetter::BeginEvaluationFlow() {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + DCHECK_EQ(state_, STATE_READY);
|
| +
|
| + if (!program_.empty()) {
|
| + state_ = STATE_WORKING;
|
| + memento_in_file_.ReadValue(
|
| + base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + } else {
|
| + // Terminate early if there is no program included (nor set by tests).
|
| + state_ = STATE_DISABLED;
|
| + }
|
| +}
|
| +
|
| +scoped_ptr<base::DictionaryValue>
|
| +AutomaticProfileResetter::BuildEvaluatorProgramInput(
|
| + const std::string& memento_value_in_file) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + // TODO(engedy): Add any additional state here that is needed by the program.
|
| + 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_,
|
| + program_,
|
| + base::Passed(&input)),
|
| + base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<AutomaticProfileResetter::EvaluationResults>
|
| +AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread(
|
| + const base::StringPiece& hash_seed,
|
| + const base::StringPiece& program,
|
| + scoped_ptr<base::DictionaryValue> program_input) {
|
| + std::string hash_seed_str(hash_seed.as_string());
|
| + std::string program_str(program.as_string());
|
| + JtlInterpreter interpreter(hash_seed_str, program_str, program_input.get());
|
| + interpreter.Execute();
|
| + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult",
|
| + interpreter.result(),
|
| + JtlInterpreter::RESULT_MAX);
|
| +
|
| + // In each case below, the respective field in result originally contains the
|
| + // default, so if the getter fails, we still have the correct value there.
|
| + scoped_ptr<EvaluationResults> results(new EvaluationResults());
|
| + 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;
|
| + if (interpreter.GetOutputBoolean(kCombinedStatusMaskKeys[i], &flag) && flag)
|
| + results->combined_status_mask |= (1 << i);
|
| + }
|
| + for (size_t i = 0; i < arraysize(kSatisfiedCriteriaMaskKeys); ++i) {
|
| + bool flag = false;
|
| + if (interpreter.GetOutputBoolean(kSatisfiedCriteriaMaskKeys[i], &flag) &&
|
| + 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 (ShouldPerformLiveRun()) {
|
| + delegate_->ShowPrompt();
|
| + } else {
|
| + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult",
|
| + PROMPT_NOT_SHOWN,
|
| + PROMPT_RESULT_MAX);
|
| + }
|
| + }
|
| +
|
| + state_ = STATE_DONE;
|
| +}
|
| +
|
| +void AutomaticProfileResetter::SetHashSeedForTesting(
|
| + const base::StringPiece& hash_key) {
|
| + hash_seed_ = hash_key;
|
| +}
|
| +
|
| +void AutomaticProfileResetter::SetProgramForTesting(
|
| + const base::StringPiece& 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();
|
| +}
|
|
|