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