12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Copyright 2013 The Chromium Authors. All rights reserved.
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Use of this source code is governed by a BSD-style license that can be
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// found in the LICENSE file.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#ifndef CHROME_BROWSER_PROFILE_RESETTER_AUTOMATIC_PROFILE_RESETTER_H_
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#define CHROME_BROWSER_PROFILE_RESETTER_AUTOMATIC_PROFILE_RESETTER_H_
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include <string>
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/basictypes.h"
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/memory/ref_counted.h"
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/memory/scoped_ptr.h"
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/memory/weak_ptr.h"
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/strings/string_piece.h"
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "base/task_runner.h"
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "chrome/browser/profile_resetter/automatic_profile_resetter_mementos.h"
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#include "components/keyed_service/core/keyed_service.h"
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass AutomaticProfileResetterDelegate;
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Profile;
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennisnamespace base {
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass DictionaryValue;
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass ListValue;
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis}
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// This service is responsible for evaluating whether the criteria for showing
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// the one-time profile reset prompt are satisfied, and for potentially
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// triggering the prompt. To ensure that the prompt only appears at most once
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// for any given profile, a "memento" that the prompt has appeared is written to
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// the profile on disk; see automatic_profile_resetter_mementos.h for details.
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// The service is created automatically with the Profile and is activated right
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// away by its factory. To avoid delaying start-up, however, it will only start
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// working after a short delay.
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// All methods in this class shall be called on the UI thread, except when noted
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// otherwise.
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass AutomaticProfileResetter : public KeyedService {
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis public:
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Enumeration listing the possible outcomes of triggering the profile reset
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // prompt.
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  enum PromptResult {
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // The reset prompt was not triggered because only a dry-run was performed,
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // or because it was not supported on the current platform.
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_NOT_TRIGGERED,
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // The reset bubble actually got shown. In contrast to the wrench menu item
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // that can always be shown, the bubble might be delayed or might never be
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // shown if another bubble was shown at the time of triggering the prompt.
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // This enumeration value is usually recorded in conjunction with another
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // PromptResult, the absence of which indicates that the prompt was ignored.
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_SHOWN_BUBBLE,
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // The user selected "Reset" or "No, thanks" (respectively) directly from
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // within the bubble.
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_ACTION_RESET,
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_ACTION_NO_RESET,
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // The reset bubble was shown, then dismissed without taking definitive
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // action. Then, however, the user initiated or refrained from doing a reset
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // (respectively) from the conventional, WebUI-based reset dialog.
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_FOLLOWED_BY_WEBUI_RESET,
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_FOLLOWED_BY_WEBUI_NO_RESET,
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // The reset bubble was suppressed (not shown) because another bubble was
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // already being shown at the time. Regardless, however, the user initiated
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // or refrained from doing a reset (respectively) from the conventional,
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    // WebUI-based reset dialog.
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET,
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET,
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    PROMPT_RESULT_MAX
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  explicit AutomaticProfileResetter(Profile* profile);
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  virtual ~AutomaticProfileResetter();
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Initializes the service if it is enabled in the field trial. Otherwise,
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // skips the initialization steps, and also permanently disables the service.
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called by AutomaticProfileResetterFactory.
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  void Initialize();
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Fires up the service by unleashing the asynchronous evaluation flow, unless
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // the service has been already disabled in Initialize() or there is no
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // |program_| to run (in which case the service also gets disabled).
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called by the AutomaticProfileResetterFactory.
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  void Activate();
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called in case the user chooses to reset their profile settings from inside
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // the reset bubble. Will trigger the reset, optionally |send_feedback|, and
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // conclude the reset prompt flow.
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  void TriggerProfileReset(bool send_feedback);
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called in case the user chooses from inside the reset bubble that they do
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // not want to reset their profile settings. Will conclude the reset prompt
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // flow without setting off a reset.
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  void SkipProfileReset();
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Returns whether or not the profile reset prompt flow is currently active,
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // that is, we have triggered the prompt and are waiting for the user to take
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // definitive action (and we are not yet performing a reset).
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  bool IsResetPromptFlowActive() const;
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Returns whether or not the profile reset banner should be shown on the
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // WebUI-based settings page.
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  bool ShouldShowResetBanner() const;
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called to give notice that the reset bubble has actually been shown.
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  void NotifyDidShowResetBubble();
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // Called to give notice that the conventional, WebUI-based settings reset
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  // dialog has been opened. This will dismiss the menu item in the wrench menu.
107  // This should always be followed by a corresponding call to
108  // NotifyDidCloseWebUIResetDialog().
109  void NotifyDidOpenWebUIResetDialog();
110
111  // Called to give notice that the conventional, WebUI-based settings reset
112  // dialog has been closed, with |performed_reset| indicating whether or not a
113  // reset was requested. This is required so that we can record the appropriate
114  // PromptResult, dismiss the prompt, and conclude the reset prompt flow early
115  // without setting off any resets in the future.
116  void NotifyDidCloseWebUIResetDialog(bool performed_reset);
117
118  // Called to give notice that reset banner has been dismissed as a result of
119  // user action on the WebUI-based settings page itself.
120  void NotifyDidCloseWebUIResetBanner();
121
122  base::WeakPtr<AutomaticProfileResetter> AsWeakPtr() {
123    return weak_ptr_factory_.GetWeakPtr();
124  }
125
126  // Should be called before Activate().
127  void SetProgramForTesting(const std::string& program);
128
129  // Should be called before Activate().
130  void SetHashSeedForTesting(const std::string& hash_seed);
131
132  // Should be called before Activate().
133  void SetDelegateForTesting(
134      scoped_ptr<AutomaticProfileResetterDelegate> delegate);
135
136  // Should be called before Activate(). Sets the task runner to be used to post
137  // task |PrepareEvaluationFlow| in a delayed manner.
138  void SetTaskRunnerForWaitingForTesting(
139      const scoped_refptr<base::TaskRunner>& task_runner);
140
141  // KeyedService:
142  virtual void Shutdown() OVERRIDE;
143
144 private:
145  class InputBuilder;
146  struct EvaluationResults;
147
148  enum State {
149    STATE_UNINITIALIZED,
150    STATE_INITIALIZED,
151    STATE_DISABLED,
152    STATE_WAITING_ON_DEPENDENCIES,
153    STATE_READY,
154    STATE_EVALUATING_CONDITIONS,
155    // The reset prompt has been triggered; but the reset bubble has not yet
156    // been shown.
157    STATE_HAS_TRIGGERED_PROMPT,
158    // The reset prompt has been triggered; the reset bubble has been shown, and
159    // potentially already dismissed by the user.
160    STATE_HAS_SHOWN_BUBBLE,
161    STATE_PERFORMING_RESET,
162    STATE_DONE
163  };
164
165  // Prepares the asynchronous evaluation flow by requesting services that it
166  // depends on to make themselves ready.
167  void PrepareEvaluationFlow();
168
169  // Called back by |resetter_delegate_| when the template URL service is ready.
170  void OnTemplateURLServiceIsLoaded();
171
172  // Called back by |resetter_delegate_| when the loaded modules have been
173  // enumerated.
174  void OnLoadedModulesAreEnumerated();
175
176  // Invoked by the above two methods. Kicks off the actual evaluation flow.
177  void OnDependencyIsReady();
178
179  // Begins the asynchronous evaluation flow, which will assess whether the
180  // criteria for showing the reset prompt are met, whether we have already
181  // shown the prompt; and, in the end, will potentially trigger the prompt.
182  void BeginEvaluationFlow();
183
184  // Called by InputBuilder once it has finished assembling the |program_input|,
185  // and will continue with the evaluation flow by triggering the evaluator
186  // program on the worker thread.
187  void ContinueWithEvaluationFlow(
188      scoped_ptr<base::DictionaryValue> program_input);
189
190  // Performs the bulk of the work. Invokes the JTL interpreter to run the
191  // |program| that will evaluate whether the conditions are met for showing the
192  // reset prompt. The program will make this decision based on the state
193  // information contained in |input| in the form of key-value pairs. The
194  // program will only see hashed keys and values that are produced using
195  // |hash_seed| as a key.
196  static scoped_ptr<EvaluationResults> EvaluateConditionsOnWorkerPoolThread(
197      const std::string& hash_seed,
198      const std::string& program,
199      scoped_ptr<base::DictionaryValue> program_input);
200
201  // Reports the given metrics through UMA. Virtual, so it can be mocked out in
202  // tests to verify that the correct value are being reported.
203  virtual void ReportStatistics(uint32 satisfied_criteria_mask,
204                                uint32 combined_status_mask);
205
206  // Called back when EvaluateConditionsOnWorkerPoolThread completes executing
207  // the program with |results|. Finishes the evaluation flow, and, based on the
208  // result, potentially initiates the reset prompt flow.
209  void FinishEvaluationFlow(scoped_ptr<EvaluationResults> results);
210
211  // Begins the reset prompt flow by triggering the reset prompt, which consists
212  // of two parts: (1.) the profile reset (pop-up) bubble, and (2.) a menu item
213  // in the wrench menu (provided by a GlobalError).
214  // The flow lasts until we receive a clear indication from the user about
215  // whether or not they wish to reset their settings. This indication can come
216  // in a variety of flavors:
217  //  * taking definitive action (i.e. selecting either "Reset" or "No, thanks")
218  //    in the pop-up reset bubble itself,
219  //  * dismissing the bubble, but then selecting the wrench menu item, which
220  //    takes them to the WebUI reset dialog in chrome://settings, and then the
221  //    user can make their choice there,
222  //  * the user going to the WebUI reset dialog by themself.
223  // For the most part, the conclusion of the reset flow coincides with when the
224  // reset prompt is dismissed, with the one exception being that the prompt is
225  // closed as soon as the WebUI reset dialog is opened, we do not wait until
226  // the user actually makes a choice in that dialog.
227  void BeginResetPromptFlow();
228
229  // Called back by the ProfileResetter once resetting the profile settings has
230  // been completed, when requested by the user from inside the reset bubble.
231  // Will dismiss the prompt and conclude the reset prompt flow.
232  void OnProfileSettingsResetCompleted();
233
234  // Reports the result of triggering the prompt through UMA. Virtual, so it can
235  // be mocked out in tests to verify that the correct value is being reported.
236  virtual void ReportPromptResult(PromptResult result);
237
238  // Writes the memento values returned by the evaluation program to disk, and
239  // then destroys |evaluation_results_|.
240  void PersistMementos();
241
242  // Concludes the reset prompt flow.
243  void FinishResetPromptFlow();
244
245  Profile* profile_;
246
247  State state_;
248  bool enumeration_of_loaded_modules_ready_;
249  bool template_url_service_ready_;
250  bool has_already_dismissed_prompt_;
251
252  scoped_ptr<InputBuilder> input_builder_;
253  std::string hash_seed_;
254  std::string program_;
255
256  scoped_ptr<EvaluationResults> evaluation_results_;
257
258  bool should_show_reset_banner_;
259
260  scoped_ptr<AutomaticProfileResetterDelegate> delegate_;
261  scoped_refptr<base::TaskRunner> task_runner_for_waiting_;
262
263  base::WeakPtrFactory<AutomaticProfileResetter> weak_ptr_factory_;
264
265  DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetter);
266};
267
268#endif  // CHROME_BROWSER_PROFILE_RESETTER_AUTOMATIC_PROFILE_RESETTER_H_
269