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/prefs/pref_service.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_util.h" 16 #include "base/task_runner.h" 17 #include "base/task_runner_util.h" 18 #include "base/threading/sequenced_worker_pool.h" 19 #include "base/time/time.h" 20 #include "base/timer/elapsed_timer.h" 21 #include "base/values.h" 22 #include "chrome/browser/browser_process.h" 23 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h" 24 #include "chrome/browser/profile_resetter/jtl_interpreter.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/search_engines/template_url_service.h" 27 #include "chrome/browser/search_engines/template_url_service_factory.h" 28 #include "components/variations/variations_associated_data.h" 29 #include "content/public/browser/browser_thread.h" 30 #include "grit/browser_resources.h" 31 #include "ui/base/resource/resource_bundle.h" 32 33 34 // Helpers ------------------------------------------------------------------- 35 36 namespace { 37 38 // Name constants for the field trial behind which we enable this feature. 39 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset"; 40 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun"; 41 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled"; 42 #if defined(GOOGLE_CHROME_BUILD) 43 const char kAutomaticProfileResetStudyProgramParameterName[] = "program"; 44 const char kAutomaticProfileResetStudyHashSeedParameterName[] = "hash_seed"; 45 #endif 46 47 // How long to wait after start-up before unleashing the evaluation flow. 48 const int64 kEvaluationFlowDelayInSeconds = 55; 49 50 // Keys used in the input dictionary of the program. 51 const char kDefaultSearchProviderKey[] = "default_search_provider"; 52 const char kDefaultSearchProviderIsUserControlledKey[] = 53 "default_search_provider_iuc"; 54 const char kLoadedModuleDigestsKey[] = "loaded_modules"; 55 const char kLocalStateKey[] = "local_state"; 56 const char kLocalStateIsUserControlledKey[] = "local_state_iuc"; 57 const char kSearchProvidersKey[] = "search_providers"; 58 const char kUserPreferencesKey[] = "preferences"; 59 const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc"; 60 61 // Keys used in the output dictionary of the program. 62 const char kCombinedStatusMaskKeyPrefix[] = "combined_status_mask_bit"; 63 const char kHadPromptedAlreadyKey[] = "had_prompted_already"; 64 const char kShouldPromptKey[] = "should_prompt"; 65 const char kSatisfiedCriteriaMaskKeyPrefix[] = "satisfied_criteria_mask_bit"; 66 67 // Keys used in both the input and output dictionary of the program. 68 const char kMementoValueInFileKey[] = "memento_value_in_file"; 69 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state"; 70 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs"; 71 72 // Number of bits, and maximum value (exclusive) for the mask whose bits 73 // indicate which of reset criteria were satisfied. 74 const size_t kSatisfiedCriteriaMaskNumberOfBits = 5u; 75 const uint32 kSatisfiedCriteriaMaskMaximumValue = 76 (1u << kSatisfiedCriteriaMaskNumberOfBits); 77 78 // Number of bits, and maximum value (exclusive) for the mask whose bits 79 // indicate if any of reset criteria were satisfied, and which of the mementos 80 // were already present. 81 const size_t kCombinedStatusMaskNumberOfBits = 4u; 82 const uint32 kCombinedStatusMaskMaximumValue = 83 (1u << kCombinedStatusMaskNumberOfBits); 84 85 // Returns whether or not a dry-run shall be performed. 86 bool ShouldPerformDryRun() { 87 return StartsWithASCII( 88 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName), 89 kAutomaticProfileResetStudyDryRunGroupName, true); 90 } 91 92 // Returns whether or not a live-run shall be performed. 93 bool ShouldPerformLiveRun() { 94 return StartsWithASCII( 95 base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName), 96 kAutomaticProfileResetStudyEnabledGroupName, true); 97 } 98 99 // If the currently active experiment group prescribes a |program| and 100 // |hash_seed| to use instead of the baked-in ones, retrieves those and returns 101 // true. Otherwise, returns false. 102 bool GetProgramAndHashSeedOverridesFromExperiment(std::string* program, 103 std::string* hash_seed) { 104 DCHECK(program); 105 DCHECK(hash_seed); 106 #if defined(GOOGLE_CHROME_BUILD) 107 std::map<std::string, std::string> params; 108 chrome_variations::GetVariationParams(kAutomaticProfileResetStudyName, 109 ¶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<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 646 // In each case below, the respective field in result originally contains the 647 // default, so if the getter fails, we still have the correct value there. 648 scoped_ptr<EvaluationResults> results(new EvaluationResults); 649 interpreter.GetOutputBoolean(kShouldPromptKey, &results->should_prompt); 650 interpreter.GetOutputBoolean(kHadPromptedAlreadyKey, 651 &results->had_prompted_already); 652 interpreter.GetOutputString(kMementoValueInPrefsKey, 653 &results->memento_value_in_prefs); 654 interpreter.GetOutputString(kMementoValueInLocalStateKey, 655 &results->memento_value_in_local_state); 656 interpreter.GetOutputString(kMementoValueInFileKey, 657 &results->memento_value_in_file); 658 for (size_t i = 0; i < kCombinedStatusMaskNumberOfBits; ++i) { 659 bool flag = false; 660 std::string mask_i_th_bit_key = 661 kCombinedStatusMaskKeyPrefix + base::IntToString(i + 1); 662 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag) 663 results->combined_status_mask |= (1 << i); 664 } 665 for (size_t i = 0; i < kSatisfiedCriteriaMaskNumberOfBits; ++i) { 666 bool flag = false; 667 std::string mask_i_th_bit_key = 668 kSatisfiedCriteriaMaskKeyPrefix + base::IntToString(i + 1); 669 if (interpreter.GetOutputBoolean(mask_i_th_bit_key, &flag) && flag) 670 results->satisfied_criteria_mask |= (1 << i); 671 } 672 return results.Pass(); 673 } 674 675 void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask, 676 uint32 combined_status_mask) { 677 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", 678 satisfied_criteria_mask, 679 kSatisfiedCriteriaMaskMaximumValue); 680 UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", 681 combined_status_mask, 682 kCombinedStatusMaskMaximumValue); 683 } 684 685 void AutomaticProfileResetter::FinishEvaluationFlow( 686 scoped_ptr<EvaluationResults> results) { 687 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 688 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); 689 690 ReportStatistics(results->satisfied_criteria_mask, 691 results->combined_status_mask); 692 693 if (results->should_prompt) 694 should_show_reset_banner_ = true; 695 696 if (results->should_prompt && !results->had_prompted_already) { 697 evaluation_results_ = results.Pass(); 698 BeginResetPromptFlow(); 699 } else { 700 state_ = STATE_DONE; 701 } 702 } 703 704 void AutomaticProfileResetter::BeginResetPromptFlow() { 705 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 706 DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); 707 708 state_ = STATE_HAS_TRIGGERED_PROMPT; 709 710 if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) { 711 // Start fetching the brandcoded default settings speculatively in the 712 // background, so as to reduce waiting time if the user chooses to go 713 // through with the reset. 714 delegate_->FetchBrandcodedDefaultSettingsIfNeeded(); 715 } else { 716 PersistMementos(); 717 ReportPromptResult(PROMPT_NOT_TRIGGERED); 718 FinishResetPromptFlow(); 719 } 720 } 721 722 void AutomaticProfileResetter::OnProfileSettingsResetCompleted() { 723 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 724 DCHECK_EQ(state_, STATE_PERFORMING_RESET); 725 726 delegate_->DismissPrompt(); 727 FinishResetPromptFlow(); 728 } 729 730 void AutomaticProfileResetter::ReportPromptResult(PromptResult result) { 731 UMA_HISTOGRAM_ENUMERATION( 732 "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX); 733 } 734 735 void AutomaticProfileResetter::PersistMementos() { 736 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 737 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || 738 state_ == STATE_HAS_SHOWN_BUBBLE); 739 DCHECK(evaluation_results_); 740 741 PreferenceHostedPromptMemento memento_in_prefs(profile_); 742 LocalStateHostedPromptMemento memento_in_local_state(profile_); 743 FileHostedPromptMemento memento_in_file(profile_); 744 745 memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs); 746 memento_in_local_state.StoreValue( 747 evaluation_results_->memento_value_in_local_state); 748 memento_in_file.StoreValue(evaluation_results_->memento_value_in_file); 749 750 evaluation_results_.reset(); 751 } 752 753 void AutomaticProfileResetter::FinishResetPromptFlow() { 754 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 755 DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || 756 state_ == STATE_HAS_SHOWN_BUBBLE || 757 state_ == STATE_PERFORMING_RESET); 758 DCHECK(!evaluation_results_); 759 760 state_ = STATE_DONE; 761 } 762