Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/profile_resetter/automatic_profile_resetter.h" | |
| 6 | |
| 7 #include "base/bind_helpers.h" | |
| 8 #include "base/json/json_reader.h" | |
| 9 #include "base/metrics/field_trial.h" | |
| 10 #include "base/metrics/histogram.h" | |
| 11 #include "base/prefs/pref_service.h" | |
| 12 #include "base/task_runner.h" | |
| 13 #include "base/threading/sequenced_worker_pool.h" | |
| 14 #include "chrome/browser/profile_resetter/automatic_profile_resetter_mementos.h" | |
| 15 #include "chrome/browser/profile_resetter/jtl_interpreter.h" | |
| 16 #include "chrome/browser/profiles/profile.h" | |
| 17 #include "content/public/browser/browser_thread.h" | |
| 18 #include "grit/browser_resources.h" | |
| 19 #include "ui/base/resource/resource_bundle.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Number of bits, and maximum value (exclusive) for the mask consisting of | |
| 24 // flags that tell which of reset criteria were satisfied. | |
| 25 const size_t kSatisfiedCriteriaMaskBits = 2; | |
| 26 const uint32 kSatisfiedCriteriaMaskMaximumValue = | |
| 27 (1 << kSatisfiedCriteriaMaskBits); | |
| 28 | |
| 29 // Number of bits, and maximum value (exclusive) for the mask consisting of | |
| 30 // flags that tell if any of reset criteria were satisfied and whether we had | |
| 31 // already shown the prompt. | |
| 32 const size_t kCombinedStatusMaskBits = 4; | |
| 33 const uint32 kCombinedStatusMaskMaximumValue = (1 << kCombinedStatusMaskBits); | |
| 34 | |
| 35 // Name constants for the field trial behind which we enable this feature. | |
| 36 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset"; | |
| 37 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun"; | |
| 38 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled"; | |
| 39 | |
| 40 // Keys used in the input dictionary of the program. | |
| 41 // TODO(engedy): Add these on an as-needed basis. | |
| 42 | |
| 43 // Keys used in the output dictionary of the program. | |
| 44 const char kHadPromptedAlreadyKey[] = "had_prompted_already"; | |
| 45 const char kSatisfiedCriteriaMaskKeys[][29] = {"satisfied_criteria_mask_bit1", | |
| 46 "satisfied_criteria_mask_bit2"}; | |
| 47 const char kCombinedStatusMaskKeys[][26] = { | |
| 48 "combined_status_mask_bit1", "combined_status_mask_bit2", | |
| 49 "combined_status_mask_bit3", "combined_status_mask_bit4"}; | |
| 50 | |
| 51 // Keys used in the input *and* output dictionary of the program. | |
| 52 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs"; | |
| 53 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state"; | |
| 54 const char kMementoValueInFileKey[] = "memento_value_in_file"; | |
| 55 | |
| 56 COMPILE_ASSERT( | |
| 57 arraysize(kSatisfiedCriteriaMaskKeys) == kSatisfiedCriteriaMaskBits, | |
| 58 satisfied_criteria_mask_bits_mismatch); | |
| 59 COMPILE_ASSERT(arraysize(kCombinedStatusMaskKeys) == kCombinedStatusMaskBits, | |
| 60 combined_status_mask_bits_mismatch); | |
| 61 | |
| 62 // Implementation detail classes --------------------------------------------- | |
| 63 | |
| 64 class AutomaticProfileResetterDelegateImpl | |
| 65 : public AutomaticProfileResetterDelegate { | |
| 66 public: | |
| 67 AutomaticProfileResetterDelegateImpl() {} | |
| 68 virtual ~AutomaticProfileResetterDelegateImpl() {} | |
| 69 | |
| 70 // AutomaticProfileResetterDelegate overrides: | |
| 71 | |
| 72 virtual void ShowPrompt() OVERRIDE { | |
| 73 // TODO(engedy): Call the UI from here once we have it. | |
| 74 } | |
| 75 | |
| 76 virtual void ReportStatistics(uint32 satisfied_criteria_mask, | |
| 77 uint32 combined_status_mask) OVERRIDE { | |
| 78 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", | |
| 79 satisfied_criteria_mask, | |
| 80 kSatisfiedCriteriaMaskMaximumValue); | |
| 81 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", | |
| 82 combined_status_mask, | |
| 83 kCombinedStatusMaskMaximumValue); | |
| 84 } | |
| 85 | |
| 86 private: | |
| 87 DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateImpl); | |
| 88 }; | |
| 89 | |
| 90 // Enumeration of the possible outcomes of showing the profile reset prompt. | |
| 91 enum PromptResult { | |
| 92 // Prompt was not shown because only a dry-run was performed. | |
| 93 PROMPT_NOT_SHOWN, | |
| 94 PROMPT_ACTION_RESET, | |
| 95 PROMPT_ACTION_NO_RESET, | |
| 96 PROMPT_DISMISSED, | |
| 97 // Prompt was still shown (not dismissed by the user) when Chrome was closed. | |
| 98 PROMPT_IGNORED, | |
| 99 PROMPT_RESULT_MAX | |
| 100 }; | |
| 101 | |
| 102 } // namespace | |
| 103 | |
| 104 // AutomaticProfileResetter::EvaluationResults ------------------------------- | |
| 105 | |
| 106 // Encapsulates the output values extracted from the evaluator program. | |
| 107 struct AutomaticProfileResetter::EvaluationResults { | |
| 108 EvaluationResults() | |
| 109 : had_prompted_already(false), | |
| 110 satisfied_criteria_mask(0), | |
| 111 combined_status_mask(0) {} | |
| 112 | |
| 113 std::string memento_value_in_prefs; | |
| 114 std::string memento_value_in_local_state; | |
| 115 std::string memento_value_in_file; | |
| 116 | |
| 117 bool had_prompted_already; | |
| 118 uint32 satisfied_criteria_mask; | |
| 119 uint32 combined_status_mask; | |
| 120 }; | |
| 121 | |
| 122 // AutomaticProfileResetter -------------------------------------------------- | |
| 123 | |
| 124 AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile) | |
| 125 : profile_(profile), | |
| 126 state_(STATE_UNINITIALIZED), | |
| 127 memento_in_prefs_(profile_), | |
| 128 memento_in_local_state_(profile_), | |
| 129 memento_in_file_(profile_), | |
| 130 weak_ptr_factory_(this) { | |
| 131 DCHECK(profile_); | |
| 132 } | |
| 133 | |
| 134 AutomaticProfileResetter::~AutomaticProfileResetter() {} | |
| 135 | |
| 136 void AutomaticProfileResetter::Initialize() { | |
| 137 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 138 DCHECK_EQ(state_, STATE_UNINITIALIZED); | |
| 139 | |
| 140 if (ShouldPerformDryRun() || ShouldPerformLiveRun()) { | |
| 141 ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance()); | |
| 142 if (ShouldPerformLiveRun()) { | |
| 143 program_ = | |
| 144 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES); | |
| 145 hash_seed_ = | |
| 146 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED); | |
| 147 } else { // ShouldPerformDryRun() | |
|
battre
2013/09/28 07:36:16
nit: +1 space before //
engedy
2013/10/01 14:12:19
Done.
| |
| 148 program_ = | |
| 149 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY); | |
| 150 hash_seed_ = resources.GetRawDataResource( | |
| 151 IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY); | |
| 152 } | |
| 153 delegate_.reset(new AutomaticProfileResetterDelegateImpl()); | |
| 154 | |
| 155 state_ = STATE_READY; | |
| 156 | |
| 157 content::BrowserThread::PostTask( | |
| 158 content::BrowserThread::UI, | |
| 159 FROM_HERE, | |
| 160 base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow, | |
| 161 weak_ptr_factory_.GetWeakPtr())); | |
| 162 } else { | |
| 163 state_ = STATE_DISABLED; | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 bool AutomaticProfileResetter::ShouldPerformDryRun() const { | |
| 168 return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == | |
| 169 kAutomaticProfileResetStudyDryRunGroupName; | |
| 170 } | |
| 171 | |
| 172 bool AutomaticProfileResetter::ShouldPerformLiveRun() const { | |
| 173 return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == | |
| 174 kAutomaticProfileResetStudyEnabledGroupName; | |
| 175 } | |
| 176 | |
| 177 void AutomaticProfileResetter::BeginEvaluationFlow() { | |
| 178 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 179 DCHECK_EQ(state_, STATE_READY); | |
| 180 | |
| 181 state_ = STATE_WORKING; | |
| 182 | |
| 183 memento_in_file_.ReadValue( | |
| 184 base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow, | |
| 185 weak_ptr_factory_.GetWeakPtr())); | |
| 186 } | |
| 187 | |
| 188 scoped_ptr<base::DictionaryValue> | |
| 189 AutomaticProfileResetter::BuildEvaluatorProgramInput( | |
| 190 const std::string& memento_value_in_file) { | |
| 191 // TODO(engedy): Add any additional state that is needed here. | |
| 192 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); | |
| 193 input->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue()); | |
| 194 input->SetString(kMementoValueInLocalStateKey, | |
| 195 memento_in_local_state_.ReadValue()); | |
| 196 input->SetString(kMementoValueInFileKey, memento_value_in_file); | |
| 197 return input.Pass(); | |
| 198 } | |
| 199 | |
| 200 void AutomaticProfileResetter::ContinueWithEvaluationFlow( | |
| 201 const std::string& memento_value_in_file) { | |
| 202 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 203 DCHECK_EQ(state_, STATE_WORKING); | |
| 204 PrefService* prefs = profile_->GetPrefs(); | |
| 205 DCHECK(prefs); | |
| 206 | |
| 207 scoped_ptr<base::DictionaryValue> input( | |
| 208 BuildEvaluatorProgramInput(memento_value_in_file)); | |
| 209 | |
| 210 base::SequencedWorkerPool* blocking_pool = | |
| 211 content::BrowserThread::GetBlockingPool(); | |
| 212 scoped_refptr<base::TaskRunner> task_runner = | |
| 213 blocking_pool->GetTaskRunnerWithShutdownBehavior( | |
| 214 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
| 215 | |
| 216 base::PostTaskAndReplyWithResult( | |
| 217 task_runner.get(), | |
| 218 FROM_HERE, | |
| 219 base::Bind(&EvaluateConditionsOnWorkerPoolThread, | |
| 220 hash_seed_, | |
| 221 program_, | |
| 222 base::Passed(&input)), | |
| 223 base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow, | |
| 224 weak_ptr_factory_.GetWeakPtr())); | |
| 225 } | |
| 226 | |
| 227 // static | |
| 228 scoped_ptr<AutomaticProfileResetter::EvaluationResults> | |
| 229 AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread( | |
| 230 const base::StringPiece& hash_seed, | |
| 231 const base::StringPiece& program, | |
| 232 scoped_ptr<base::DictionaryValue> program_input) { | |
| 233 std::string hash_seed_str(hash_seed.as_string()); | |
| 234 std::string program_str(program.as_string()); | |
| 235 | |
| 236 JtlInterpreter interpreter(hash_seed_str, program_str, program_input.get()); | |
| 237 interpreter.Execute(); | |
|
battre
2013/09/28 07:36:16
please UMA report the success status.
engedy
2013/10/01 14:12:19
Done.
| |
| 238 | |
| 239 scoped_ptr<EvaluationResults> results(new EvaluationResults()); | |
| 240 interpreter.GetOutputBoolean(kHadPromptedAlreadyKey, | |
| 241 &results->had_prompted_already); | |
| 242 interpreter.GetOutputString(kMementoValueInPrefsKey, | |
| 243 &results->memento_value_in_prefs); | |
| 244 interpreter.GetOutputString(kMementoValueInLocalStateKey, | |
| 245 &results->memento_value_in_local_state); | |
| 246 interpreter.GetOutputString(kMementoValueInFileKey, | |
| 247 &results->memento_value_in_file); | |
| 248 for (size_t i = 0; i < arraysize(kCombinedStatusMaskKeys); ++i) { | |
| 249 bool flag = false; // Default value. | |
| 250 interpreter.GetOutputBoolean(kCombinedStatusMaskKeys[i], &flag); | |
| 251 if (flag) | |
| 252 results->combined_status_mask |= (1 << i); | |
| 253 } | |
| 254 for (size_t i = 0; i < arraysize(kSatisfiedCriteriaMaskKeys); ++i) { | |
| 255 bool flag = false; // Default value. | |
| 256 interpreter.GetOutputBoolean(kSatisfiedCriteriaMaskKeys[i], &flag); | |
| 257 if (flag) | |
| 258 results->satisfied_criteria_mask |= (1 << i); | |
| 259 } | |
| 260 | |
| 261 return results.Pass(); | |
| 262 } | |
| 263 | |
| 264 void AutomaticProfileResetter::FinishEvaluationFlow( | |
| 265 scoped_ptr<EvaluationResults> results) { | |
| 266 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 267 DCHECK_EQ(state_, STATE_WORKING); | |
| 268 | |
| 269 delegate_->ReportStatistics(results->satisfied_criteria_mask, | |
| 270 results->combined_status_mask); | |
| 271 | |
| 272 if (results->satisfied_criteria_mask != 0 && !results->had_prompted_already) { | |
| 273 memento_in_prefs_.StoreValue(results->memento_value_in_prefs); | |
| 274 memento_in_local_state_.StoreValue(results->memento_value_in_local_state); | |
| 275 memento_in_file_.StoreValue(results->memento_value_in_file); | |
| 276 | |
| 277 if (ShouldPerformLiveRun()) { | |
| 278 delegate_->ShowPrompt(); | |
| 279 } else { | |
| 280 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult", | |
| 281 PROMPT_NOT_SHOWN, | |
| 282 PROMPT_RESULT_MAX); | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 state_ = STATE_DONE; | |
| 287 } | |
| 288 | |
| 289 void AutomaticProfileResetter::SetHashSeedForTesting( | |
| 290 const base::StringPiece& hash_key) { | |
| 291 hash_seed_ = hash_key; | |
| 292 } | |
| 293 | |
| 294 void AutomaticProfileResetter::SetProgramForTesting( | |
| 295 const base::StringPiece& program) { | |
| 296 program_ = program; | |
| 297 } | |
| 298 | |
| 299 void AutomaticProfileResetter::SetDelegateForTesting( | |
| 300 AutomaticProfileResetterDelegate* delegate) { | |
| 301 delegate_.reset(delegate); | |
| 302 } | |
| 303 | |
| 304 void AutomaticProfileResetter::Shutdown() { | |
| 305 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 306 | |
| 307 state_ = STATE_DISABLED; | |
| 308 delegate_.reset(); | |
| 309 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 310 } | |
| OLD | NEW |