- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / profile_resetter / automatic_profile_resetter.cc
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/task_runner.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h"
22 #include "chrome/browser/profile_resetter/jtl_interpreter.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/search_engines/template_url_service.h"
25 #include "chrome/browser/search_engines/template_url_service_factory.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "grit/browser_resources.h"
28 #include "ui/base/resource/resource_bundle.h"
29
30
31 // AutomaticProfileResetter::EvaluationResults -------------------------------
32
33 // Encapsulates the output values extracted from the evaluator program.
34 struct AutomaticProfileResetter::EvaluationResults {
35   EvaluationResults()
36       : had_prompted_already(false),
37         satisfied_criteria_mask(0),
38         combined_status_mask(0) {}
39
40   std::string memento_value_in_prefs;
41   std::string memento_value_in_local_state;
42   std::string memento_value_in_file;
43
44   bool had_prompted_already;
45   uint32 satisfied_criteria_mask;
46   uint32 combined_status_mask;
47 };
48
49
50 // Helpers -------------------------------------------------------------------
51
52 namespace {
53
54 // Name constants for the field trial behind which we enable this feature.
55 const char kAutomaticProfileResetStudyName[] = "AutomaticProfileReset";
56 const char kAutomaticProfileResetStudyDryRunGroupName[] = "DryRun";
57 const char kAutomaticProfileResetStudyEnabledGroupName[] = "Enabled";
58
59 // How long to wait after start-up before unleashing the evaluation flow.
60 const int64 kEvaluationFlowDelayInSeconds = 55;
61
62 // Keys used in the input dictionary of the program.
63 const char kDefaultSearchProviderKey[] = "default_search_provider";
64 const char kDefaultSearchProviderIsUserControlledKey[] =
65     "default_search_provider_iuc";
66 const char kLoadedModuleDigestsKey[] = "loaded_modules";
67 const char kLocalStateKey[] = "local_state";
68 const char kLocalStateIsUserControlledKey[] = "local_state_iuc";
69 const char kSearchProvidersKey[] = "search_providers";
70 const char kUserPreferencesKey[] = "preferences";
71 const char kUserPreferencesIsUserControlledKey[] = "preferences_iuc";
72
73 // Keys used in the output dictionary of the program.
74 const char kCombinedStatusMaskKeys[][26] = {
75     "combined_status_mask_bit1", "combined_status_mask_bit2",
76     "combined_status_mask_bit3", "combined_status_mask_bit4"};
77 const char kHadPromptedAlreadyKey[] = "had_prompted_already";
78 const char kSatisfiedCriteriaMaskKeys[][29] = {"satisfied_criteria_mask_bit1",
79                                                "satisfied_criteria_mask_bit2"};
80
81 // Keys used in both the input and output dictionary of the program.
82 const char kMementoValueInFileKey[] = "memento_value_in_file";
83 const char kMementoValueInLocalStateKey[] = "memento_value_in_local_state";
84 const char kMementoValueInPrefsKey[] = "memento_value_in_prefs";
85
86 // Number of bits, and maximum value (exclusive) for the mask whose bits
87 // indicate which of reset criteria were satisfied.
88 const size_t kSatisfiedCriteriaMaskNumberOfBits = 2u;
89 const uint32 kSatisfiedCriteriaMaskMaximumValue =
90     (1u << kSatisfiedCriteriaMaskNumberOfBits);
91
92 // Number of bits, and maximum value (exclusive) for the mask whose bits
93 // indicate if any of reset criteria were satisfied, and which of the mementos
94 // were already present.
95 const size_t kCombinedStatusMaskNumberOfBits = 4u;
96 const uint32 kCombinedStatusMaskMaximumValue =
97     (1u << kCombinedStatusMaskNumberOfBits);
98
99 COMPILE_ASSERT(
100     arraysize(kSatisfiedCriteriaMaskKeys) == kSatisfiedCriteriaMaskNumberOfBits,
101     satisfied_criteria_mask_bits_mismatch);
102 COMPILE_ASSERT(
103     arraysize(kCombinedStatusMaskKeys) == kCombinedStatusMaskNumberOfBits,
104     combined_status_mask_bits_mismatch);
105
106 // Enumeration of the possible outcomes of showing the profile reset prompt.
107 enum PromptResult {
108   // Prompt was not shown because only a dry-run was performed.
109   PROMPT_NOT_SHOWN,
110   PROMPT_ACTION_RESET,
111   PROMPT_ACTION_NO_RESET,
112   PROMPT_DISMISSED,
113   // Prompt was still shown (not dismissed by the user) when Chrome was closed.
114   PROMPT_IGNORED,
115   PROMPT_RESULT_MAX
116 };
117
118 // Returns whether or not a dry-run shall be performed.
119 bool ShouldPerformDryRun() {
120   return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) ==
121          kAutomaticProfileResetStudyDryRunGroupName;
122 }
123
124 // Returns whether or not a live-run shall be performed.
125 bool ShouldPerformLiveRun() {
126   return base::FieldTrialList::FindFullName(kAutomaticProfileResetStudyName) ==
127          kAutomaticProfileResetStudyEnabledGroupName;
128 }
129
130 // Deep-copies all preferences in |source| to a sub-tree named |value_tree_key|
131 // in |target_dictionary|, with path expansion, and also creates an isomorphic
132 // sub-tree under the key |is_user_controlled_tree_key| that contains only
133 // Boolean values, indicating whether or not the corresponding preferences are
134 // coming from the 'user' PrefStore.
135 void BuildSubTreesFromPreferences(const PrefService* source,
136                                   const char* value_tree_key,
137                                   const char* is_user_controlled_tree_key,
138                                   base::DictionaryValue* target_dictionary) {
139   scoped_ptr<base::DictionaryValue> pref_name_to_value_map(
140       source->GetPreferenceValuesWithoutPathExpansion());
141   std::vector<std::string> pref_names;
142   pref_names.reserve(pref_name_to_value_map->size());
143   for (base::DictionaryValue::Iterator it(*pref_name_to_value_map);
144        !it.IsAtEnd(); it.Advance())
145     pref_names.push_back(it.key());
146
147   base::DictionaryValue* value_tree = new base::DictionaryValue;
148   base::DictionaryValue* is_user_controlled_tree = new base::DictionaryValue;
149   for (std::vector<std::string>::const_iterator it = pref_names.begin();
150        it != pref_names.end(); ++it) {
151     scoped_ptr<Value> pref_value_owned;
152     if (pref_name_to_value_map->RemoveWithoutPathExpansion(*it,
153                                                            &pref_value_owned)) {
154       value_tree->Set(*it, pref_value_owned.release());
155       const PrefService::Preference* pref = source->FindPreference(it->c_str());
156       is_user_controlled_tree->Set(
157           *it, new base::FundamentalValue(pref->IsUserControlled()));
158     }
159   }
160   target_dictionary->Set(value_tree_key, value_tree);
161   target_dictionary->Set(is_user_controlled_tree_key, is_user_controlled_tree);
162 }
163
164 }  // namespace
165
166
167 // AutomaticProfileResetter --------------------------------------------------
168
169 AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile)
170     : profile_(profile),
171       state_(STATE_UNINITIALIZED),
172       enumeration_of_loaded_modules_ready_(false),
173       template_url_service_ready_(false),
174       memento_in_prefs_(profile_),
175       memento_in_local_state_(profile_),
176       memento_in_file_(profile_),
177       weak_ptr_factory_(this) {
178   DCHECK(profile_);
179   Initialize();
180 }
181
182 AutomaticProfileResetter::~AutomaticProfileResetter() {}
183
184 void AutomaticProfileResetter::Activate() {
185   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
186   DCHECK(state_ == STATE_INITIALIZED || state_ == STATE_DISABLED);
187
188   if (state_ == STATE_INITIALIZED) {
189     if (!program_.empty()) {
190       // Some steps in the flow (e.g. loaded modules, file-based memento) are
191       // IO-intensive, so defer execution until some time later.
192       task_runner_for_waiting_->PostDelayedTask(
193           FROM_HERE,
194           base::Bind(&AutomaticProfileResetter::PrepareEvaluationFlow,
195                      weak_ptr_factory_.GetWeakPtr()),
196           base::TimeDelta::FromSeconds(kEvaluationFlowDelayInSeconds));
197     } else {
198       // Terminate early if there is no program included (nor set by tests).
199       state_ = STATE_DISABLED;
200     }
201   }
202 }
203
204 void AutomaticProfileResetter::SetHashSeedForTesting(
205     const base::StringPiece& hash_key) {
206   hash_seed_ = hash_key;
207 }
208
209 void AutomaticProfileResetter::SetProgramForTesting(
210     const base::StringPiece& program) {
211   program_ = program;
212 }
213
214 void AutomaticProfileResetter::SetDelegateForTesting(
215     scoped_ptr<AutomaticProfileResetterDelegate> delegate) {
216   delegate_ = delegate.Pass();
217 }
218
219 void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting(
220     const scoped_refptr<base::TaskRunner>& task_runner) {
221   task_runner_for_waiting_ = task_runner;
222 }
223
224 void AutomaticProfileResetter::Initialize() {
225   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
226   DCHECK_EQ(state_, STATE_UNINITIALIZED);
227
228   if (ShouldPerformDryRun() || ShouldPerformLiveRun()) {
229     ui::ResourceBundle& resources(ui::ResourceBundle::GetSharedInstance());
230     if (ShouldPerformLiveRun()) {
231       program_ =
232           resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES);
233       hash_seed_ =
234           resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED);
235     } else {  // ShouldPerformDryRun()
236       program_ =
237           resources.GetRawDataResource(IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY);
238       hash_seed_ = resources.GetRawDataResource(
239           IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY);
240     }
241     delegate_.reset(new AutomaticProfileResetterDelegateImpl(
242         TemplateURLServiceFactory::GetForProfile(profile_)));
243     task_runner_for_waiting_ =
244         content::BrowserThread::GetMessageLoopProxyForThread(
245             content::BrowserThread::UI);
246     state_ = STATE_INITIALIZED;
247   } else {
248     state_ = STATE_DISABLED;
249   }
250 }
251
252 void AutomaticProfileResetter::PrepareEvaluationFlow() {
253   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
254   DCHECK_EQ(state_, STATE_INITIALIZED);
255
256   state_ = STATE_WAITING_ON_DEPENDENCIES;
257
258   delegate_->RequestCallbackWhenTemplateURLServiceIsLoaded(
259       base::Bind(&AutomaticProfileResetter::OnTemplateURLServiceIsLoaded,
260                  weak_ptr_factory_.GetWeakPtr()));
261   delegate_->RequestCallbackWhenLoadedModulesAreEnumerated(
262       base::Bind(&AutomaticProfileResetter::OnLoadedModulesAreEnumerated,
263                  weak_ptr_factory_.GetWeakPtr()));
264   delegate_->LoadTemplateURLServiceIfNeeded();
265   delegate_->EnumerateLoadedModulesIfNeeded();
266 }
267
268 void AutomaticProfileResetter::OnTemplateURLServiceIsLoaded() {
269   template_url_service_ready_ = true;
270   OnDependencyIsReady();
271 }
272
273 void AutomaticProfileResetter::OnLoadedModulesAreEnumerated() {
274   enumeration_of_loaded_modules_ready_ = true;
275   OnDependencyIsReady();
276 }
277
278 void AutomaticProfileResetter::OnDependencyIsReady() {
279   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
280   DCHECK_EQ(state_, STATE_WAITING_ON_DEPENDENCIES);
281
282   if (template_url_service_ready_ && enumeration_of_loaded_modules_ready_) {
283     state_ = STATE_READY;
284     content::BrowserThread::PostTask(
285         content::BrowserThread::UI,
286         FROM_HERE,
287         base::Bind(&AutomaticProfileResetter::BeginEvaluationFlow,
288                    weak_ptr_factory_.GetWeakPtr()));
289   }
290 }
291
292 void AutomaticProfileResetter::BeginEvaluationFlow() {
293   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
294   DCHECK_EQ(state_, STATE_READY);
295   DCHECK(!program_.empty());
296
297   state_ = STATE_WORKING;
298   memento_in_file_.ReadValue(
299       base::Bind(&AutomaticProfileResetter::ContinueWithEvaluationFlow,
300                  weak_ptr_factory_.GetWeakPtr()));
301 }
302
303 scoped_ptr<base::DictionaryValue>
304     AutomaticProfileResetter::BuildEvaluatorProgramInput(
305     const std::string& memento_value_in_file) {
306   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
307
308   scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue);
309
310   // Include memento values (or empty strings in case mementos are not there).
311   input->SetString(kMementoValueInPrefsKey, memento_in_prefs_.ReadValue());
312   input->SetString(kMementoValueInLocalStateKey,
313                    memento_in_local_state_.ReadValue());
314   input->SetString(kMementoValueInFileKey, memento_value_in_file);
315
316   // Include all user (i.e. profile-specific) preferences, along with
317   // information about whether the value is coming from the 'user' PrefStore.
318   PrefService* prefs = profile_->GetPrefs();
319   DCHECK(prefs);
320   BuildSubTreesFromPreferences(prefs,
321                                kUserPreferencesKey,
322                                kUserPreferencesIsUserControlledKey,
323                                input.get());
324
325   // Include all local state (i.e. shared) preferences, along with information
326   // about whether the value is coming from the 'user' PrefStore.
327   PrefService* local_state = g_browser_process->local_state();
328   DCHECK(local_state);
329   BuildSubTreesFromPreferences(
330       local_state, kLocalStateKey, kLocalStateIsUserControlledKey, input.get());
331
332   // Include all information related to search engines.
333   scoped_ptr<base::DictionaryValue> default_search_provider_details(
334       delegate_->GetDefaultSearchProviderDetails());
335   input->Set(kDefaultSearchProviderKey,
336              default_search_provider_details.release());
337
338   scoped_ptr<base::ListValue> search_providers_details(
339       delegate_->GetPrepopulatedSearchProvidersDetails());
340   input->Set(kSearchProvidersKey, search_providers_details.release());
341
342   input->SetBoolean(kDefaultSearchProviderIsUserControlledKey,
343                     !delegate_->IsDefaultSearchProviderManaged());
344
345   // Include information about loaded modules.
346   scoped_ptr<base::ListValue> loaded_module_digests(
347       delegate_->GetLoadedModuleNameDigests());
348   input->Set(kLoadedModuleDigestsKey, loaded_module_digests.release());
349
350   return input.Pass();
351 }
352
353 void AutomaticProfileResetter::ContinueWithEvaluationFlow(
354     const std::string& memento_value_in_file) {
355   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
356   DCHECK_EQ(state_, STATE_WORKING);
357   PrefService* prefs = profile_->GetPrefs();
358   DCHECK(prefs);
359
360   scoped_ptr<base::DictionaryValue> input(
361       BuildEvaluatorProgramInput(memento_value_in_file));
362
363   base::SequencedWorkerPool* blocking_pool =
364       content::BrowserThread::GetBlockingPool();
365   scoped_refptr<base::TaskRunner> task_runner =
366       blocking_pool->GetTaskRunnerWithShutdownBehavior(
367           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
368
369   base::PostTaskAndReplyWithResult(
370       task_runner.get(),
371       FROM_HERE,
372       base::Bind(&EvaluateConditionsOnWorkerPoolThread,
373                  hash_seed_,
374                  program_,
375                  base::Passed(&input)),
376       base::Bind(&AutomaticProfileResetter::FinishEvaluationFlow,
377                  weak_ptr_factory_.GetWeakPtr()));
378 }
379
380 // static
381 scoped_ptr<AutomaticProfileResetter::EvaluationResults>
382     AutomaticProfileResetter::EvaluateConditionsOnWorkerPoolThread(
383     const base::StringPiece& hash_seed,
384     const base::StringPiece& program,
385     scoped_ptr<base::DictionaryValue> program_input) {
386   JtlInterpreter interpreter(
387       hash_seed.as_string(), program.as_string(), program_input.get());
388   interpreter.Execute();
389   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.InterpreterResult",
390                             interpreter.result(),
391                             JtlInterpreter::RESULT_MAX);
392
393   // In each case below, the respective field in result originally contains the
394   // default, so if the getter fails, we still have the correct value there.
395   scoped_ptr<EvaluationResults> results(new EvaluationResults);
396   interpreter.GetOutputBoolean(kHadPromptedAlreadyKey,
397                                &results->had_prompted_already);
398   interpreter.GetOutputString(kMementoValueInPrefsKey,
399                               &results->memento_value_in_prefs);
400   interpreter.GetOutputString(kMementoValueInLocalStateKey,
401                               &results->memento_value_in_local_state);
402   interpreter.GetOutputString(kMementoValueInFileKey,
403                               &results->memento_value_in_file);
404   for (size_t i = 0; i < arraysize(kCombinedStatusMaskKeys); ++i) {
405     bool flag = false;
406     if (interpreter.GetOutputBoolean(kCombinedStatusMaskKeys[i], &flag) && flag)
407       results->combined_status_mask |= (1 << i);
408   }
409   for (size_t i = 0; i < arraysize(kSatisfiedCriteriaMaskKeys); ++i) {
410     bool flag = false;
411     if (interpreter.GetOutputBoolean(kSatisfiedCriteriaMaskKeys[i], &flag) &&
412         flag)
413       results->satisfied_criteria_mask |= (1 << i);
414   }
415   return results.Pass();
416 }
417
418 void AutomaticProfileResetter::FinishEvaluationFlow(
419     scoped_ptr<EvaluationResults> results) {
420   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
421   DCHECK_EQ(state_, STATE_WORKING);
422
423   ReportStatistics(results->satisfied_criteria_mask,
424                    results->combined_status_mask);
425
426   if (results->satisfied_criteria_mask != 0 && !results->had_prompted_already) {
427     memento_in_prefs_.StoreValue(results->memento_value_in_prefs);
428     memento_in_local_state_.StoreValue(results->memento_value_in_local_state);
429     memento_in_file_.StoreValue(results->memento_value_in_file);
430
431     if (ShouldPerformLiveRun()) {
432       delegate_->ShowPrompt();
433     } else {
434       UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult",
435                                 PROMPT_NOT_SHOWN,
436                                 PROMPT_RESULT_MAX);
437     }
438   }
439
440   state_ = STATE_DONE;
441 }
442
443 void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask,
444                                                 uint32 combined_status_mask) {
445   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask",
446                             satisfied_criteria_mask,
447                             kSatisfiedCriteriaMaskMaximumValue);
448   UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask",
449                             combined_status_mask,
450                             kCombinedStatusMaskMaximumValue);
451 }
452
453 void AutomaticProfileResetter::Shutdown() {
454   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
455
456   state_ = STATE_DISABLED;
457   delegate_.reset();
458   weak_ptr_factory_.InvalidateWeakPtrs();
459 }