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 } 765