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.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/logging.h" | |
10 #include "base/memory/ref_counted.h" | |
11 #include "base/metrics/field_trial.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/metrics/sparse_histogram.h" | |
14 #include "base/prefs/pref_service.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #include "base/strings/string_util.h" | |
17 #include "base/task_runner.h" | |
18 #include "base/task_runner_util.h" | |
19 #include "base/threading/sequenced_worker_pool.h" | |
20 #include "base/time/time.h" | |
21 #include "base/timer/elapsed_timer.h" | |
22 #include "base/values.h" | |
23 #include "chrome/browser/browser_process.h" | |
24 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h" | |
25 #include "chrome/browser/profile_resetter/jtl_interpreter.h" | |
26 #include "chrome/browser/profiles/profile.h" | |
27 #include "chrome/browser/search_engines/template_url_service.h" | |
28 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
29 #include "components/variations/variations_associated_data.h" | |
30 #include "content/public/browser/browser_thread.h" | |
31 #include "grit/browser_resources.h" | |
32 #include "ui/base/resource/resource_bundle.h" | |
33 | |
34 | |
35 // Helpers ------------------------------------------------------------------- | |
36 | |
37 namespace { | |
38 | |
39 // Name constants for the field trial behind which we enable this feature. | |
40 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset"; | |
41 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun"; | |
42 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled"; | |
43 #if defined(GOOGLE_CHROME_BUILD) | |
44 const char kAutomaticProfileResetStudyProgramParameterName[] = "program"; | |
45 const char kAutomaticProfileResetStudyHashSeedParameterName[] = "hash_seed"; | |
46 #endif | |
47 | |
48 // How long to wait after start-up before unleashing the evaluation flow. | |
49 const int64 kEvaluationFlowDelayInSeconds = 55; | |
50 | |
51 // Keys used in the input dictionary of the program. | |
52 const char kDefaultSearchProviderKey[] = "default_search_provider"; | |
53 const char kDefaultSearchProviderIsUserControlledKey[] = | |
54 "default_search_provider_iuc"; | |
55 const char kLoadedModuleDigestsKey[] = "loaded_modules"; | |
56 const char kLocalStateKey[] = "local_state"; | |
57 const char kLocalStateIsUserControlledKey[] = "local_state_iuc"; | |
58 const char kSearchProvidersKey[] = "search_providers"; | |
59 const char kUserPreferencesKey[] = "preferences"; | |
60 const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc"; | |
61 | |
62 // Keys used in the output dictionary of the program. | |
63 const char kCombinedStatusMaskKeyPrefix[] = "combined_status_mask_bit"; | |
64 const char kHadPromptedAlreadyKey[] = "had_prompted_already"; | |
65 const char kShouldPromptKey[] = "should_prompt"; | |
66 const char kSatisfiedCriteriaMaskKeyPrefix[] = "satisfied_criteria_mask_bit"; | |
67 | |
68 // Keys used in both the input and output dictionary of the program. | |
69 const char kMementoValueInFileKey[] = "memento_value_in_file"; | |
70 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state"; | |
71 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs"; | |
72 | |
73 // Number of bits, and maximum value (exclusive) for the mask whose bits | |
74 // indicate which of reset criteria were satisfied. | |
75 const size_t kSatisfiedCriteriaMaskNumberOfBits = 5u; | |
76 const uint32 kSatisfiedCriteriaMaskMaximumValue = | |
77 (1u << kSatisfiedCriteriaMaskNumberOfBits); | |
78 | |
79 // Number of bits, and maximum value (exclusive) for the mask whose bits | |
80 // indicate if any of reset criteria were satisfied, and which of the mementos | |
81 // were already present. | |
82 const size_t kCombinedStatusMaskNumberOfBits = 4u; | |
83 const uint32 kCombinedStatusMaskMaximumValue = | |
84 (1u << kCombinedStatusMaskNumberOfBits); | |
85 | |
86 // Returns whether or not a dry-run shall be performed. | |
87 bool ShouldPerformDryRun() { | |
88 return StartsWithASCII( | |
89 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName), | |
90 kAutomaticProfileResetStudyDryRunGroupName, true); | |
91 } | |
92 | |
93 // Returns whether or not a live-run shall be performed. | |
94 bool ShouldPerformLiveRun() { | |
95 return StartsWithASCII( | |
96 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName), | |
97 kAutomaticProfileResetStudyEnabledGroupName, true); | |
98 } | |
99 | |
100 // If the currently active experiment group prescribes a |program| and | |
101 // |hash_seed| to use instead of the baked-in ones, retrieves those and returns | |
102 // true. Otherwise, returns false. | |
103 bool GetProgramAndHashSeedOverridesFromExperiment(std::string* program, | |
104 std::string* hash_seed) { | |
105 DCHECK(program); | |
106 DCHECK(hash_seed); | |
107 #if defined(GOOGLE_CHROME_BUILD) | |
108 std::map<std::string, std::string> params; | |
109 chrome_variations::GetVariationParams(kAutomaticProfileResetStudyName, | |
110 ¶ms); | |
111 if (params.count(kAutomaticProfileResetStudyProgramParameterName) && | |
112 params.count(kAutomaticProfileResetStudyHashSeedParameterName)) { | |
113 program->swap(params[kAutomaticProfileResetStudyProgramParameterName]); | |
114 hash_seed->swap(params[kAutomaticProfileResetStudyHashSeedParameterName]); | |
115 return true; | |
116 } | |
117 #endif | |
118 return false; | |
119 } | |
120 | |
121 // Takes |pref_name_to_value_map|, which shall be a deep-copy of all preferences | |
122 // in |source| without path expansion; and (1.) creates a sub-tree from it named | |
123 // |value_tree_key| in |target_dictionary| with path expansion, and (2.) also | |
124 // creates an isomorphic sub-tree under the key |is_user_controlled_tree_key| | |
125 // that contains only Boolean values indicating whether or not the corresponding | |
126 // preference is coming from the 'user' PrefStore. | |
127 void BuildSubTreesFromPreferences( | |
128 scoped_ptr<base::DictionaryValue> pref_name_to_value_map, | |
129 const PrefService* source, | |
130 const char* value_tree_key, | |
131 const char* is_user_controlled_tree_key, | |
132 base::DictionaryValue* target_dictionary) { | |
133 std::vector<std::string> pref_names; | |
134 pref_names.reserve(pref_name_to_value_map->size()); | |
135 for (base::DictionaryValue::Iterator it(*pref_name_to_value_map); | |
136 !it.IsAtEnd(); it.Advance()) | |
137 pref_names.push_back(it.key()); | |
138 | |
139 base::DictionaryValue* value_tree = new base::DictionaryValue; | |
140 base::DictionaryValue* is_user_controlled_tree = new base::DictionaryValue; | |
141 for (std::vector<std::string>::const_iterator it = pref_names.begin(); | |
142 it != pref_names.end(); ++it) { | |
143 scoped_ptr<base::Value> pref_value_owned; | |
144 if (pref_name_to_value_map->RemoveWithoutPathExpansion(*it, | |
145 &pref_value_owned)) { | |
146 value_tree->Set(*it, pref_value_owned.release()); | |
147 const PrefService::Preference* pref = source->FindPreference(it->c_str()); | |
148 is_user_controlled_tree->Set( | |
149 *it, new base::FundamentalValue(pref->IsUserControlled())); | |
150 } | |
151 } | |
152 target_dictionary->Set(value_tree_key, value_tree); | |
153 target_dictionary->Set(is_user_controlled_tree_key, is_user_controlled_tree); | |
154 } | |
155 | |
156 } // namespace | |
157 | |
158 | |
159 // AutomaticProfileResetter::InputBuilder ------------------------------------ | |
160 | |
161 // Collects all the information that is required by the evaluator program to | |
162 // assess whether or not the conditions for showing the reset prompt are met. | |
163 // | |
164 // This necessitates a lot of work that has to be performed on the UI thread, | |
165 // such as: accessing the Preferences, Local State, and TemplateURLService. | |
166 // In order to keep the browser responsive, the UI thread shall not be blocked | |
167 // for long consecutive periods of time. Unfortunately, we cannot reduce the | |
168 // total amount of work. Instead, what this class does is to split the work into | |
169 // shorter tasks that are posted one-at-a-time to the UI thread in a serial | |
170 // fashion, so as to give a chance to run other tasks that have accumulated in | |
171 // the meantime. | |
172 class AutomaticProfileResetter::InputBuilder | |
173 : public base::SupportsWeakPtr<InputBuilder> { | |
174 public: | |
175 typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)> | |
176 ProgramInputCallback; | |
177 | |
178 // The dependencies must have been initialized through |delegate|, i.e. the | |
179 // RequestCallback[...] methods must have already fired before calling this. | |
180 InputBuilder(Profile* profile, AutomaticProfileResetterDelegate* delegate) | |
181 : profile_(profile), | |
182 delegate_(delegate), | |
183 memento_in_prefs_(profile_), | |
184 memento_in_local_state_(profile_), | |
185 memento_in_file_(profile_) {} | |
186 ~InputBuilder() {} | |
187 | |
188 // Assembles the data required by the evaluator program into a dictionary | |
189 // format, and posts it back to the UI thread with |callback| once ready. In | |
190 // order not to block the UI thread for long consecutive periods of time, the | |
191 // work is divided into smaller tasks, see class comment above for details. | |
192 // It is safe to destroy |this| immediately from within the |callback|. | |
193 void BuildEvaluatorProgramInput(const ProgramInputCallback& callback) { | |
194 DCHECK(!data_); | |
195 DCHECK(!callback.is_null()); | |
196 data_.reset(new base::DictionaryValue); | |
197 callback_ = callback; | |
198 | |
199 AddAsyncTask(base::Bind(&InputBuilder::IncludeMementoValues, AsWeakPtr())); | |
200 AddTask(base::Bind(&InputBuilder::IncludeUserPreferences, AsWeakPtr())); | |
201 AddTask(base::Bind(&InputBuilder::IncludeLocalState, AsWeakPtr())); | |
202 AddTask(base::Bind(&InputBuilder::IncludeSearchEngines, AsWeakPtr())); | |
203 AddTask(base::Bind(&InputBuilder::IncludeLoadedModules, AsWeakPtr())); | |
204 | |
205 // Each task will post the next one. Just trigger the chain reaction. | |
206 PostNextTask(); | |
207 } | |
208 | |
209 private: | |
210 // Asynchronous task that includes memento values (or empty strings in case | |
211 // mementos are not there). | |
212 void IncludeMementoValues() { | |
213 data_->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue()); | |
214 data_->SetString(kMementoValueInLocalStateKey, | |
215 memento_in_local_state_.ReadValue()); | |
216 memento_in_file_.ReadValue(base::Bind( | |
217 &InputBuilder::IncludeFileBasedMementoCallback, AsWeakPtr())); | |
218 } | |
219 | |
220 // Called back by |memento_in_file_| once the |memento_value| has been read. | |
221 void IncludeFileBasedMementoCallback(const std::string& memento_value) { | |
222 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
223 data_->SetString(kMementoValueInFileKey, memento_value); | |
224 // As an asynchronous task, we need to take care of posting the next task. | |
225 PostNextTask(); | |
226 } | |
227 | |
228 // Task that includes all user (i.e. profile-specific) preferences, along with | |
229 // information about whether the value is coming from the 'user' PrefStore. | |
230 // This is the most expensive operation, so it is itself split into two parts. | |
231 void IncludeUserPreferences() { | |
232 PrefService* prefs = profile_->GetPrefs(); | |
233 DCHECK(prefs); | |
234 scoped_ptr<base::DictionaryValue> pref_name_to_value_map( | |
235 prefs->GetPreferenceValuesWithoutPathExpansion()); | |
236 AddTask(base::Bind(&InputBuilder::IncludeUserPreferencesPartTwo, | |
237 AsWeakPtr(), | |
238 base::Passed(&pref_name_to_value_map))); | |
239 } | |
240 | |
241 // Second part to above. | |
242 void IncludeUserPreferencesPartTwo( | |
243 scoped_ptr<base::DictionaryValue> pref_name_to_value_map) { | |
244 PrefService* prefs = profile_->GetPrefs(); | |
245 DCHECK(prefs); | |
246 BuildSubTreesFromPreferences( | |
247 pref_name_to_value_map.Pass(), | |
248 prefs, | |
249 kUserPreferencesKey, | |
250 kUserPreferencesIsUserControlledKey, | |
251 data_.get()); | |
252 } | |
253 | |
254 // Task that includes all local state (i.e. shared) preferences, along with | |
255 // information about whether the value is coming from the 'user' PrefStore. | |
256 void IncludeLocalState() { | |
257 PrefService* local_state = g_browser_process->local_state(); | |
258 DCHECK(local_state); | |
259 scoped_ptr<base::DictionaryValue> pref_name_to_value_map( | |
260 local_state->GetPreferenceValuesWithoutPathExpansion()); | |
261 BuildSubTreesFromPreferences( | |
262 pref_name_to_value_map.Pass(), | |
263 local_state, | |
264 kLocalStateKey, | |
265 kLocalStateIsUserControlledKey, | |
266 data_.get()); | |
267 } | |
268 | |
269 // Task that includes all information related to search engines. | |
270 void IncludeSearchEngines() { | |
271 scoped_ptr<base::DictionaryValue> default_search_provider_details( | |
272 delegate_->GetDefaultSearchProviderDetails()); | |
273 data_->Set(kDefaultSearchProviderKey, | |
274 default_search_provider_details.release()); | |
275 | |
276 scoped_ptr<base::ListValue> search_providers_details( | |
277 delegate_->GetPrepopulatedSearchProvidersDetails()); | |
278 data_->Set(kSearchProvidersKey, search_providers_details.release()); | |
279 | |
280 data_->SetBoolean(kDefaultSearchProviderIsUserControlledKey, | |
281 !delegate_->IsDefaultSearchProviderManaged()); | |
282 } | |
283 | |
284 // Task that includes information about loaded modules. | |
285 void IncludeLoadedModules() { | |
286 scoped_ptr<base::ListValue> loaded_module_digests( | |
287 delegate_->GetLoadedModuleNameDigests()); | |
288 data_->Set(kLoadedModuleDigestsKey, loaded_module_digests.release()); | |
289 } | |
290 | |
291 // ------------------------------------------------------------------------- | |
292 | |
293 // Adds a |task| that can do as much asynchronous processing as it wants, but | |
294 // will need to finally call PostNextTask() on the UI thread when done. | |
295 void AddAsyncTask(const base::Closure& task) { | |
296 task_queue_.push(task); | |
297 } | |
298 | |
299 // Convenience wrapper for synchronous tasks. | |
300 void SynchronousTaskWrapper(const base::Closure& task) { | |
301 base::ElapsedTimer timer; | |
302 task.Run(); | |
303 UMA_HISTOGRAM_CUSTOM_TIMES( | |
304 "AutomaticProfileReset.InputBuilder.TaskDuration", | |
305 timer.Elapsed(), | |
306 base::TimeDelta::FromMilliseconds(1), | |
307 base::TimeDelta::FromSeconds(2), | |
308 50); | |
309 PostNextTask(); | |
310 } | |
311 | |
312 // Adds a task that needs to finish synchronously. In exchange, PostNextTask() | |
313 // is called automatically when the |task| returns, and execution time is | |
314 // measured. | |
315 void AddTask(const base::Closure& task) { | |
316 task_queue_.push( | |
317 base::Bind(&InputBuilder::SynchronousTaskWrapper, AsWeakPtr(), task)); | |
318 } | |
319 | |
320 // Posts the next task from the |task_queue_|, unless it is exhausted, in | |
321 // which case it posts |callback_| to return with the results. | |
322 void PostNextTask() { | |
323 base::Closure next_task; | |
324 if (task_queue_.empty()) { | |
325 next_task = base::Bind(callback_, base::Passed(&data_)); | |
326 } else { | |
327 next_task = task_queue_.front(); | |
328 task_queue_.pop(); | |
329 } | |
330 content::BrowserThread::PostTask( | |
331 content::BrowserThread::UI, FROM_HERE, next_task); | |
332 } | |
333 | |
334 Profile* profile_; | |
335 AutomaticProfileResetterDelegate* delegate_; | |
336 ProgramInputCallback callback_; | |
337 | |
338 PreferenceHostedPromptMemento memento_in_prefs_; | |
339 LocalStateHostedPromptMemento memento_in_local_state_; | |
340 FileHostedPromptMemento memento_in_file_; | |
341 | |
342 scoped_ptr<base::DictionaryValue> data_; | |
343 std::queue<base::Closure> task_queue_; | |
344 | |
345 DISALLOW_COPY_AND_ASSIGN(InputBuilder); | |
346 }; | |
347 | |
348 | |
349 // AutomaticProfileResetter::EvaluationResults ------------------------------- | |
350 | |
351 // Encapsulates the output values extracted from the evaluator program. | |
352 struct AutomaticProfileResetter::EvaluationResults { | |
353 EvaluationResults() | |
354 : should_prompt(false), | |
355 had_prompted_already(false), | |
356 satisfied_criteria_mask(0), | |
357 combined_status_mask(0) {} | |
358 | |
359 std::string memento_value_in_prefs; | |
360 std::string memento_value_in_local_state; | |
361 std::string memento_value_in_file; | |
362 | |
363 bool should_prompt; | |
364 bool had_prompted_already; | |
365 uint32 satisfied_criteria_mask; | |
366 uint32 combined_status_mask; | |
367 }; | |
368 | |
369 | |
370 // AutomaticProfileResetter -------------------------------------------------- | |
371 | |
372 AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile) | |
373 : profile_(profile), | |
374 state_(STATE_UNINITIALIZED), | |
375 enumeration_of_loaded_modules_ready_(false), | |
376 template_url_service_ready_(false), | |
377 has_already_dismissed_prompt_(false), | |
378 should_show_reset_banner_(false), | |
379 weak_ptr_factory_(this) { | |
380 DCHECK(profile_); | |
381 } | |
382 | |
383 AutomaticProfileResetter::~AutomaticProfileResetter() {} | |
384 | |
385 void AutomaticProfileResetter::Initialize() { | |
386 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
387 DCHECK_EQ(state_, STATE_UNINITIALIZED); | |
388 | |
389 if (!ShouldPerformDryRun() && !ShouldPerformLiveRun()) { | |
390 state_ = STATE_DISABLED; | |
391 return; | |
392 } | |
393 | |
394 if (!GetProgramAndHashSeedOverridesFromExperiment(&program_, &hash_seed_)) { | |
395 ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance()); | |
396 if (ShouldPerformLiveRun()) { | |
397 program_ = resources.GetRawDataResource( | |
398 IDR_AUTOMATIC_PROFILE_RESET_RULES).as_string(); | |
399 hash_seed_ = resources.GetRawDataResource( | |
400 IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED).as_string(); | |
401 } else { // ShouldPerformDryRun() | |
402 program_ = resources.GetRawDataResource( | |
403 IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY).as_string(); | |
404 hash_seed_ = resources.GetRawDataResource( | |
405 IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY).as_string(); | |
406 } | |
407 } | |
408 | |
409 delegate_.reset(new AutomaticProfileResetterDelegateImpl( | |
410 profile_, ProfileResetter::ALL)); | |
411 task_runner_for_waiting_ = | |
412 content::BrowserThread::GetMessageLoopProxyForThread( | |
413 content::BrowserThread::UI); | |
414 | |
415 state_ = STATE_INITIALIZED; | |
416 } | |
417 | |
418 void AutomaticProfileResetter::Activate() { | |
419 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
420 DCHECK(state_ == STATE_INITIALIZED || state_ == STATE_DISABLED); | |
421 | |
422 if (state_ == STATE_INITIALIZED) { | |
423 if (!program_.empty()) { | |
424 // Some steps in the flow (e.g. loaded modules, file-based memento) are | |
425 // IO-intensive, so defer execution until some time later. | |
426 task_runner_for_waiting_->PostDelayedTask( | |
427 FROM_HERE, | |
428 base::Bind(&AutomaticProfileResetter::PrepareEvaluationFlow, | |
429 weak_ptr_factory_.GetWeakPtr()), | |
430 base::TimeDelta::FromSeconds(kEvaluationFlowDelayInSeconds)); | |
431 } else { | |
432 // Terminate early if there is no program included (nor set by tests). | |
433 state_ = STATE_DISABLED; | |
434 } | |
435 } | |
436 } | |
437 | |
438 void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback) { | |
439 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
440 DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE); | |
441 | |
442 state_ = STATE_PERFORMING_RESET; | |
443 should_show_reset_banner_ = false; | |
444 | |
445 ReportPromptResult(PROMPT_ACTION_RESET); | |
446 delegate_->TriggerProfileSettingsReset( | |
447 send_feedback, | |
448 base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted, | |
449 weak_ptr_factory_.GetWeakPtr())); | |
450 } | |
451 | |
452 void AutomaticProfileResetter::SkipProfileReset() { | |
453 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
454 DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE); | |
455 | |
456 should_show_reset_banner_ = false; | |
457 | |
458 ReportPromptResult(PROMPT_ACTION_NO_RESET); | |
459 delegate_->DismissPrompt(); | |
460 FinishResetPromptFlow(); | |
461 } | |
462 | |
463 bool AutomaticProfileResetter::IsResetPromptFlowActive() const { | |
464 return state_ == STATE_HAS_TRIGGERED_PROMPT || | |
465 state_ == STATE_HAS_SHOWN_BUBBLE; | |
466 } | |
467 | |
468 bool AutomaticProfileResetter::ShouldShowResetBanner() const { | |
469 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
470 return should_show_reset_banner_ && ShouldPerformLiveRun(); | |
471 } | |
472 | |
473 void AutomaticProfileResetter::NotifyDidShowResetBubble() { | |
474 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
475 DCHECK_EQ(state_, STATE_HAS_TRIGGERED_PROMPT); | |
476 | |
477 state_ = STATE_HAS_SHOWN_BUBBLE; | |
478 | |
479 PersistMementos(); | |
480 ReportPromptResult(PROMPT_SHOWN_BUBBLE); | |
481 } | |
482 | |
483 void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() { | |
484 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
485 | |
486 // This notification is invoked unconditionally by the WebUI, only care about | |
487 // it when the prompt flow is currently active (and not yet resetting). | |
488 if (state_ == STATE_HAS_TRIGGERED_PROMPT || | |
489 state_ == STATE_HAS_SHOWN_BUBBLE) { | |
490 has_already_dismissed_prompt_ = true; | |
491 delegate_->DismissPrompt(); | |
492 } | |
493 } | |
494 | |
495 void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog( | |
496 bool performed_reset) { | |
497 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
498 | |
499 // This notification is invoked unconditionally by the WebUI, only care about | |
500 // it when the prompt flow is currently active (and not yet resetting). | |
501 if (state_ == STATE_HAS_TRIGGERED_PROMPT || | |
502 state_ == STATE_HAS_SHOWN_BUBBLE) { | |
503 if (!has_already_dismissed_prompt_) | |
504 delegate_->DismissPrompt(); | |
505 if (state_ == STATE_HAS_TRIGGERED_PROMPT) { | |
506 PersistMementos(); | |
507 ReportPromptResult(performed_reset ? | |
508 PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET : | |
509 PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET); | |
510 } else { // if (state_ == STATE_HAS_SHOWN_PROMPT) | |
511 ReportPromptResult(performed_reset ? | |
512 PROMPT_FOLLOWED_BY_WEBUI_RESET : | |
513 PROMPT_FOLLOWED_BY_WEBUI_NO_RESET); | |
514 } | |
515 FinishResetPromptFlow(); | |
516 } | |
517 } | |
518 | |
519 void AutomaticProfileResetter::NotifyDidCloseWebUIResetBanner() { | |
520 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
521 should_show_reset_banner_ = false; | |
522 } | |
523 | |
524 void AutomaticProfileResetter::SetProgramForTesting( | |
525 const std::string& program) { | |
526 program_ = program; | |
527 } | |
528 | |
529 void AutomaticProfileResetter::SetHashSeedForTesting( | |
530 const std::string& hash_key) { | |
531 hash_seed_ = hash_key; | |
532 } | |
533 | |
534 void AutomaticProfileResetter::SetDelegateForTesting( | |
535 scoped_ptr<AutomaticProfileResetterDelegate> delegate) { | |
536 delegate_ = delegate.Pass(); | |
537 } | |
538 | |
539 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting( | |
540 const scoped_refptr<base::TaskRunner>& task_runner) { | |
541 task_runner_for_waiting_ = task_runner; | |
542 } | |
543 | |
544 void AutomaticProfileResetter::Shutdown() { | |
545 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
546 | |
547 // We better not do anything substantial at this point. The metrics service | |
548 // has already been shut down; and local state has already been commited to | |
549 // file (in the regular fashion) for the last time. | |
550 | |
551 state_ = STATE_DISABLED; | |
552 | |
553 weak_ptr_factory_.InvalidateWeakPtrs(); | |
554 delegate_.reset(); | |
555 } | |
556 | |
557 void AutomaticProfileResetter::PrepareEvaluationFlow() { | |
558 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
559 DCHECK_EQ(state_, STATE_INITIALIZED); | |
560 | |
561 state_ = STATE_WAITING_ON_DEPENDENCIES; | |
562 | |
563 delegate_->RequestCallbackWhenTemplateURLServiceIsLoaded( | |
564 base::Bind(&AutomaticProfileResetter::OnTemplateURLServiceIsLoaded, | |
565 weak_ptr_factory_.GetWeakPtr())); | |
566 delegate_->RequestCallbackWhenLoadedModulesAreEnumerated( | |
567 base::Bind(&AutomaticProfileResetter::OnLoadedModulesAreEnumerated, | |
568 weak_ptr_factory_.GetWeakPtr())); | |
569 delegate_->LoadTemplateURLServiceIfNeeded(); | |
570 delegate_->EnumerateLoadedModulesIfNeeded(); | |
571 } | |
572 | |
573 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() { | |
574 template_url_service_ready_ = true; | |
575 OnDependencyIsReady(); | |
576 } | |
577 | |
578 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() { | |
579 enumeration_of_loaded_modules_ready_ = true; | |
580 OnDependencyIsReady(); | |
581 } | |
582 | |
583 void AutomaticProfileResetter::OnDependencyIsReady() { | |
584 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
585 DCHECK_EQ(state_, STATE_WAITING_ON_DEPENDENCIES); | |
586 | |
587 if (template_url_service_ready_ && enumeration_of_loaded_modules_ready_) { | |
588 state_ = STATE_READY; | |
589 content::BrowserThread::PostTask( | |
590 content::BrowserThread::UI, | |
591 FROM_HERE, | |
592 base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow, | |
593 weak_ptr_factory_.GetWeakPtr())); | |
594 } | |
595 } | |
596 | |
597 void AutomaticProfileResetter::BeginEvaluationFlow() { | |
598 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
599 DCHECK_EQ(state_, STATE_READY); | |
600 DCHECK(!program_.empty()); | |
601 DCHECK(!input_builder_); | |
602 | |
603 state_ = STATE_EVALUATING_CONDITIONS; | |
604 | |
605 input_builder_.reset(new InputBuilder(profile_, delegate_.get())); | |
606 input_builder_->BuildEvaluatorProgramInput( | |
607 base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow, | |
608 weak_ptr_factory_.GetWeakPtr())); | |
609 } | |
610 | |
611 void AutomaticProfileResetter::ContinueWithEvaluationFlow( | |
612 scoped_ptr<base::DictionaryValue> program_input) { | |
613 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
614 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); | |
615 | |
616 input_builder_.reset(); | |
617 | |
618 base::SequencedWorkerPool* blocking_pool = | |
619 content::BrowserThread::GetBlockingPool(); | |
620 scoped_refptr<base::TaskRunner> task_runner = | |
621 blocking_pool->GetTaskRunnerWithShutdownBehavior( | |
622 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
623 | |
624 base::PostTaskAndReplyWithResult( | |
625 task_runner.get(), | |
626 FROM_HERE, | |
627 base::Bind(&EvaluateConditionsOnWorkerPoolThread, | |
628 hash_seed_, | |
629 program_, | |
630 base::Passed(&program_input)), | |
631 base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow, | |
632 weak_ptr_factory_.GetWeakPtr())); | |
633 } | |
634 | |
635 // static | |
636 scoped_ptr<AutomaticProfileResetter::EvaluationResults> | |
637 AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread( | |
638 const std::string& hash_seed, | |
639 const std::string& program, | |
640 scoped_ptr<base::DictionaryValue> program_input) { | |
641 JtlInterpreter interpreter(hash_seed, program, program_input.get()); | |
642 interpreter.Execute(); | |
643 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult", | |
644 interpreter.result(), | |
645 JtlInterpreter::RESULT_MAX); | |
646 UMA_HISTOGRAM_SPARSE_SLOWLY("AutomaticProfileReset.ProgramChecksum", | |
647 interpreter.CalculateProgramChecksum()); | |
648 | |
649 // In each case below, the respective field in result originally contains the | |
650 // default, so if the getter fails, we still have the correct value there. | |
651 scoped_ptr<EvaluationResults> results(new EvaluationResults); | |
652 interpreter.GetOutputBoolean(kShouldPromptKey, &results->should_prompt); | |
653 interpreter.GetOutputBoolean(kHadPromptedAlreadyKey, | |
654 &results->had_prompted_already); | |
655 interpreter.GetOutputString(kMementoValueInPrefsKey, | |
656 &results->memento_value_in_prefs); | |
657 interpreter.GetOutputString(kMementoValueInLocalStateKey, | |
658 &results->memento_value_in_local_state); | |
659 interpreter.GetOutputString(kMementoValueInFileKey, | |
660 &results->memento_value_in_file); | |
661 for (size_t i = 0; i < kCombinedStatusMaskNumberOfBits; ++i) { | |
662 bool flag = false; | |
663 std::string mask_i_th_bit_key = | |
664 kCombinedStatusMaskKeyPrefix + base::IntToString(i + 1); | |
665 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag) | |
666 results->combined_status_mask |= (1 << i); | |
667 } | |
668 for (size_t i = 0; i < kSatisfiedCriteriaMaskNumberOfBits; ++i) { | |
669 bool flag = false; | |
670 std::string mask_i_th_bit_key = | |
671 kSatisfiedCriteriaMaskKeyPrefix + base::IntToString(i + 1); | |
672 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag) | |
673 results->satisfied_criteria_mask |= (1 << i); | |
674 } | |
675 return results.Pass(); | |
676 } | |
677 | |
678 void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask, | |
679 uint32 combined_status_mask) { | |
680 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", | |
681 satisfied_criteria_mask, | |
682 kSatisfiedCriteriaMaskMaximumValue); | |
683 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", | |
684 combined_status_mask, | |
685 kCombinedStatusMaskMaximumValue); | |
686 } | |
687 | |
688 void AutomaticProfileResetter::FinishEvaluationFlow( | |
689 scoped_ptr<EvaluationResults> results) { | |
690 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
691 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); | |
692 | |
693 ReportStatistics(results->satisfied_criteria_mask, | |
694 results->combined_status_mask); | |
695 | |
696 if (results->should_prompt) | |
697 should_show_reset_banner_ = true; | |
698 | |
699 if (results->should_prompt && !results->had_prompted_already) { | |
700 evaluation_results_ = results.Pass(); | |
701 BeginResetPromptFlow(); | |
702 } else { | |
703 state_ = STATE_DONE; | |
704 } | |
705 } | |
706 | |
707 void AutomaticProfileResetter::BeginResetPromptFlow() { | |
708 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
709 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); | |
710 | |
711 state_ = STATE_HAS_TRIGGERED_PROMPT; | |
712 | |
713 if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) { | |
714 // Start fetching the brandcoded default settings speculatively in the | |
715 // background, so as to reduce waiting time if the user chooses to go | |
716 // through with the reset. | |
717 delegate_->FetchBrandcodedDefaultSettingsIfNeeded(); | |
718 } else { | |
719 PersistMementos(); | |
720 ReportPromptResult(PROMPT_NOT_TRIGGERED); | |
721 FinishResetPromptFlow(); | |
722 } | |
723 } | |
724 | |
725 void AutomaticProfileResetter::OnProfileSettingsResetCompleted() { | |
726 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
727 DCHECK_EQ(state_, STATE_PERFORMING_RESET); | |
728 | |
729 delegate_->DismissPrompt(); | |
730 FinishResetPromptFlow(); | |
731 } | |
732 | |
733 void AutomaticProfileResetter::ReportPromptResult(PromptResult result) { | |
734 UMA_HISTOGRAM_ENUMERATION( | |
735 "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX); | |
736 } | |
737 | |
738 void AutomaticProfileResetter::PersistMementos() { | |
739 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
740 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || | |
741 state_ == STATE_HAS_SHOWN_BUBBLE); | |
742 DCHECK(evaluation_results_); | |
743 | |
744 PreferenceHostedPromptMemento memento_in_prefs(profile_); | |
745 LocalStateHostedPromptMemento memento_in_local_state(profile_); | |
746 FileHostedPromptMemento memento_in_file(profile_); | |
747 | |
748 memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs); | |
749 memento_in_local_state.StoreValue( | |
750 evaluation_results_->memento_value_in_local_state); | |
751 memento_in_file.StoreValue(evaluation_results_->memento_value_in_file); | |
752 | |
753 evaluation_results_.reset(); | |
754 } | |
755 | |
756 void AutomaticProfileResetter::FinishResetPromptFlow() { | |
757 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
758 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || | |
759 state_ == STATE_HAS_SHOWN_BUBBLE || | |
760 state_ == STATE_PERFORMING_RESET); | |
761 DCHECK(!evaluation_results_); | |
762 | |
763 state_ = STATE_DONE; | |
764 } | |
OLD | NEW |