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