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/profiles/profile.h" | |
16 #include "content/public/browser/browser_thread.h" | |
17 #include "grit/browser_resources.h" | |
18 #include "ui/base/resource/resource_bundle.h" | |
19 | |
20 namespace profile_resetter { | |
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 kAutomaticProfileResetStudyEnabledroupName[] = "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 // 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.
| |
65 class AutomaticProfileResetterDelegateImpl | |
66 : public AutomaticProfileResetterDelegate { | |
67 public: | |
68 AutomaticProfileResetterDelegateImpl() {} | |
69 virtual ~AutomaticProfileResetterDelegateImpl() {} | |
70 | |
71 // AutomaticProfileResetterDelegate overrides: | |
72 | |
73 virtual void ShowPrompt() OVERRIDE { | |
74 // TODO(engedy): Call the UI from here once we have it. | |
75 } | |
76 | |
77 virtual void ReportStatistics(uint32 satisfied_criteria_mask, | |
78 uint32 combined_status_mask) OVERRIDE { | |
79 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", | |
80 satisfied_criteria_mask, | |
81 kSatisfiedCriteriaMaskMaximumValue); | |
82 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", | |
83 combined_status_mask, | |
84 kCombinedStatusMaskMaximumValue); | |
85 } | |
86 | |
87 private: | |
88 DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateImpl); | |
89 }; | |
90 | |
91 // 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.
| |
92 class FakeInterpreter { | |
93 public: | |
94 FakeInterpreter() {} | |
95 ~FakeInterpreter() {} | |
96 | |
97 bool GetOutputBoolean(const std::string& key, bool* target) { return false; } | |
98 | |
99 bool GetOutputString(const std::string& key, std::string* target) { | |
100 return false; | |
101 } | |
102 | |
103 private: | |
104 DISALLOW_COPY_AND_ASSIGN(FakeInterpreter); | |
105 }; | |
106 | |
107 // Enumeration of the possible outcomes of showing the profile reset prompt. | |
108 enum PromptResult { | |
109 // Prompt was not shown because only a dry-run was performed. | |
110 PROMPT_NOT_SHOWN, | |
111 PROMPT_ACTION_RESET, | |
112 PROMPT_ACTION_NO_RESET, | |
113 PROMPT_DISMISSED, | |
114 // Prompt was still shown (not dismissed by the user) when Chrome was closed. | |
115 PROMPT_IGNORED, | |
116 PROMPT_RESULT_MAX | |
117 }; | |
118 | |
119 } // namespace | |
120 | |
121 // AutomaticProfileResetter::EvaluationResults ------------------------------- | |
122 | |
123 // Encapsulates the output values extracted from the evaluator program. | |
124 struct AutomaticProfileResetter::EvaluationResults { | |
125 EvaluationResults() | |
126 : had_prompted_already(false), | |
127 satisfied_criteria_mask(0), | |
128 combined_status_mask(0) {} | |
129 | |
130 std::string memento_value_in_prefs; | |
131 std::string memento_value_in_local_state; | |
132 std::string memento_value_in_file; | |
133 | |
134 bool had_prompted_already; | |
135 uint32 satisfied_criteria_mask; | |
136 uint32 combined_status_mask; | |
137 }; | |
138 | |
139 // AutomaticProfileResetter -------------------------------------------------- | |
140 | |
141 AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile) | |
142 : profile_(profile), | |
143 state_(STATE_UNINITIALIZED), | |
144 memento_in_prefs_(profile_), | |
145 memento_in_local_state_(profile_), | |
146 memento_in_file_(profile_), | |
147 weak_ptr_factory_(this) { | |
148 DCHECK(profile_); | |
149 } | |
150 | |
151 AutomaticProfileResetter::~AutomaticProfileResetter() {} | |
152 | |
153 void AutomaticProfileResetter::Initialize() { | |
154 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
155 DCHECK_EQ(state_, STATE_UNINITIALIZED); | |
156 | |
157 if (IsDryRun() || IsLiveRun()) { | |
158 ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance()); | |
159 if (IsLiveRun()) { | |
160 program_ = | |
161 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES); | |
162 hash_seed_ = | |
163 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED); | |
164 } else { | |
165 program_ = | |
166 resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY); | |
167 hash_seed_ = resources.GetRawDataResource( | |
168 IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY); | |
169 } | |
170 delegate_.reset(new AutomaticProfileResetterDelegateImpl()); | |
171 state_ = STATE_READY; | |
172 | |
173 content::BrowserThread::PostTask( | |
174 content::BrowserThread::UI, | |
175 FROM_HERE, | |
176 base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow, | |
177 weak_ptr_factory_.GetWeakPtr())); | |
178 } else { | |
179 state_ = STATE_DISABLED; | |
180 } | |
181 } | |
182 | |
183 bool AutomaticProfileResetter::IsDryRun() const { | |
184 return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == | |
185 kAutomaticProfileResetStudyDryRunGroupName; | |
186 } | |
187 | |
188 bool AutomaticProfileResetter::IsLiveRun() const { | |
189 return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) == | |
190 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.
| |
191 } | |
192 | |
193 void AutomaticProfileResetter::BeginEvaluationFlow() { | |
194 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
195 DCHECK_EQ(state_, STATE_READY); | |
196 | |
197 state_ = STATE_WORKING; | |
198 | |
199 memento_in_file_.ReadValue( | |
200 base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow, | |
201 weak_ptr_factory_.GetWeakPtr())); | |
202 } | |
203 | |
204 scoped_ptr<base::DictionaryValue> | |
205 AutomaticProfileResetter::BuildEvaluatorProgramInput( | |
206 const std::string& memento_value_in_file) { | |
207 // TODO(engedy): Add any additional state that is needed here. | |
208 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); | |
209 input->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue()); | |
210 input->SetString(kMementoValueInLocalStateKey, | |
211 memento_in_local_state_.ReadValue()); | |
212 input->SetString(kMementoValueInFileKey, memento_value_in_file); | |
213 return input.Pass(); | |
214 } | |
215 | |
216 void AutomaticProfileResetter::ContinueWithEvaluationFlow( | |
217 const std::string& memento_value_in_file) { | |
218 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
219 DCHECK_EQ(state_, STATE_WORKING); | |
220 PrefService* prefs = profile_->GetPrefs(); | |
221 DCHECK(prefs); | |
222 | |
223 scoped_ptr<base::DictionaryValue> input( | |
224 BuildEvaluatorProgramInput(memento_value_in_file)); | |
225 | |
226 base::SequencedWorkerPool* blocking_pool = | |
227 content::BrowserThread::GetBlockingPool(); | |
228 scoped_refptr<base::TaskRunner> task_runner = | |
229 blocking_pool->GetTaskRunnerWithShutdownBehavior( | |
230 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
231 | |
232 base::PostTaskAndReplyWithResult( | |
233 task_runner.get(), | |
234 FROM_HERE, | |
235 base::Bind(&EvaluateConditionsOnWorkerPoolThread, | |
236 hash_seed_.as_string(), | |
237 program_.as_string(), | |
238 base::Passed(&input)), | |
239 base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow, | |
240 weak_ptr_factory_.GetWeakPtr())); | |
241 } | |
242 | |
243 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
| |
244 AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread( | |
245 const std::string& hash_key, | |
246 const std::string& program, | |
247 scoped_ptr<base::DictionaryValue> program_input) { | |
248 // TODO(engedy): Once implemented, call real interpreter from here. | |
249 scoped_ptr<EvaluationResults> results(new EvaluationResults()); | |
250 FakeInterpreter interpreter; | |
251 interpreter.GetOutputBoolean(kHadPromptedAlreadyKey, | |
252 &results->had_prompted_already); | |
253 interpreter.GetOutputString(kMementoValueInPrefsKey, | |
254 &results->memento_value_in_prefs); | |
255 interpreter.GetOutputString(kMementoValueInLocalStateKey, | |
256 &results->memento_value_in_local_state); | |
257 interpreter.GetOutputString(kMementoValueInFileKey, | |
258 &results->memento_value_in_file); | |
259 for (size_t i = 0; i < arraysize(kCombinedStatusMaskKeys); ++i) { | |
260 bool flag = false; // Default value. | |
261 interpreter.GetOutputBoolean(kCombinedStatusMaskKeys[i], &flag); | |
262 if (flag) | |
263 results->combined_status_mask |= (1 << i); | |
264 } | |
265 for (size_t i = 0; i < arraysize(kSatisfiedCriteriaMaskKeys); ++i) { | |
266 bool flag = false; // Default value. | |
267 interpreter.GetOutputBoolean(kSatisfiedCriteriaMaskKeys[i], &flag); | |
268 if (flag) | |
269 results->satisfied_criteria_mask |= (1 << i); | |
270 } | |
271 | |
272 return results.Pass(); | |
273 } | |
274 | |
275 void AutomaticProfileResetter::FinishEvaluationFlow( | |
276 scoped_ptr<EvaluationResults> results) { | |
277 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
278 DCHECK_EQ(state_, STATE_WORKING); | |
279 | |
280 delegate_->ReportStatistics(results->satisfied_criteria_mask, | |
281 results->combined_status_mask); | |
282 | |
283 if (results->satisfied_criteria_mask != 0 && !results->had_prompted_already) { | |
284 memento_in_prefs_.StoreValue(results->memento_value_in_prefs); | |
285 memento_in_local_state_.StoreValue(results->memento_value_in_local_state); | |
286 memento_in_file_.StoreValue(results->memento_value_in_file); | |
287 | |
288 if (IsLiveRun()) | |
289 delegate_->ShowPrompt(); | |
290 else | |
robertshield
2013/09/25 20:18:12
style nit: multi line statements should be wrapped
engedy
2013/09/27 23:27:34
Done.
| |
291 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult", | |
292 PROMPT_NOT_SHOWN, | |
293 PROMPT_RESULT_MAX); | |
294 } | |
295 | |
296 state_ = STATE_DONE; | |
297 } | |
298 | |
299 void AutomaticProfileResetter::SetHashSeedForTesting( | |
300 const std::string& hash_key) { | |
301 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.
| |
302 } | |
303 | |
304 void AutomaticProfileResetter::SetProgramForTesting( | |
305 const std::string& program) { | |
306 program_ = program; | |
307 } | |
308 | |
309 void AutomaticProfileResetter::SetDelegateForTesting( | |
310 AutomaticProfileResetterDelegate* delegate) { | |
311 delegate_.reset(delegate); | |
312 } | |
313 | |
314 void AutomaticProfileResetter::Shutdown() { | |
315 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
316 | |
317 state_ = STATE_DISABLED; | |
318 delegate_.reset(); | |
319 weak_ptr_factory_.InvalidateWeakPtrs(); | |
320 } | |
321 | |
322 } // namespace profile_resetter | |
OLD | NEW |