diff options
author | engedy@chromium.org <engedy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 10:09:45 +0000 |
---|---|---|
committer | engedy@chromium.org <engedy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 10:09:45 +0000 |
commit | f02b47763087e9eab8e6969243f932e18bbdc8fb (patch) | |
tree | 526bee7e37d33f35aa5ed2b45ddfddf16b1d4131 /chrome/browser/profile_resetter | |
parent | 83651ce8dbbb01415f2e9b81ef343c50405567e5 (diff) | |
download | chromium_src-f02b47763087e9eab8e6969243f932e18bbdc8fb.zip chromium_src-f02b47763087e9eab8e6969243f932e18bbdc8fb.tar.gz chromium_src-f02b47763087e9eab8e6969243f932e18bbdc8fb.tar.bz2 |
Integrate UI and reset logic into AutomaticProfileResetter.
BUG=298036
NOTRY=true
Review URL: https://codereview.chromium.org/62193002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235309 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/profile_resetter')
9 files changed, 1273 insertions, 396 deletions
diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter.cc b/chrome/browser/profile_resetter/automatic_profile_resetter.cc index c63674f..8def451 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter.cc @@ -91,18 +91,6 @@ COMPILE_ASSERT( arraysize(kCombinedStatusMaskKeys) == kCombinedStatusMaskNumberOfBits, combined_status_mask_bits_mismatch); -// Enumeration of the possible outcomes of showing the profile reset prompt. -enum PromptResult { - // Prompt was not shown because only a dry-run was performed. - PROMPT_NOT_SHOWN, - PROMPT_ACTION_RESET, - PROMPT_ACTION_NO_RESET, - PROMPT_DISMISSED, - // Prompt was still shown (not dismissed by the user) when Chrome was closed. - PROMPT_IGNORED, - PROMPT_RESULT_MAX -}; - // Returns whether or not a dry-run shall be performed. bool ShouldPerformDryRun() { return StartsWithASCII( @@ -392,6 +380,7 @@ AutomaticProfileResetter::AutomaticProfileResetter(Profile* profile) state_(STATE_UNINITIALIZED), enumeration_of_loaded_modules_ready_(false), template_url_service_ready_(false), + has_already_dismissed_prompt_(false), weak_ptr_factory_(this) { DCHECK(profile_); } @@ -423,7 +412,7 @@ void AutomaticProfileResetter::Initialize() { } delegate_.reset(new AutomaticProfileResetterDelegateImpl( - TemplateURLServiceFactory::GetForProfile(profile_))); + profile_, ProfileResetter::ALL)); task_runner_for_waiting_ = content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::UI); @@ -451,6 +440,79 @@ void AutomaticProfileResetter::Activate() { } } +void AutomaticProfileResetter::TriggerProfileReset(bool send_feedback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE); + + state_ = STATE_PERFORMING_RESET; + + ReportPromptResult(PROMPT_ACTION_RESET); + delegate_->TriggerProfileSettingsReset( + send_feedback, + base::Bind(&AutomaticProfileResetter::OnProfileSettingsResetCompleted, + weak_ptr_factory_.GetWeakPtr())); +} + +void AutomaticProfileResetter::SkipProfileReset() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_EQ(state_, STATE_HAS_SHOWN_BUBBLE); + + ReportPromptResult(PROMPT_ACTION_NO_RESET); + delegate_->DismissPrompt(); + FinishResetPromptFlow(); +} + +bool AutomaticProfileResetter::IsResetPromptFlowActive() const { + return state_ == STATE_HAS_TRIGGERED_PROMPT || + state_ == STATE_HAS_SHOWN_BUBBLE; +} + +void AutomaticProfileResetter::NotifyDidShowResetBubble() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_EQ(state_, STATE_HAS_TRIGGERED_PROMPT); + + state_ = STATE_HAS_SHOWN_BUBBLE; + + PersistMementos(); + ReportPromptResult(PROMPT_SHOWN_BUBBLE); +} + +void AutomaticProfileResetter::NotifyDidOpenWebUIResetDialog() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + // This notification is invoked unconditionally by the WebUI, only care about + // it when the prompt flow is currently active (and not yet resetting). + if (state_ == STATE_HAS_TRIGGERED_PROMPT || + state_ == STATE_HAS_SHOWN_BUBBLE) { + has_already_dismissed_prompt_ = true; + delegate_->DismissPrompt(); + } +} + +void AutomaticProfileResetter::NotifyDidCloseWebUIResetDialog( + bool performed_reset) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + // This notification is invoked unconditionally by the WebUI, only care about + // it when the prompt flow is currently active (and not yet resetting). + if (state_ == STATE_HAS_TRIGGERED_PROMPT || + state_ == STATE_HAS_SHOWN_BUBBLE) { + if (!has_already_dismissed_prompt_) + delegate_->DismissPrompt(); + if (state_ == STATE_HAS_TRIGGERED_PROMPT) { + PersistMementos(); + ReportPromptResult(performed_reset ? + PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET : + PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET); + } else { // if (state_ == STATE_HAS_SHOWN_PROMPT) + ReportPromptResult(performed_reset ? + PROMPT_FOLLOWED_BY_WEBUI_RESET : + PROMPT_FOLLOWED_BY_WEBUI_NO_RESET); + } + FinishResetPromptFlow(); + } +} + void AutomaticProfileResetter::SetProgramForTesting( const std::string& program) { program_ = program; @@ -471,6 +533,19 @@ void AutomaticProfileResetter::SetTaskRunnerForWaitingForTesting( task_runner_for_waiting_ = task_runner; } +void AutomaticProfileResetter::Shutdown() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + // We better not do anything substantial at this point. The metrics service + // has already been shut down; and local state has already been commited to + // file (in the regular fashion) for the last time. + + state_ = STATE_DISABLED; + + weak_ptr_factory_.InvalidateWeakPtrs(); + delegate_.reset(); +} + void AutomaticProfileResetter::PrepareEvaluationFlow() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK_EQ(state_, STATE_INITIALIZED); @@ -517,7 +592,7 @@ void AutomaticProfileResetter::BeginEvaluationFlow() { DCHECK(!program_.empty()); DCHECK(!input_builder_); - state_ = STATE_WORKING; + state_ = STATE_EVALUATING_CONDITIONS; input_builder_.reset(new InputBuilder(profile_, delegate_.get())); input_builder_->BuildEvaluatorProgramInput( @@ -528,7 +603,7 @@ void AutomaticProfileResetter::BeginEvaluationFlow() { void AutomaticProfileResetter::ContinueWithEvaluationFlow( scoped_ptr<base::DictionaryValue> program_input) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - DCHECK_EQ(state_, STATE_WORKING); + DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); input_builder_.reset(); @@ -586,49 +661,87 @@ scoped_ptr<AutomaticProfileResetter::EvaluationResults> return results.Pass(); } +void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask, + uint32 combined_status_mask) { + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", + satisfied_criteria_mask, + kSatisfiedCriteriaMaskMaximumValue); + UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", + combined_status_mask, + kCombinedStatusMaskMaximumValue); +} + void AutomaticProfileResetter::FinishEvaluationFlow( scoped_ptr<EvaluationResults> results) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - DCHECK_EQ(state_, STATE_WORKING); + DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); ReportStatistics(results->satisfied_criteria_mask, results->combined_status_mask); if (results->satisfied_criteria_mask != 0 && !results->had_prompted_already) { - PreferenceHostedPromptMemento memento_in_prefs(profile_); - LocalStateHostedPromptMemento memento_in_local_state(profile_); - FileHostedPromptMemento memento_in_file(profile_); - - memento_in_prefs.StoreValue(results->memento_value_in_prefs); - memento_in_local_state.StoreValue(results->memento_value_in_local_state); - memento_in_file.StoreValue(results->memento_value_in_file); + evaluation_results_ = results.Pass(); + BeginResetPromptFlow(); + } else { + state_ = STATE_DONE; + } +} - if (ShouldPerformLiveRun()) { - delegate_->ShowPrompt(); - } else { - UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.PromptResult", - PROMPT_NOT_SHOWN, - PROMPT_RESULT_MAX); - } +void AutomaticProfileResetter::BeginResetPromptFlow() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_EQ(state_, STATE_EVALUATING_CONDITIONS); + + state_ = STATE_HAS_TRIGGERED_PROMPT; + + if (ShouldPerformLiveRun() && delegate_->TriggerPrompt()) { + // Start fetching the brandcoded default settings speculatively in the + // background, so as to reduce waiting time if the user chooses to go + // through with the reset. + delegate_->FetchBrandcodedDefaultSettingsIfNeeded(); + } else { + PersistMementos(); + ReportPromptResult(PROMPT_NOT_TRIGGERED); + FinishResetPromptFlow(); } +} - state_ = STATE_DONE; +void AutomaticProfileResetter::OnProfileSettingsResetCompleted() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK_EQ(state_, STATE_PERFORMING_RESET); + + delegate_->DismissPrompt(); + FinishResetPromptFlow(); } -void AutomaticProfileResetter::ReportStatistics(uint32 satisfied_criteria_mask, - uint32 combined_status_mask) { - UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.SatisfiedCriteriaMask", - satisfied_criteria_mask, - kSatisfiedCriteriaMaskMaximumValue); - UMA_HISTOGRAM_ENUMERATION("AutomaticProfileReset.CombinedStatusMask", - combined_status_mask, - kCombinedStatusMaskMaximumValue); +void AutomaticProfileResetter::ReportPromptResult(PromptResult result) { + UMA_HISTOGRAM_ENUMERATION( + "AutomaticProfileReset.PromptResult", result, PROMPT_RESULT_MAX); } -void AutomaticProfileResetter::Shutdown() { +void AutomaticProfileResetter::PersistMementos() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || + state_ == STATE_HAS_SHOWN_BUBBLE); + DCHECK(evaluation_results_); - state_ = STATE_DISABLED; - delegate_.reset(); - weak_ptr_factory_.InvalidateWeakPtrs(); + PreferenceHostedPromptMemento memento_in_prefs(profile_); + LocalStateHostedPromptMemento memento_in_local_state(profile_); + FileHostedPromptMemento memento_in_file(profile_); + + memento_in_prefs.StoreValue(evaluation_results_->memento_value_in_prefs); + memento_in_local_state.StoreValue( + evaluation_results_->memento_value_in_local_state); + memento_in_file.StoreValue(evaluation_results_->memento_value_in_file); + + evaluation_results_.reset(); +} + +void AutomaticProfileResetter::FinishResetPromptFlow() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(state_ == STATE_HAS_TRIGGERED_PROMPT || + state_ == STATE_HAS_SHOWN_BUBBLE || + state_ == STATE_PERFORMING_RESET); + DCHECK(!evaluation_results_); + + state_ = STATE_DONE; } diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter.h b/chrome/browser/profile_resetter/automatic_profile_resetter.h index a7a2296..44513da 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter.h +++ b/chrome/browser/profile_resetter/automatic_profile_resetter.h @@ -36,6 +36,36 @@ class ListValue; // otherwise. class AutomaticProfileResetter : public BrowserContextKeyedService { public: + // Enumeration listing the possible outcomes of triggering the profile reset + // prompt. + enum PromptResult { + // The reset prompt was not triggered because only a dry-run was performed, + // or because it was not supported on the current platform. + PROMPT_NOT_TRIGGERED, + // The reset bubble actually got shown. In contrast to the wrench menu item + // that can always be shown, the bubble might be delayed or might never be + // shown if another bubble was shown at the time of triggering the prompt. + // This enumeration value is usually recorded in conjunction with another + // PromptResult, the absence of which indicates that the prompt was ignored. + PROMPT_SHOWN_BUBBLE, + // The user selected "Reset" or "No, thanks" (respectively) directly from + // within the bubble. + PROMPT_ACTION_RESET, + PROMPT_ACTION_NO_RESET, + // The reset bubble was shown, then dismissed without taking definitive + // action. Then, however, the user initiated or refrained from doing a reset + // (respectively) from the conventional, WebUI-based reset dialog. + PROMPT_FOLLOWED_BY_WEBUI_RESET, + PROMPT_FOLLOWED_BY_WEBUI_NO_RESET, + // The reset bubble was suppressed (not shown) because another bubble was + // already being shown at the time. Regardless, however, the user initiated + // or refrained from doing a reset (respectively) from the conventional, + // WebUI-based reset dialog. + PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET, + PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET, + PROMPT_RESULT_MAX + }; + explicit AutomaticProfileResetter(Profile* profile); virtual ~AutomaticProfileResetter(); @@ -50,6 +80,41 @@ class AutomaticProfileResetter : public BrowserContextKeyedService { // Called by the AutomaticProfileResetterFactory. void Activate(); + // Called in case the user chooses to reset their profile settings from inside + // the reset bubble. Will trigger the reset, optionally |send_feedback|, and + // conclude the reset prompt flow. + void TriggerProfileReset(bool send_feedback); + + // Called in case the user chooses from inside the reset bubble that they do + // not want to reset their profile settings. Will conclude the reset prompt + // flow without setting off a reset. + void SkipProfileReset(); + + // Returns whether or not the profile reset prompt flow is currently active, + // that is, we have triggered the prompt and are waiting for the user to take + // definitive action (and we are not yet performing a reset). + bool IsResetPromptFlowActive() const; + + // Called to give notice that the reset bubble has actually been shown. + void NotifyDidShowResetBubble(); + + // Called to give notice that the conventional, WebUI-based settings reset + // dialog has been opened. This will dismiss the menu item in the wrench menu. + // This should always be followed by a corresponding call to + // NotifyDidCloseWebUIResetDialog(). + void NotifyDidOpenWebUIResetDialog(); + + // Called to give notice that the conventional, WebUI-based settings reset + // dialog has been closed, with |performed_reset| indicating whether or not a + // reset was requested. This is required so that we can record the appropriate + // PromptResult, dismiss the prompt, and conclude the reset prompt flow early + // without setting off any resets in the future. + void NotifyDidCloseWebUIResetDialog(bool performed_reset); + + base::WeakPtr<AutomaticProfileResetter> AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + // Should be called before Activate(). void SetProgramForTesting(const std::string& program); @@ -65,6 +130,9 @@ class AutomaticProfileResetter : public BrowserContextKeyedService { void SetTaskRunnerForWaitingForTesting( const scoped_refptr<base::TaskRunner>& task_runner); + // BrowserContextKeyedService: + virtual void Shutdown() OVERRIDE; + private: class InputBuilder; struct EvaluationResults; @@ -75,7 +143,14 @@ class AutomaticProfileResetter : public BrowserContextKeyedService { STATE_DISABLED, STATE_WAITING_ON_DEPENDENCIES, STATE_READY, - STATE_WORKING, + STATE_EVALUATING_CONDITIONS, + // The reset prompt has been triggered; but the reset bubble has not yet + // been shown. + STATE_HAS_TRIGGERED_PROMPT, + // The reset prompt has been triggered; the reset bubble has been shown, and + // potentially already dismissed by the user. + STATE_HAS_SHOWN_BUBBLE, + STATE_PERFORMING_RESET, STATE_DONE }; @@ -115,29 +190,63 @@ class AutomaticProfileResetter : public BrowserContextKeyedService { const std::string& program, scoped_ptr<base::DictionaryValue> program_input); - // Called back when EvaluateConditionsOnWorkerPoolThread() completes executing - // the program with |results|. Finishes the evaluation flow, and, based on the - // result, will potentially show the reset prompt. - void FinishEvaluationFlow(scoped_ptr<EvaluationResults> results); - // Reports the given metrics through UMA. Virtual, so it can be mocked out in // tests to verify that the correct value are being reported. virtual void ReportStatistics(uint32 satisfied_criteria_mask, uint32 combined_status_mask); - // BrowserContextKeyedService: - virtual void Shutdown() OVERRIDE; + // Called back when EvaluateConditionsOnWorkerPoolThread completes executing + // the program with |results|. Finishes the evaluation flow, and, based on the + // result, potentially initiates the reset prompt flow. + void FinishEvaluationFlow(scoped_ptr<EvaluationResults> results); + + // Begins the reset prompt flow by triggering the reset prompt, which consists + // of two parts: (1.) the profile reset (pop-up) bubble, and (2.) a menu item + // in the wrench menu (provided by a GlobalError). + // The flow lasts until we receive a clear indication from the user about + // whether or not they wish to reset their settings. This indication can come + // in a variety of flavors: + // * taking definitive action (i.e. selecting either "Reset" or "No, thanks") + // in the pop-up reset bubble itself, + // * dismissing the bubble, but then selecting the wrench menu item, which + // takes them to the WebUI reset dialog in chrome://settings, and then the + // user can make their choice there, + // * the user going to the WebUI reset dialog by themself. + // For the most part, the conclusion of the reset flow coincides with when the + // reset prompt is dismissed, with the one exception being that the prompt is + // closed as soon as the WebUI reset dialog is opened, we do not wait until + // the user actually makes a choice in that dialog. + void BeginResetPromptFlow(); + + // Called back by the ProfileResetter once resetting the profile settings has + // been completed, when requested by the user from inside the reset bubble. + // Will dismiss the prompt and conclude the reset prompt flow. + void OnProfileSettingsResetCompleted(); + + // Reports the result of triggering the prompt through UMA. Virtual, so it can + // be mocked out in tests to verify that the correct value is being reported. + virtual void ReportPromptResult(PromptResult result); + + // Writes the memento values returned by the evaluation program to disk, and + // then destroys |evaluation_results_|. + void PersistMementos(); + + // Concludes the reset prompt flow. + void FinishResetPromptFlow(); Profile* profile_; State state_; bool enumeration_of_loaded_modules_ready_; bool template_url_service_ready_; + bool has_already_dismissed_prompt_; scoped_ptr<InputBuilder> input_builder_; std::string hash_seed_; std::string program_; + scoped_ptr<EvaluationResults> evaluation_results_; + scoped_ptr<AutomaticProfileResetterDelegate> delegate_; scoped_refptr<base::TaskRunner> task_runner_for_waiting_; diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc index e793669..63e4920 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc @@ -6,6 +6,8 @@ #include <string> +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback.h" #include "base/logging.h" #include "base/md5.h" @@ -13,11 +15,21 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/values.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/google/google_util.h" +#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h" +#include "chrome/browser/profile_resetter/profile_reset_global_error.h" +#include "chrome/browser/profile_resetter/profile_resetter.h" +#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/search_engines/template_url_service.h" #include "chrome/browser/search_engines/template_url_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/global_error/global_error_service.h" +#include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" @@ -95,8 +107,13 @@ void ExtractLoadedModuleNameDigests( // AutomaticProfileResetterDelegateImpl -------------------------------------- AutomaticProfileResetterDelegateImpl::AutomaticProfileResetterDelegateImpl( - TemplateURLService* template_url_service) - : template_url_service_(template_url_service) { + Profile* profile, + ProfileResetter::ResettableFlags resettable_aspects) + : profile_(profile), + global_error_service_(GlobalErrorServiceFactory::GetForProfile(profile_)), + template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)), + resettable_aspects_(resettable_aspects) { + DCHECK(profile_); if (template_url_service_) { template_url_service_->AddObserver(this); // Needed so that |template_url_service_ready_event_| will be signaled even @@ -151,6 +168,34 @@ void AutomaticProfileResetterDelegateImpl:: template_url_service_ready_event_.Post(FROM_HERE, ready_callback); } +void AutomaticProfileResetterDelegateImpl:: + FetchBrandcodedDefaultSettingsIfNeeded() { + if (brandcoded_config_fetcher_ || + brandcoded_defaults_fetched_event_.is_signaled()) + return; + + std::string brandcode; + google_util::GetBrand(&brandcode); + if (brandcode.empty()) { + brandcoded_defaults_.reset(new BrandcodedDefaultSettings); + brandcoded_defaults_fetched_event_.Signal(); + } else { + brandcoded_config_fetcher_.reset(new BrandcodeConfigFetcher( + base::Bind( + &AutomaticProfileResetterDelegateImpl::OnBrandcodedDefaultsFetched, + base::Unretained(this)), + GURL("https://tools.google.com/service/update2"), + brandcode)); + } +} + +void AutomaticProfileResetterDelegateImpl:: + RequestCallbackWhenBrandcodedDefaultsAreFetched( + const base::Closure& ready_callback) const { + DCHECK(!ready_callback.is_null()); + brandcoded_defaults_fetched_event_.Post(FROM_HERE, ready_callback); +} + scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl:: GetLoadedModuleNameDigests() const { DCHECK(modules_have_been_enumerated_event_.is_signaled()); @@ -188,7 +233,6 @@ bool AutomaticProfileResetterDelegateImpl:: scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl:: GetPrepopulatedSearchProvidersDetails() const { - DCHECK(template_url_service_); size_t default_search_index = 0; ScopedVector<TemplateURL> engines( TemplateURLPrepopulateData::GetPrepopulatedEngines( @@ -200,8 +244,47 @@ scoped_ptr<base::ListValue> AutomaticProfileResetterDelegateImpl:: return engines_details_list.Pass(); } -void AutomaticProfileResetterDelegateImpl::ShowPrompt() { - // TODO(engedy): Call the UI from here once we have it. +bool AutomaticProfileResetterDelegateImpl::TriggerPrompt() { + DCHECK(global_error_service_); + + if (!ProfileResetGlobalError::IsSupportedOnPlatform()) + return false; + + ProfileResetGlobalError* global_error = new ProfileResetGlobalError(profile_); + global_error_service_->AddGlobalError(global_error); + + // Do not try to show bubble if another GlobalError is already showing one. + const GlobalErrorService::GlobalErrorList& global_errors( + global_error_service_->errors()); + GlobalErrorService::GlobalErrorList::const_iterator it; + for (it = global_errors.begin(); it != global_errors.end(); ++it) { + if ((*it)->GetBubbleView()) + break; + } + if (it == global_errors.end()) { + Browser* browser = chrome::FindTabbedBrowser( + profile_, + false /*match_original_profiles*/, + chrome::GetActiveDesktop()); + if (browser) + global_error->ShowBubbleView(browser); + } + return true; +} + +void AutomaticProfileResetterDelegateImpl::TriggerProfileSettingsReset( + bool send_feedback, + const base::Closure& completion) { + DCHECK(!profile_resetter_); + DCHECK(!completion.is_null()); + + profile_resetter_.reset(new ProfileResetter(profile_)); + FetchBrandcodedDefaultSettingsIfNeeded(); + RequestCallbackWhenBrandcodedDefaultsAreFetched(base::Bind( + &AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset, + AsWeakPtr(), + send_feedback, + completion)); } void AutomaticProfileResetterDelegateImpl::OnTemplateURLServiceChanged() { @@ -212,6 +295,18 @@ void AutomaticProfileResetterDelegateImpl::OnTemplateURLServiceChanged() { template_url_service_ready_event_.Signal(); } +void AutomaticProfileResetterDelegateImpl::DismissPrompt() { + DCHECK(global_error_service_); + GlobalError* global_error = + global_error_service_->GetGlobalErrorByMenuItemCommandID( + IDC_SHOW_SETTINGS_RESET_BUBBLE); + if (global_error) { + // This will also close/destroy the Bubble UI if it is currently shown. + global_error_service_->RemoveGlobalError(global_error); + delete global_error; + } +} + void AutomaticProfileResetterDelegateImpl::Observe( int type, const content::NotificationSource& source, @@ -225,3 +320,54 @@ void AutomaticProfileResetterDelegateImpl::Observe( modules_have_been_enumerated_event_.Signal(); } } + +void AutomaticProfileResetterDelegateImpl::SendFeedback( + const std::string& report) const { + SendSettingsFeedback(report, profile_, PROFILE_RESET_PROMPT); +} + +void AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset( + bool send_feedback, + const base::Closure& completion) { + DCHECK(brandcoded_defaults_); + scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot( + send_feedback ? new ResettableSettingsSnapshot(profile_) : NULL); + profile_resetter_->Reset( + resettable_aspects_, + brandcoded_defaults_.Pass(), + base::Bind(&AutomaticProfileResetterDelegateImpl:: + OnProfileSettingsResetCompleted, + AsWeakPtr(), + completion, + base::Passed(&old_settings_snapshot))); +} + +void AutomaticProfileResetterDelegateImpl:: + OnBrandcodedDefaultsFetched() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(brandcoded_config_fetcher_); + DCHECK(!brandcoded_config_fetcher_->IsActive()); + brandcoded_defaults_ = brandcoded_config_fetcher_->GetSettings(); + if (!brandcoded_defaults_) + brandcoded_defaults_.reset(new BrandcodedDefaultSettings); + brandcoded_defaults_fetched_event_.Signal(); +} + +void AutomaticProfileResetterDelegateImpl::OnProfileSettingsResetCompleted( + const base::Closure& user_callback, + scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (old_settings_snapshot) { + ResettableSettingsSnapshot new_settings_snapshot(profile_); + int difference = + old_settings_snapshot->FindDifferentFields(new_settings_snapshot); + if (difference) { + old_settings_snapshot->Subtract(new_settings_snapshot); + std::string report = + SerializeSettingsReport(*old_settings_snapshot, difference); + SendFeedback(report); + } + } + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, user_callback); +} diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h index dc8a613..7321370 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.h @@ -8,11 +8,17 @@ #include "base/basictypes.h" #include "base/callback_forward.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/profile_resetter/profile_resetter.h" #include "chrome/browser/search_engines/template_url_service_observer.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "extensions/common/one_shot_event.h" +class BrandcodeConfigFetcher; +class GlobalErrorService; +class Profile; +class ResettableSettingsSnapshot; class TemplateURLService; namespace base { @@ -44,6 +50,17 @@ class AutomaticProfileResetterDelegate { virtual void RequestCallbackWhenTemplateURLServiceIsLoaded( const base::Closure& ready_callback) const = 0; + // Starts downloading the configuration file that specifies the default + // settings for the current brandcode; or establishes that we are running a + // vanilla (non-branded) build. + virtual void FetchBrandcodedDefaultSettingsIfNeeded() = 0; + + // Requests |ready_callback| to be posted on the UI thread once the brandcoded + // default settings have been downloaded, or once it has been established that + // we are running a vanilla (non-branded) build. + virtual void RequestCallbackWhenBrandcodedDefaultsAreFetched( + const base::Closure& ready_callback) const = 0; + // Returns a list of loaded module name digests. virtual scoped_ptr<base::ListValue> GetLoadedModuleNameDigests() const = 0; @@ -69,28 +86,47 @@ class AutomaticProfileResetterDelegate { virtual scoped_ptr<base::ListValue> GetPrepopulatedSearchProvidersDetails() const = 0; - // Triggers showing the one-time profile settings reset prompt. - virtual void ShowPrompt() = 0; + // Triggers showing the one-time profile settings reset prompt, which consists + // of two parts: (1.) the profile reset (pop-up) bubble, and (2.) a menu item + // in the wrench menu. Showing the bubble might be delayed if there is another + // bubble currently shown, in which case the bubble will be shown the first + // time a new browser window is opened. + // Returns true unless the reset prompt is not supported on the current + // platform, in which case it returns false and does nothing. + virtual bool TriggerPrompt() = 0; + + // Triggers the ProfileResetter to reset all supported settings and optionally + // |send_feedback|. Will post |completion| on the UI thread once completed. + // Brandcoded default settings will be fetched if they are not yet available, + // the reset will be performed once the download is complete. + // NOTE: Should only be called at most once during the lifetime of the object. + virtual void TriggerProfileSettingsReset(bool send_feedback, + const base::Closure& completion) = 0; + + // Dismisses the one-time profile settings reset prompt, if it is currently + // triggered. Will synchronously destroy the corresponding GlobalError. + virtual void DismissPrompt() = 0; }; // Implementation for AutomaticProfileResetterDelegate. -// To facilitate unit testing, having the TemplateURLService available is only -// required when using search engine related methods. class AutomaticProfileResetterDelegateImpl : public AutomaticProfileResetterDelegate, + public base::SupportsWeakPtr<AutomaticProfileResetterDelegateImpl>, public TemplateURLServiceObserver, public content::NotificationObserver { public: - // The |template_url_service| may be NULL in unit tests. Must be non-NULL for - // callers who wish to call: - // * LoadTemplateURLServiceIfNeeded(), - // * GetDefaultSearchProviderDetails(), - // * GetPrepopulatedSearchProvidersDetails(), or - // * IsDefaultSearchManaged(). explicit AutomaticProfileResetterDelegateImpl( - TemplateURLService* template_url_service); + Profile* profile, + ProfileResetter::ResettableFlags resettable_aspects); virtual ~AutomaticProfileResetterDelegateImpl(); + // Returns the brandcoded default settings; empty defaults if this is not a + // branded build; or NULL if FetchBrandcodedDefaultSettingsIfNeeded() has not + // yet been called or not yet finished. Exposed only for unit tests. + const BrandcodedDefaultSettings* brandcoded_defaults() const { + return brandcoded_defaults_.get(); + } + // AutomaticProfileResetterDelegate: virtual void EnumerateLoadedModulesIfNeeded() OVERRIDE; virtual void RequestCallbackWhenLoadedModulesAreEnumerated( @@ -98,6 +134,9 @@ class AutomaticProfileResetterDelegateImpl virtual void LoadTemplateURLServiceIfNeeded() OVERRIDE; virtual void RequestCallbackWhenTemplateURLServiceIsLoaded( const base::Closure& ready_callback) const OVERRIDE; + virtual void FetchBrandcodedDefaultSettingsIfNeeded() OVERRIDE; + virtual void RequestCallbackWhenBrandcodedDefaultsAreFetched( + const base::Closure& ready_callback) const OVERRIDE; virtual scoped_ptr<base::ListValue> GetLoadedModuleNameDigests() const OVERRIDE; virtual scoped_ptr<base::DictionaryValue> @@ -105,7 +144,11 @@ class AutomaticProfileResetterDelegateImpl virtual bool IsDefaultSearchProviderManaged() const OVERRIDE; virtual scoped_ptr<base::ListValue> GetPrepopulatedSearchProvidersDetails() const OVERRIDE; - virtual void ShowPrompt() OVERRIDE; + virtual bool TriggerPrompt() OVERRIDE; + virtual void TriggerProfileSettingsReset( + bool send_feedback, + const base::Closure& completion) OVERRIDE; + virtual void DismissPrompt() OVERRIDE; // TemplateURLServiceObserver: virtual void OnTemplateURLServiceChanged() OVERRIDE; @@ -116,15 +159,45 @@ class AutomaticProfileResetterDelegateImpl const content::NotificationDetails& details) OVERRIDE; private: + // Sends a feedback |report|, where |report| is supposed to be result of + // ::SerializeSettingsReport(). Virtual, so it can be mocked out for tests. + virtual void SendFeedback(const std::string& report) const; + + // Triggers the ProfileResetter to reset |resettable_aspects_| and optionally + // |send_feedback|. Will invoke |completion| on the UI thread once completed + // The |brandcoded_defaults_| must already be initialized when this is called. + void RunProfileSettingsReset(bool send_feedback, + const base::Closure& completion); + + // Called by |brandcoded_config_fetcher_| when it finishes downloading the + // brandcoded default settings (or the download times out). + void OnBrandcodedDefaultsFetched(); + + // Called back by the ProfileResetter once resetting the profile settings has + // been completed. If |old_settings_snapshot| is non-NULL, will compare it to + // the new settings and send the differences to Google for analysis. Finally, + // will post |user_callback|. + void OnProfileSettingsResetCompleted( + const base::Closure& user_callback, + scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot); + + Profile* profile_; + GlobalErrorService* global_error_service_; TemplateURLService* template_url_service_; + scoped_ptr<BrandcodeConfigFetcher> brandcoded_config_fetcher_; + scoped_ptr<BrandcodedDefaultSettings> brandcoded_defaults_; + + const ProfileResetter::ResettableFlags resettable_aspects_; + scoped_ptr<ProfileResetter> profile_resetter_; + content::NotificationRegistrar registrar_; // The list of modules found. Even when |modules_have_been_enumerated_event_| // is signaled, this may still be NULL. scoped_ptr<base::ListValue> module_list_; - // This event is signaled once module enumeration has been attempted. If + // This event is signaled once module enumeration has been attempted. If // during construction, EnumerateModulesModel can supply a non-empty list of // modules, module enumeration has clearly already happened, so the event will // be signaled immediately; otherwise, when EnumerateLoadedModulesIfNeeded() @@ -137,6 +210,10 @@ class AutomaticProfileResetterDelegateImpl // the event will be signaled during construction. extensions::OneShotEvent template_url_service_ready_event_; + // This event is signaled once brandcoded default settings have been fetched, + // or once it has been established that this is not a branded build. + extensions::OneShotEvent brandcoded_defaults_fetched_event_; + DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateImpl); }; diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate_unittest.cc b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate_unittest.cc index ee89773..be2ace8 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate_unittest.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate_unittest.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" @@ -17,15 +18,25 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/values_test_util.h" #include "base/values.h" +#include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_service_unittest.h" +#include "chrome/browser/google/google_util.h" +#include "chrome/browser/profile_resetter/brandcoded_default_settings.h" +#include "chrome/browser/profile_resetter/profile_reset_global_error.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/search_engines/template_url_service.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/search_engines/template_url_service_test_util.h" +#include "chrome/browser/ui/global_error/global_error.h" +#include "chrome/browser/ui/global_error/global_error_service.h" +#include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_pref_service_syncable.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/notification_service.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/test_url_fetcher_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,88 +46,49 @@ namespace { -// Test fixtures ------------------------------------------------------------- +const char kTestBrandcode[] = "FOOBAR"; -class AutomaticProfileResetterDelegateTest : public testing::Test { - protected: - AutomaticProfileResetterDelegateTest() {} - virtual ~AutomaticProfileResetterDelegateTest() {} +const char kTestHomepage[] = "http://google.com"; +const char kTestBrandedHomepage[] = "http://example.com"; - virtual void SetUp() OVERRIDE { - resetter_delegate_.reset(new AutomaticProfileResetterDelegateImpl(NULL)); - } +const ProfileResetter::ResettableFlags kResettableAspectsForTest = + ProfileResetter::ALL & ~ProfileResetter::COOKIES_AND_SITE_DATA; - virtual void TearDown() OVERRIDE { resetter_delegate_.reset(); } +// Helpers ------------------------------------------------------------------- - AutomaticProfileResetterDelegate* resetter_delegate() { - return resetter_delegate_.get(); - } +// A testing version of the AutomaticProfileResetterDelegate that differs from +// the real one only in that it has its feedback reporting mocked out, and it +// will not reset COOKIES_AND_SITE_DATA, due to difficulties to set up some +// required URLRequestContexts in unit tests. +class AutomaticProfileResetterDelegateUnderTest + : public AutomaticProfileResetterDelegateImpl { + public: + explicit AutomaticProfileResetterDelegateUnderTest(Profile* profile) + : AutomaticProfileResetterDelegateImpl( + profile, kResettableAspectsForTest) {} + virtual ~AutomaticProfileResetterDelegateUnderTest() {} - private: - content::TestBrowserThreadBundle thread_bundle_; - scoped_ptr<AutomaticProfileResetterDelegate> resetter_delegate_; + MOCK_CONST_METHOD1(SendFeedback, void(const std::string&)); - DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateTest); + private: + DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateUnderTest); }; -class AutomaticProfileResetterDelegateTestTemplateURLs : public testing::Test { - protected: - AutomaticProfileResetterDelegateTestTemplateURLs() {} - virtual ~AutomaticProfileResetterDelegateTestTemplateURLs() {} - - virtual void SetUp() OVERRIDE { - test_util_.SetUp(); - resetter_delegate_.reset( - new AutomaticProfileResetterDelegateImpl(test_util_.model())); - } - - virtual void TearDown() OVERRIDE { - resetter_delegate_.reset(); - test_util_.TearDown(); - } - - TestingProfile* profile() { return test_util_.profile(); } - - AutomaticProfileResetterDelegate* resetter_delegate() { - return resetter_delegate_.get(); - } - - scoped_ptr<TemplateURL> CreateTestTemplateURL() { - TemplateURLData data; - - data.SetURL("http://example.com/search?q={searchTerms}"); - data.suggestions_url = "http://example.com/suggest?q={searchTerms}"; - data.instant_url = "http://example.com/instant?q={searchTerms}"; - data.image_url = "http://example.com/image?q={searchTerms}"; - data.search_url_post_params = "search-post-params"; - data.suggestions_url_post_params = "suggest-post-params"; - data.instant_url_post_params = "instant-post-params"; - data.image_url_post_params = "image-post-params"; - - data.favicon_url = GURL("http://example.com/favicon.ico"); - data.new_tab_url = "http://example.com/newtab.html"; - data.alternate_urls.push_back("http://example.com/s?q={searchTerms}"); +class MockCallbackTarget { + public: + MockCallbackTarget() {} + ~MockCallbackTarget() {} - data.short_name = base::ASCIIToUTF16("name"); - data.SetKeyword(base::ASCIIToUTF16("keyword")); - data.search_terms_replacement_key = "search-terms-replacment-key"; - data.prepopulate_id = 42; - data.input_encodings.push_back("UTF-8"); - data.safe_for_autoreplace = true; + MOCK_CONST_METHOD0(Run, void(void)); - return scoped_ptr<TemplateURL>(new TemplateURL(profile(), data)); + base::Closure CreateClosure() { + return base::Bind(&MockCallbackTarget::Run, base::Unretained(this)); } - TemplateURLServiceTestUtil test_util_; - private: - scoped_ptr<AutomaticProfileResetterDelegate> resetter_delegate_; - - DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateTestTemplateURLs); + DISALLOW_COPY_AND_ASSIGN(MockCallbackTarget); }; -// Helper classes and functions ---------------------------------------------- - // Returns the details of the default search provider from |prefs| in a format // suitable for usage as |expected_details| in ExpectDetailsMatch(). scoped_ptr<base::DictionaryValue> GetDefaultSearchProviderDetailsFromPrefs( @@ -170,32 +142,134 @@ void ExpectDetailsMatch(const base::DictionaryValue& expected_details, } } -class MockCallbackTarget { - public: - MockCallbackTarget() {} - ~MockCallbackTarget() {} +// If |simulate_failure| is false, then replies to the pending request on +// |fetcher| with a brandcoded config that only specifies a home page URL. +// If |simulate_failure| is true, replies with 404. +void ServicePendingBrancodedConfigFetch(net::TestURLFetcher* fetcher, + bool simulate_failure) { + const char kBrandcodedXmlSettings[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<response protocol=\"3.0\" server=\"prod\">" + "<app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\">" + "<data index=\"skipfirstrunui-importsearch-defaultbrowser\" " + "name=\"install\" status=\"ok\">" + "{\"homepage\" : \"$1\"}" + "</data>" + "</app>" + "</response>"; + + fetcher->set_response_code(simulate_failure ? 404 : 200); + scoped_refptr<net::HttpResponseHeaders> response_headers( + new net::HttpResponseHeaders("")); + response_headers->AddHeader("Content-Type: text/xml"); + fetcher->set_response_headers(response_headers); + if (!simulate_failure) { + std::string response(kBrandcodedXmlSettings); + size_t placeholder_index = response.find("$1"); + ASSERT_NE(std::string::npos, placeholder_index); + response.replace(placeholder_index, 2, kTestBrandedHomepage); + fetcher->SetResponseString(response); + } + fetcher->delegate()->OnURLFetchComplete(fetcher); +} - MOCK_CONST_METHOD0(Run, void(void)); - base::Closure CreateClosure() { - return base::Closure( - base::Bind(&MockCallbackTarget::Run, base::Unretained(this))); +// Test fixture -------------------------------------------------------------- + +// ExtensionServiceTestBase sets up a TestingProfile with the ExtensionService, +// we then add the TemplateURLService, so the ProfileResetter can be exercised. +class AutomaticProfileResetterDelegateTest + : public ExtensionServiceTestBase, + public TemplateURLServiceTestUtilBase { + protected: + AutomaticProfileResetterDelegateTest() {} + virtual ~AutomaticProfileResetterDelegateTest() {} + + virtual void SetUp() OVERRIDE { + ExtensionServiceTestBase::SetUp(); + ExtensionServiceInitParams params = CreateDefaultInitParams(); + params.pref_file.clear(); // Prescribes a TestingPrefService to be created. + InitializeExtensionService(params); + TemplateURLServiceTestUtilBase::CreateTemplateUrlService(); + resetter_delegate_.reset( + new AutomaticProfileResetterDelegateUnderTest(profile())); + } + + virtual void TearDown() OVERRIDE { + resetter_delegate_.reset(); + ExtensionServiceTestBase::TearDown(); + } + + scoped_ptr<TemplateURL> CreateTestTemplateURL() { + TemplateURLData data; + + data.SetURL("http://example.com/search?q={searchTerms}"); + data.suggestions_url = "http://example.com/suggest?q={searchTerms}"; + data.instant_url = "http://example.com/instant?q={searchTerms}"; + data.image_url = "http://example.com/image?q={searchTerms}"; + data.search_url_post_params = "search-post-params"; + data.suggestions_url_post_params = "suggest-post-params"; + data.instant_url_post_params = "instant-post-params"; + data.image_url_post_params = "image-post-params"; + + data.favicon_url = GURL("http://example.com/favicon.ico"); + data.new_tab_url = "http://example.com/newtab.html"; + data.alternate_urls.push_back("http://example.com/s?q={searchTerms}"); + + data.short_name = base::ASCIIToUTF16("name"); + data.SetKeyword(base::ASCIIToUTF16("keyword")); + data.search_terms_replacement_key = "search-terms-replacment-key"; + data.prepopulate_id = 42; + data.input_encodings.push_back("UTF-8"); + data.safe_for_autoreplace = true; + + return scoped_ptr<TemplateURL>(new TemplateURL(profile(), data)); + } + + void ExpectNoPendingBrandcodedConfigFetch() { + EXPECT_FALSE(test_url_fetcher_factory_.GetFetcherByID(0)); + } + + void ExpectAndServicePendingBrandcodedConfigFetch(bool simulate_failure) { + net::TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + EXPECT_THAT(fetcher->upload_data(), + testing::HasSubstr(kTestBrandcode)); + ServicePendingBrancodedConfigFetch(fetcher, simulate_failure); } + void ExpectResetPromptState(bool active) { + GlobalErrorService* global_error_service = + GlobalErrorServiceFactory::GetForProfile(profile()); + GlobalError* global_error = global_error_service-> + GetGlobalErrorByMenuItemCommandID(IDC_SHOW_SETTINGS_RESET_BUBBLE); + EXPECT_EQ(active, !!global_error); + } + + AutomaticProfileResetterDelegateUnderTest* resetter_delegate() { + return resetter_delegate_.get(); + } + + // TemplateURLServiceTestUtilBase: + virtual TestingProfile* profile() const OVERRIDE { return profile_.get(); } + private: - DISALLOW_COPY_AND_ASSIGN(MockCallbackTarget); + net::TestURLFetcherFactory test_url_fetcher_factory_; + scoped_ptr<AutomaticProfileResetterDelegateUnderTest> resetter_delegate_; + + DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterDelegateTest); }; + // Tests --------------------------------------------------------------------- TEST_F(AutomaticProfileResetterDelegateTest, TriggerAndWaitOnModuleEnumeration) { - testing::StrictMock<MockCallbackTarget> mock_target; - // Expect ready_callback to be called just after the modules have been // enumerated. Fail if it is not called. Note: as the EnumerateModulesModel is // a global singleton, the callback might be invoked immediately if another // test-case (e.g. the one below) has already performed module enumeration. + testing::StrictMock<MockCallbackTarget> mock_target; EXPECT_CALL(mock_target, Run()); resetter_delegate()->RequestCallbackWhenLoadedModulesAreEnumerated( mock_target.CreateClosure()); @@ -217,7 +291,8 @@ TEST_F(AutomaticProfileResetterDelegateTest, // Expect ready_callback to be posted immediately even when the modules had // already been enumerated when the delegate was constructed. scoped_ptr<AutomaticProfileResetterDelegate> late_resetter_delegate( - new AutomaticProfileResetterDelegateImpl(NULL)); + new AutomaticProfileResetterDelegateImpl(profile(), + ProfileResetter::ALL)); EXPECT_CALL(mock_target, Run()); late_resetter_delegate->RequestCallbackWhenLoadedModulesAreEnumerated( @@ -249,12 +324,10 @@ TEST_F(AutomaticProfileResetterDelegateTest, GetLoadedModuleNameDigests) { #endif } -TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, - LoadAndWaitOnTemplateURLService) { - testing::StrictMock<MockCallbackTarget> mock_target; - +TEST_F(AutomaticProfileResetterDelegateTest, LoadAndWaitOnTemplateURLService) { // Expect ready_callback to be called just after the template URL service gets // initialized. Fail if it is not called, or called too early. + testing::StrictMock<MockCallbackTarget> mock_target; resetter_delegate()->RequestCallbackWhenTemplateURLServiceIsLoaded( mock_target.CreateClosure()); base::RunLoop().RunUntilIdle(); @@ -277,7 +350,8 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, // Expect ready_callback to be posted immediately even when the template URL // service had already been initialized when the delegate was constructed. scoped_ptr<AutomaticProfileResetterDelegate> late_resetter_delegate( - new AutomaticProfileResetterDelegateImpl(test_util_.model())); + new AutomaticProfileResetterDelegateImpl(profile(), + ProfileResetter::ALL)); EXPECT_CALL(mock_target, Run()); late_resetter_delegate->RequestCallbackWhenTemplateURLServiceIsLoaded( @@ -285,10 +359,11 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, base::RunLoop().RunUntilIdle(); } -TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, +TEST_F(AutomaticProfileResetterDelegateTest, DefaultSearchProviderDataWhenNotManaged) { - TemplateURLService* template_url_service = test_util_.model(); - test_util_.VerifyLoad(); + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile()); + TemplateURLServiceTestUtilBase::VerifyLoad(); // Check that the "managed state" and the details returned by the delegate are // correct. We verify the details against the data stored by @@ -309,18 +384,18 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, EXPECT_FALSE(resetter_delegate()->IsDefaultSearchProviderManaged()); } -TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, +TEST_F(AutomaticProfileResetterDelegateTest, DefaultSearchProviderDataWhenManaged) { const char kTestSearchURL[] = "http://example.com/search?q={searchTerms}"; const char kTestName[] = "name"; const char kTestKeyword[] = "keyword"; - test_util_.VerifyLoad(); + TemplateURLServiceTestUtilBase::VerifyLoad(); EXPECT_FALSE(resetter_delegate()->IsDefaultSearchProviderManaged()); // Set managed preferences to emulate a default search provider set by policy. - test_util_.SetManagedDefaultSearchPreferences( + SetManagedDefaultSearchPreferences( true, kTestName, kTestKeyword, kTestSearchURL, std::string(), std::string(), std::string(), std::string(), std::string()); @@ -333,8 +408,8 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, // Set managed preferences to emulate that having a default search provider is // disabled by policy. - test_util_.RemoveManagedDefaultSearchPreferences(); - test_util_.SetManagedDefaultSearchPreferences( + RemoveManagedDefaultSearchPreferences(); + SetManagedDefaultSearchPreferences( true, std::string(), std::string(), std::string(), std::string(), std::string(), std::string(), std::string(), std::string()); @@ -343,10 +418,11 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, EXPECT_TRUE(dsp_details->empty()); } -TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, +TEST_F(AutomaticProfileResetterDelegateTest, GetPrepopulatedSearchProvidersDetails) { - TemplateURLService* template_url_service = test_util_.model(); - test_util_.VerifyLoad(); + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile()); + TemplateURLServiceTestUtilBase::VerifyLoad(); scoped_ptr<base::ListValue> search_engines_details( resetter_delegate()->GetPrepopulatedSearchProvidersDetails()); @@ -379,4 +455,166 @@ TEST_F(AutomaticProfileResetterDelegateTestTemplateURLs, } } +TEST_F(AutomaticProfileResetterDelegateTest, + FetchAndWaitOnDefaultSettingsVanilla) { + google_util::BrandForTesting scoped_brand_for_testing((std::string())); + + // Expect ready_callback to be called just after empty brandcoded settings + // are loaded, given this is a vanilla build. Fail if it is not called, or + // called too early. + testing::StrictMock<MockCallbackTarget> mock_target; + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(resetter_delegate()->brandcoded_defaults()); + + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->FetchBrandcodedDefaultSettingsIfNeeded(); + base::RunLoop().RunUntilIdle(); + ExpectNoPendingBrandcodedConfigFetch(); + + testing::Mock::VerifyAndClearExpectations(&mock_target); + EXPECT_TRUE(resetter_delegate()->brandcoded_defaults()); + + // Expect ready_callback to be posted immediately when the brandcoded settings + // have already been loaded. + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); + + // No test for a new instance of AutomaticProfileResetterDelegate. That will + // need to fetch the brandcoded settings again. +} + +TEST_F(AutomaticProfileResetterDelegateTest, + FetchAndWaitOnDefaultSettingsBranded) { + google_util::BrandForTesting scoped_brand_for_testing(kTestBrandcode); + + // Expect ready_callback to be called just after the brandcoded settings are + // downloaded. Fail if it is not called, or called too early. + testing::StrictMock<MockCallbackTarget> mock_target; + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(resetter_delegate()->brandcoded_defaults()); + + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->FetchBrandcodedDefaultSettingsIfNeeded(); + ExpectAndServicePendingBrandcodedConfigFetch(false /*simulate_failure*/); + base::RunLoop().RunUntilIdle(); + + testing::Mock::VerifyAndClearExpectations(&mock_target); + const BrandcodedDefaultSettings* brandcoded_defaults = + resetter_delegate()->brandcoded_defaults(); + ASSERT_TRUE(brandcoded_defaults); + std::string homepage_url; + EXPECT_TRUE(brandcoded_defaults->GetHomepage(&homepage_url)); + EXPECT_EQ(kTestBrandedHomepage, homepage_url); + + // Expect ready_callback to be posted immediately when the brandcoded settings + // have already been downloaded. + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(AutomaticProfileResetterDelegateTest, + FetchAndWaitOnDefaultSettingsBrandedFailure) { + google_util::BrandForTesting scoped_brand_for_testing(kTestBrandcode); + + // Expect ready_callback to be called just after the brandcoded settings have + // failed to download. Fail if it is not called, or called too early. + testing::StrictMock<MockCallbackTarget> mock_target; + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); + + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->FetchBrandcodedDefaultSettingsIfNeeded(); + ExpectAndServicePendingBrandcodedConfigFetch(true /*simulate_failure*/); + base::RunLoop().RunUntilIdle(); + + testing::Mock::VerifyAndClearExpectations(&mock_target); + EXPECT_TRUE(resetter_delegate()->brandcoded_defaults()); + + // Expect ready_callback to be posted immediately when the brandcoded settings + // have already been attempted to be downloaded, but failed. + EXPECT_CALL(mock_target, Run()); + resetter_delegate()->RequestCallbackWhenBrandcodedDefaultsAreFetched( + mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(AutomaticProfileResetterDelegateTest, TriggerReset) { + google_util::BrandForTesting scoped_brand_for_testing(kTestBrandcode); + + PrefService* prefs = profile()->GetPrefs(); + DCHECK(prefs); + prefs->SetString(prefs::kHomePage, kTestHomepage); + + testing::StrictMock<MockCallbackTarget> mock_target; + EXPECT_CALL(mock_target, Run()); + EXPECT_CALL(*resetter_delegate(), SendFeedback(testing::_)).Times(0); + resetter_delegate()->TriggerProfileSettingsReset( + false /*send_feedback*/, mock_target.CreateClosure()); + ExpectAndServicePendingBrandcodedConfigFetch(false /*simulate_failure*/); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(kTestBrandedHomepage, prefs->GetString(prefs::kHomePage)); +} + +TEST_F(AutomaticProfileResetterDelegateTest, + TriggerResetWithDefaultSettingsAlreadyLoaded) { + google_util::BrandForTesting scoped_brand_for_testing(kTestBrandcode); + + PrefService* prefs = profile()->GetPrefs(); + DCHECK(prefs); + prefs->SetString(prefs::kHomePage, kTestHomepage); + + resetter_delegate()->FetchBrandcodedDefaultSettingsIfNeeded(); + ExpectAndServicePendingBrandcodedConfigFetch(false /*simulate_failure*/); + base::RunLoop().RunUntilIdle(); + + testing::StrictMock<MockCallbackTarget> mock_target; + EXPECT_CALL(mock_target, Run()); + EXPECT_CALL(*resetter_delegate(), SendFeedback(testing::_)).Times(0); + resetter_delegate()->TriggerProfileSettingsReset( + false /*send_feedback*/, mock_target.CreateClosure()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(kTestBrandedHomepage, prefs->GetString(prefs::kHomePage)); +} + +TEST_F(AutomaticProfileResetterDelegateTest, + TriggerResetAndSendFeedback) { + google_util::BrandForTesting scoped_brand_for_testing(kTestBrandcode); + + PrefService* prefs = profile()->GetPrefs(); + DCHECK(prefs); + prefs->SetString(prefs::kHomePage, kTestHomepage); + + testing::StrictMock<MockCallbackTarget> mock_target; + EXPECT_CALL(mock_target, Run()); + EXPECT_CALL(*resetter_delegate(), + SendFeedback(testing::HasSubstr(kTestHomepage))); + + resetter_delegate()->TriggerProfileSettingsReset( + true /*send_feedback*/, mock_target.CreateClosure()); + ExpectAndServicePendingBrandcodedConfigFetch(false /*simulate_failure*/); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(AutomaticProfileResetterDelegateTest, ShowAndDismissPrompt) { + resetter_delegate()->TriggerPrompt(); + if (ProfileResetGlobalError::IsSupportedOnPlatform()) + ExpectResetPromptState(true /*active*/); + else + ExpectResetPromptState(false /*active*/); + resetter_delegate()->DismissPrompt(); + ExpectResetPromptState(false /*active*/); + resetter_delegate()->DismissPrompt(); +} + } // namespace diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_factory.cc b/chrome/browser/profile_resetter/automatic_profile_resetter_factory.cc index a8c9fa1..b67d258 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_factory.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_factory.cc @@ -6,12 +6,9 @@ #include "base/memory/singleton.h" #include "base/prefs/pref_registry_simple.h" -#include "chrome/browser/extensions/extension_system_factory.h" -#include "chrome/browser/google/google_url_tracker_factory.h" #include "chrome/browser/profile_resetter/automatic_profile_resetter.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "chrome/common/pref_names.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" @@ -42,6 +39,7 @@ AutomaticProfileResetterFactory::AutomaticProfileResetterFactory() "AutomaticProfileResetter", BrowserContextDependencyManager::GetInstance()) { DependsOn(TemplateURLServiceFactory::GetInstance()); + DependsOn(GlobalErrorServiceFactory::GetInstance()); } AutomaticProfileResetterFactory::~AutomaticProfileResetterFactory() {} diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_unittest.cc b/chrome/browser/profile_resetter/automatic_profile_resetter_unittest.cc index ed7b67a..4f62b02 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_unittest.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_unittest.cc @@ -32,7 +32,6 @@ #include "testing/gtest/include/gtest/gtest.h" using testing::_; -using testing::Invoke; namespace { @@ -66,6 +65,8 @@ class AutomaticProfileResetterUnderTest : public AutomaticProfileResetter { virtual ~AutomaticProfileResetterUnderTest() {} MOCK_METHOD2(ReportStatistics, void(uint32, uint32)); + MOCK_METHOD1(ReportPromptResult, + void(AutomaticProfileResetter::PromptResult)); private: DISALLOW_COPY_AND_ASSIGN(AutomaticProfileResetterUnderTest); @@ -74,7 +75,7 @@ class AutomaticProfileResetterUnderTest : public AutomaticProfileResetter { class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { public: MockProfileResetterDelegate() - : emulated_default_search_provider_is_managed_(false) {} + : emulated_is_default_search_provider_managed_(false) {} virtual ~MockProfileResetterDelegate() {} MOCK_METHOD0(EnumerateLoadedModulesIfNeeded, void()); @@ -85,12 +86,18 @@ class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { MOCK_CONST_METHOD1(RequestCallbackWhenTemplateURLServiceIsLoaded, void(const base::Closure&)); + MOCK_METHOD0(FetchBrandcodedDefaultSettingsIfNeeded, void()); + MOCK_CONST_METHOD1(RequestCallbackWhenBrandcodedDefaultsAreFetched, + void(const base::Closure&)); + MOCK_CONST_METHOD0(OnGetLoadedModuleNameDigestsCalled, void()); MOCK_CONST_METHOD0(OnGetDefaultSearchProviderDetailsCalled, void()); MOCK_CONST_METHOD0(OnIsDefaultSearchProviderManagedCalled, void()); MOCK_CONST_METHOD0(OnGetPrepopulatedSearchProvidersDetailsCalled, void()); - MOCK_METHOD0(ShowPrompt, void()); + MOCK_METHOD0(TriggerPrompt, bool()); + MOCK_METHOD2(TriggerProfileSettingsReset, void(bool, const base::Closure&)); + MOCK_METHOD0(DismissPrompt, void()); virtual scoped_ptr<base::ListValue> GetLoadedModuleNameDigests() const OVERRIDE { @@ -108,7 +115,7 @@ class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { virtual bool IsDefaultSearchProviderManaged() const OVERRIDE { OnIsDefaultSearchProviderManagedCalled(); - return emulated_default_search_provider_is_managed_; + return emulated_is_default_search_provider_managed_; } virtual scoped_ptr<base::ListValue> @@ -124,9 +131,9 @@ class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { EXPECT_CALL(*this, EnumerateLoadedModulesIfNeeded()); EXPECT_CALL(*this, LoadTemplateURLServiceIfNeeded()); EXPECT_CALL(*this, RequestCallbackWhenLoadedModulesAreEnumerated(_)) - .WillOnce(Invoke(ClosureInvoker)); + .WillOnce(testing::Invoke(ClosureInvoker)); EXPECT_CALL(*this, RequestCallbackWhenTemplateURLServiceIsLoaded(_)) - .WillOnce(Invoke(ClosureInvoker)); + .WillOnce(testing::Invoke(ClosureInvoker)); } void ExpectCallsToGetterMethods() { @@ -136,6 +143,16 @@ class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { EXPECT_CALL(*this, OnGetPrepopulatedSearchProvidersDetailsCalled()); } + void ExpectCallToShowPrompt() { + EXPECT_CALL(*this, TriggerPrompt()).WillOnce(testing::Return(true)); + EXPECT_CALL(*this, FetchBrandcodedDefaultSettingsIfNeeded()); + } + + void ExpectCallToTriggerReset(bool send_feedback) { + EXPECT_CALL(*this, TriggerProfileSettingsReset(send_feedback, _)) + .WillOnce(testing::SaveArg<1>(&reset_completion_)); + } + base::DictionaryValue& emulated_default_search_provider_details() { return emulated_default_search_provider_details_; } @@ -148,15 +165,20 @@ class MockProfileResetterDelegate : public AutomaticProfileResetterDelegate { return emulated_loaded_module_digests_; } - void set_emulated_default_search_provider_is_managed(bool value) { - emulated_default_search_provider_is_managed_ = value; + void set_emulated_is_default_search_provider_managed(bool value) { + emulated_is_default_search_provider_managed_ = value; + } + + void EmulateProfileResetCompleted() { + reset_completion_.Run(); } private: base::DictionaryValue emulated_default_search_provider_details_; base::ListValue emulated_search_providers_details_; base::ListValue emulated_loaded_module_digests_; - bool emulated_default_search_provider_is_managed_; + bool emulated_is_default_search_provider_managed_; + base::Closure reset_completion_; DISALLOW_COPY_AND_ASSIGN(MockProfileResetterDelegate); }; @@ -407,6 +429,10 @@ class AutomaticProfileResetterTestBase : public testing::Test { : waiting_task_runner_(new base::TestSimpleTaskRunner), local_state_(TestingBrowserProcess::GetGlobal()), profile_(new TestingProfile()), + field_trials_(new base::FieldTrialList(NULL)), + memento_in_prefs_(new PreferenceHostedPromptMemento(profile())), + memento_in_local_state_(new LocalStateHostedPromptMemento(profile())), + memento_in_file_(new FileHostedPromptMementoSynchronous(profile())), experiment_group_name_(experiment_group_name), inject_data_through_variation_params_(false), mock_delegate_(NULL) { @@ -424,13 +450,11 @@ class AutomaticProfileResetterTestBase : public testing::Test { profile_->GetTestingPrefService()->registry(); DCHECK(user_prefs_registry); user_prefs_registry->RegisterStringPref( - kTestPreferencePath, - "", + kTestPreferencePath, std::string(), user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); } virtual void SetUp() OVERRIDE { - field_trials_.reset(new base::FieldTrialList(NULL)); chrome_variations::testing::ClearAllVariationParams(); base::FieldTrialList::CreateFieldTrial(kAutomaticProfileResetStudyName, experiment_group_name_); @@ -439,6 +463,8 @@ class AutomaticProfileResetterTestBase : public testing::Test { mock_delegate_owned_.reset( new testing::StrictMock<MockProfileResetterDelegate>()); mock_delegate_ = mock_delegate_owned_.get(); + + ExpectAllMementoValuesEqualTo(std::string()); } void SetTestingHashSeed(const std::string& hash_seed) { @@ -453,6 +479,12 @@ class AutomaticProfileResetterTestBase : public testing::Test { inject_data_through_variation_params_ = value; } + void ExpectAllMementoValuesEqualTo(const std::string& value) { + EXPECT_EQ(value, memento_in_prefs_->ReadValue()); + EXPECT_EQ(value, memento_in_local_state_->ReadValue()); + EXPECT_EQ(value, memento_in_file_->ReadValue()); + } + void UnleashResetterAndWait() { if (inject_data_through_variation_params_) { std::map<std::string, std::string> variation_params; @@ -483,9 +515,50 @@ class AutomaticProfileResetterTestBase : public testing::Test { base::RunLoop().RunUntilIdle(); } + // Goes through an evaluation flow such that the reset criteria are satisfied. + // Used to reduce boilerplate for tests that need to verify behavior during + // the reset prompt flow. + void OrchestrateThroughEvaluationFlow() { + SetTestingProgram(ConstructProgram(true, true)); + SetTestingHashSeed(kTestHashSeed); + + mock_delegate().ExpectCallsToDependenciesSetUpMethods(); + mock_delegate().ExpectCallsToGetterMethods(); + mock_delegate().ExpectCallToShowPrompt(); + EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + + UnleashResetterAndWait(); + + testing::Mock::VerifyAndClearExpectations(&resetter()); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + } + + // Explicitly shut down the service to double-check that nothing explodes, but + // first, verify expectations to make sure the service makes no more calls to + // any mocked functions during or after shutdown. + void VerifyExpectationsThenShutdownResetter() { + testing::Mock::VerifyAndClearExpectations(&resetter()); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + resetter_->Shutdown(); + resetter_.reset(); + } + TestingProfile* profile() { return profile_.get(); } TestingPrefServiceSimple* local_state() { return local_state_.Get(); } + PreferenceHostedPromptMemento& memento_in_prefs() { + return *memento_in_prefs_; + } + + LocalStateHostedPromptMemento& memento_in_local_state() { + return *memento_in_local_state_; + } + + FileHostedPromptMementoSynchronous& memento_in_file() { + return *memento_in_file_; + } + MockProfileResetterDelegate& mock_delegate() { return *mock_delegate_; } AutomaticProfileResetterUnderTest& resetter() { return *resetter_; } @@ -495,6 +568,10 @@ class AutomaticProfileResetterTestBase : public testing::Test { ScopedTestingLocalState local_state_; scoped_ptr<TestingProfile> profile_; scoped_ptr<base::FieldTrialList> field_trials_; + scoped_ptr<PreferenceHostedPromptMemento> memento_in_prefs_; + scoped_ptr<LocalStateHostedPromptMemento> memento_in_local_state_; + scoped_ptr<FileHostedPromptMementoSynchronous> memento_in_file_; + std::string experiment_group_name_; std::string testing_program_; std::string testing_hash_seed_; @@ -530,35 +607,18 @@ class AutomaticProfileResetterTestDisabled // Tests --------------------------------------------------------------------- TEST_F(AutomaticProfileResetterTestDisabled, NothingIsDoneWhenDisabled) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); // No calls are expected to the delegate. UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(std::string()); } TEST_F(AutomaticProfileResetterTestDryRun, ConditionsNotSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(false, false)); SetTestingHashSeed(kTestHashSeed); @@ -567,68 +627,45 @@ TEST_F(AutomaticProfileResetterTestDryRun, ConditionsNotSatisfied) { EXPECT_CALL(resetter(), ReportStatistics(0x00u, 0x00u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(std::string()); } TEST_F(AutomaticProfileResetterTestDryRun, OneConditionSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(true, false)); SetTestingHashSeed(kTestHashSeed); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); EXPECT_CALL(resetter(), ReportStatistics(0x01u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_TRIGGERED)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } TEST_F(AutomaticProfileResetterTestDryRun, OtherConditionSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(false, true)); SetTestingHashSeed(kTestHashSeed); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); EXPECT_CALL(resetter(), ReportStatistics(0x02u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_TRIGGERED)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } #if defined(GOOGLE_CHROME_BUILD) TEST_F(AutomaticProfileResetterTestDryRun, ProgramSetThroughVariationParams) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); AllowInjectingTestDataThroughVariationParams(true); @@ -636,24 +673,21 @@ TEST_F(AutomaticProfileResetterTestDryRun, ProgramSetThroughVariationParams) { mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_TRIGGERED)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } #endif TEST_F(AutomaticProfileResetterTestDryRun, ConditionsSatisfiedAndInvalidMementos) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_prefs.StoreValue(kTestInvalidMementoValue); - memento_in_local_state.StoreValue(kTestInvalidMementoValue); - memento_in_file.StoreValue(kTestInvalidMementoValue); + memento_in_prefs().StoreValue(kTestInvalidMementoValue); + memento_in_local_state().StoreValue(kTestInvalidMementoValue); + memento_in_file().StoreValue(kTestInvalidMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -661,20 +695,17 @@ TEST_F(AutomaticProfileResetterTestDryRun, mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_TRIGGERED)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadPrefHostedMemento) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_prefs.StoreValue(kTestMementoValue); + memento_in_prefs().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -684,18 +715,15 @@ TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadPrefHostedMemento) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x03u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_prefs().ReadValue()); + EXPECT_EQ(std::string(), memento_in_local_state().ReadValue()); + EXPECT_EQ(std::string(), memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadLocalStateHostedMemento) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_local_state.StoreValue(kTestMementoValue); + memento_in_local_state().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -705,18 +733,15 @@ TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadLocalStateHostedMemento) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x05u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + EXPECT_EQ(std::string(), memento_in_prefs().ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_local_state().ReadValue()); + EXPECT_EQ(std::string(), memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadFileHostedMemento) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_file.StoreValue(kTestMementoValue); + memento_in_file().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -726,38 +751,26 @@ TEST_F(AutomaticProfileResetterTestDryRun, AlreadyHadFileHostedMemento) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x09u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + EXPECT_EQ(std::string(), memento_in_prefs().ReadValue()); + EXPECT_EQ(std::string(), memento_in_local_state().ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTestDryRun, DoNothingWhenResourcesAreMissing) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - SetTestingProgram(""); - SetTestingHashSeed(""); + SetTestingProgram(std::string()); + SetTestingHashSeed(std::string()); // No calls are expected to the delegate. UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(std::string()); } TEST_F(AutomaticProfileResetterTest, ConditionsNotSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(false, false)); SetTestingHashSeed(kTestHashSeed); @@ -766,117 +779,84 @@ TEST_F(AutomaticProfileResetterTest, ConditionsNotSatisfied) { EXPECT_CALL(resetter(), ReportStatistics(0x00u, 0x00u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(std::string()); } TEST_F(AutomaticProfileResetterTest, OneConditionSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(true, false)); SetTestingHashSeed(kTestHashSeed); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - EXPECT_CALL(mock_delegate(), ShowPrompt()); + mock_delegate().ExpectCallToShowPrompt(); EXPECT_CALL(resetter(), ReportStatistics(0x01u, 0x01u)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + VerifyExpectationsThenShutdownResetter(); } TEST_F(AutomaticProfileResetterTest, OtherConditionSatisfied) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(false, true)); SetTestingHashSeed(kTestHashSeed); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - EXPECT_CALL(mock_delegate(), ShowPrompt()); + mock_delegate().ExpectCallToShowPrompt(); EXPECT_CALL(resetter(), ReportStatistics(0x02u, 0x01u)); UnleashResetterAndWait(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + VerifyExpectationsThenShutdownResetter(); } #if defined(GOOGLE_CHROME_BUILD) TEST_F(AutomaticProfileResetterTest, ProgramSetThroughVariationParams) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); - SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); AllowInjectingTestDataThroughVariationParams(true); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - EXPECT_CALL(mock_delegate(), ShowPrompt()); + mock_delegate().ExpectCallToShowPrompt(); EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); UnleashResetterAndWait(); + resetter().NotifyDidShowResetBubble(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } #endif TEST_F(AutomaticProfileResetterTest, ConditionsSatisfiedAndInvalidMementos) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_prefs.StoreValue(kTestInvalidMementoValue); - memento_in_local_state.StoreValue(kTestInvalidMementoValue); - memento_in_file.StoreValue(kTestInvalidMementoValue); + memento_in_prefs().StoreValue(kTestInvalidMementoValue); + memento_in_local_state().StoreValue(kTestInvalidMementoValue); + memento_in_file().StoreValue(kTestInvalidMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - EXPECT_CALL(mock_delegate(), ShowPrompt()); + mock_delegate().ExpectCallToShowPrompt(); EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); UnleashResetterAndWait(); + resetter().NotifyDidShowResetBubble(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } TEST_F(AutomaticProfileResetterTest, PrefHostedMementoPreventsPrompt) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_prefs.StoreValue(kTestMementoValue); + memento_in_prefs().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -886,18 +866,15 @@ TEST_F(AutomaticProfileResetterTest, PrefHostedMementoPreventsPrompt) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x03u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ(kTestMementoValue, memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_prefs().ReadValue()); + EXPECT_EQ(std::string(), memento_in_local_state().ReadValue()); + EXPECT_EQ(std::string(), memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTest, LocalStateHostedMementoPreventsPrompt) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_local_state.StoreValue(kTestMementoValue); + memento_in_local_state().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -907,18 +884,15 @@ TEST_F(AutomaticProfileResetterTest, LocalStateHostedMementoPreventsPrompt) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x05u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + EXPECT_EQ(std::string(), memento_in_prefs().ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_local_state().ReadValue()); + EXPECT_EQ(std::string(), memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTest, FileHostedMementoPreventsPrompt) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - memento_in_file.StoreValue(kTestMementoValue); + memento_in_file().StoreValue(kTestMementoValue); SetTestingProgram(ConstructProgram(true, true)); SetTestingHashSeed(kTestHashSeed); @@ -928,27 +902,203 @@ TEST_F(AutomaticProfileResetterTest, FileHostedMementoPreventsPrompt) { EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x09u)); UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ(kTestMementoValue, memento_in_file.ReadValue()); + EXPECT_EQ(std::string(), memento_in_prefs().ReadValue()); + EXPECT_EQ(std::string(), memento_in_local_state().ReadValue()); + EXPECT_EQ(kTestMementoValue, memento_in_file().ReadValue()); } TEST_F(AutomaticProfileResetterTest, DoNothingWhenResourcesAreMissing) { - PreferenceHostedPromptMemento memento_in_prefs(profile()); - LocalStateHostedPromptMemento memento_in_local_state(profile()); - FileHostedPromptMementoSynchronous memento_in_file(profile()); - - SetTestingProgram(""); - SetTestingHashSeed(""); + SetTestingProgram(std::string()); + SetTestingHashSeed(std::string()); // No calls are expected to the delegate. UnleashResetterAndWait(); + VerifyExpectationsThenShutdownResetter(); + + ExpectAllMementoValuesEqualTo(std::string()); +} + +TEST_F(AutomaticProfileResetterTest, PromptSuppressed) { + OrchestrateThroughEvaluationFlow(); + + VerifyExpectationsThenShutdownResetter(); + + ExpectAllMementoValuesEqualTo(std::string()); +} + +TEST_F(AutomaticProfileResetterTest, PromptNotSupported) { + SetTestingProgram(ConstructProgram(true, true)); + SetTestingHashSeed(kTestHashSeed); + + mock_delegate().ExpectCallsToDependenciesSetUpMethods(); + mock_delegate().ExpectCallsToGetterMethods(); + EXPECT_CALL(mock_delegate(), TriggerPrompt()) + .WillOnce(testing::Return(false)); + EXPECT_CALL(resetter(), ReportStatistics(0x03u, 0x01u)); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_TRIGGERED)); + + UnleashResetterAndWait(); + + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptIgnored) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptActionReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + mock_delegate().ExpectCallToTriggerReset(false); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_ACTION_RESET)); + resetter().TriggerProfileReset(false /*send_feedback*/); + testing::Mock::VerifyAndClearExpectations(&resetter()); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + mock_delegate().EmulateProfileResetCompleted(); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptActionResetWithFeedback) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + mock_delegate().ExpectCallToTriggerReset(true); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_ACTION_RESET)); + resetter().TriggerProfileReset(true /*send_feedback*/); + testing::Mock::VerifyAndClearExpectations(&resetter()); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + mock_delegate().EmulateProfileResetCompleted(); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptActionNoReset) { + OrchestrateThroughEvaluationFlow(); - EXPECT_EQ("", memento_in_prefs.ReadValue()); - EXPECT_EQ("", memento_in_local_state.ReadValue()); - EXPECT_EQ("", memento_in_file.ReadValue()); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_ACTION_NO_RESET)); + resetter().SkipProfileReset(); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptFollowedByWebUIReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + resetter().NotifyDidOpenWebUIResetDialog(); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_FOLLOWED_BY_WEBUI_RESET)); + resetter().NotifyDidCloseWebUIResetDialog(true); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptFollowedByWebUINoReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + resetter().NotifyDidOpenWebUIResetDialog(); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_FOLLOWED_BY_WEBUI_NO_RESET)); + resetter().NotifyDidCloseWebUIResetDialog(false); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptFollowedByIncidentalWebUIReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_SHOWN_BUBBLE)); + resetter().NotifyDidShowResetBubble(); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + testing::Mock::VerifyAndClearExpectations(&resetter()); + + // Missing NotifyDidOpenWebUIResetDialog(). + // This can arise if a settings page was already opened at the time the prompt + // was triggered, and it used to initiate reset after dismissing the prompt. + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_FOLLOWED_BY_WEBUI_RESET)); + resetter().NotifyDidCloseWebUIResetDialog(true); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptSuppressedButHadWebUIReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + resetter().NotifyDidOpenWebUIResetDialog(); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(resetter(), ReportPromptResult( + AutomaticProfileResetter::PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_RESET)); + resetter().NotifyDidCloseWebUIResetDialog(true); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); +} + +TEST_F(AutomaticProfileResetterTest, PromptSuppressedButHadWebUINoReset) { + OrchestrateThroughEvaluationFlow(); + + EXPECT_CALL(mock_delegate(), DismissPrompt()); + resetter().NotifyDidOpenWebUIResetDialog(); + testing::Mock::VerifyAndClearExpectations(&mock_delegate()); + + EXPECT_CALL(resetter(), ReportPromptResult(AutomaticProfileResetter:: + PROMPT_NOT_SHOWN_BUBBLE_BUT_HAD_WEBUI_NO_RESET)); + resetter().NotifyDidCloseWebUIResetDialog(false); + ExpectAllMementoValuesEqualTo(kTestMementoValue); + VerifyExpectationsThenShutdownResetter(); } // Please see comments above ConstructProgramToCheckPreferences() to understand @@ -963,8 +1113,8 @@ TEST_F(AutomaticProfileResetterTest, InputUserPreferencesCorrect) { mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - uint32 expected_mask = - HAS_EXPECTED_USER_PREFERENCE | USER_PREFERENCE_IS_USER_CONTROLLED; + uint32 expected_mask = HAS_EXPECTED_USER_PREFERENCE | + USER_PREFERENCE_IS_USER_CONTROLLED; EXPECT_CALL(resetter(), ReportStatistics(0x00u, expected_mask)); UnleashResetterAndWait(); @@ -979,8 +1129,8 @@ TEST_F(AutomaticProfileResetterTest, InputLocalStateCorrect) { mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); - uint32 expected_mask = - HAS_EXPECTED_LOCAL_STATE_PREFERENCE | LOCAL_STATE_IS_USER_CONTROLLED; + uint32 expected_mask = HAS_EXPECTED_LOCAL_STATE_PREFERENCE | + LOCAL_STATE_IS_USER_CONTROLLED; EXPECT_CALL(resetter(), ReportStatistics(0x00u, expected_mask)); UnleashResetterAndWait(); @@ -1043,7 +1193,7 @@ TEST_F(AutomaticProfileResetterTest, InputSearchProviderManagedCorrect) { mock_delegate().emulated_default_search_provider_details().SetString( kSearchURLAttributeKey, kTestSearchURL); - mock_delegate().set_emulated_default_search_provider_is_managed(true); + mock_delegate().set_emulated_is_default_search_provider_managed(true); mock_delegate().ExpectCallsToDependenciesSetUpMethods(); mock_delegate().ExpectCallsToGetterMethods(); diff --git a/chrome/browser/profile_resetter/profile_reset_global_error.cc b/chrome/browser/profile_resetter/profile_reset_global_error.cc index 03c78c7..5a2cd06 100644 --- a/chrome/browser/profile_resetter/profile_reset_global_error.cc +++ b/chrome/browser/profile_resetter/profile_reset_global_error.cc @@ -6,6 +6,12 @@ #include "base/metrics/histogram.h" #include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/profile_resetter/automatic_profile_resetter.h" +#include "chrome/browser/profile_resetter/automatic_profile_resetter_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/global_error/global_error_service.h" +#include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "chrome/browser/ui/profile_reset_bubble.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -13,12 +19,21 @@ namespace { -// The maximum number of ignored bubbles we track in the NumNoThanksPerReset -// histogram. -const int kMaxIgnored = 50; +// The URL that leads directly to the WebUI reset dialog in the settings page. +const char kResetProfileSettingsURL[] = + "chrome://settings/resetProfileSettings"; -// The number of buckets we want the NumNoThanksPerReset histogram to use. -const int kNumIgnoredBuckets = 5; +base::TimeDelta GetPromptDelayHistogramMaximum() { + return base::TimeDelta::FromDays(7); +} + +// Records the delay between when the reset prompt is triggered and when the +// bubble can actually be shown. +void RecordPromptDelay(const base::TimeDelta& delay) { + UMA_HISTOGRAM_CUSTOM_TIMES( + "AutomaticProfileReset.PromptDelay", delay, + base::TimeDelta::FromSeconds(1), GetPromptDelayHistogramMaximum(), 50); +} } // namespace @@ -26,9 +41,22 @@ const int kNumIgnoredBuckets = 5; // ProfileResetGlobalError --------------------------------------------------- ProfileResetGlobalError::ProfileResetGlobalError(Profile* profile) - : profile_(profile), num_times_bubble_view_shown_(0), bubble_view_(NULL) {} + : profile_(profile), has_shown_bubble_view_(false), bubble_view_(NULL) { + AutomaticProfileResetter* automatic_profile_resetter = + AutomaticProfileResetterFactory::GetForBrowserContext(profile_); + if (automatic_profile_resetter) + automatic_profile_resetter_ = automatic_profile_resetter->AsWeakPtr(); +} -ProfileResetGlobalError::~ProfileResetGlobalError() {} +ProfileResetGlobalError::~ProfileResetGlobalError() { + if (!has_shown_bubble_view_) + RecordPromptDelay(GetPromptDelayHistogramMaximum()); +} + +// static +bool ProfileResetGlobalError::IsSupportedOnPlatform() { + return IsProfileResetBubbleSupported(); +} bool ProfileResetGlobalError::HasMenuItem() { return true; } @@ -43,20 +71,27 @@ string16 ProfileResetGlobalError::MenuItemLabel() { } void ProfileResetGlobalError::ExecuteMenuItem(Browser* browser) { - ShowBubbleView(browser); + browser->OpenURL(content::OpenURLParams( + GURL(kResetProfileSettingsURL), content::Referrer(), + NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false)); } bool ProfileResetGlobalError::HasBubbleView() { return true; } bool ProfileResetGlobalError::HasShownBubbleView() { - return num_times_bubble_view_shown_ > 0; + return has_shown_bubble_view_; } void ProfileResetGlobalError::ShowBubbleView(Browser* browser) { - if (bubble_view_) + if (has_shown_bubble_view_) return; - ++num_times_bubble_view_shown_; + + has_shown_bubble_view_ = true; bubble_view_ = ShowProfileResetBubble(AsWeakPtr(), browser); + + if (automatic_profile_resetter_) + automatic_profile_resetter_->NotifyDidShowResetBubble(); + RecordPromptDelay(timer_.Elapsed()); } void ProfileResetGlobalError::OnBubbleViewDidClose() { @@ -65,16 +100,13 @@ void ProfileResetGlobalError::OnBubbleViewDidClose() { void ProfileResetGlobalError::OnBubbleViewResetButtonPressed( bool send_feedback) { - // TODO(engedy): Integrate with the AutomaticProfileResetter. - UMA_HISTOGRAM_CUSTOM_COUNTS("SettingsResetBubble.NumNoThanksPerReset", - num_times_bubble_view_shown_ - 1, - 0, - kMaxIgnored, - kNumIgnoredBuckets); + if (automatic_profile_resetter_) + automatic_profile_resetter_->TriggerProfileReset(send_feedback); } void ProfileResetGlobalError::OnBubbleViewNoThanksButtonPressed() { - // TODO(engedy): Integrate with the AutomaticProfileResetter. + if (automatic_profile_resetter_) + automatic_profile_resetter_->SkipProfileReset(); } GlobalErrorBubbleViewBase* ProfileResetGlobalError::GetBubbleView() { diff --git a/chrome/browser/profile_resetter/profile_reset_global_error.h b/chrome/browser/profile_resetter/profile_reset_global_error.h index 8b35696..3bdc295 100644 --- a/chrome/browser/profile_resetter/profile_reset_global_error.h +++ b/chrome/browser/profile_resetter/profile_reset_global_error.h @@ -7,13 +7,16 @@ #include "base/basictypes.h" #include "base/memory/weak_ptr.h" +#include "base/timer/elapsed_timer.h" #include "chrome/browser/ui/global_error/global_error.h" +class AutomaticProfileResetter; class GlobalErrorBubbleViewBase; class Profile; -// Shows preferences reset errors on the wrench menu and exposes a menu item to -// launch a bubble view. +// Encapsulates UI-related functionality for the one-time profile settings reset +// prompt. The UI consists of two parts: (1.) the profile reset (pop-up) bubble, +// and (2.) a menu item in the wrench menu (provided by us being a GlobalError). class ProfileResetGlobalError : public GlobalError, public base::SupportsWeakPtr<ProfileResetGlobalError> { @@ -21,6 +24,9 @@ class ProfileResetGlobalError explicit ProfileResetGlobalError(Profile* profile); virtual ~ProfileResetGlobalError(); + // Returns whether or not the reset prompt is supported on this platform. + static bool IsSupportedOnPlatform(); + // Called by the bubble view when it is closed. void OnBubbleViewDidClose(); @@ -45,9 +51,17 @@ class ProfileResetGlobalError private: Profile* profile_; - // The number of times we have shown the bubble so far. This can be >1 if the - // user dismissed the bubble, then selected the menu item to show it again. - int num_times_bubble_view_shown_; + // GlobalErrorService owns us, on which AutomaticProfileResetter depends, so + // during shutdown, it may get destroyed before we are. + // Note: the AutomaticProfileResetter expects call-backs from us to always be + // synchronous, so that there will be no call-backs once we are destroyed. + base::WeakPtr<AutomaticProfileResetter> automatic_profile_resetter_; + + // Used to measure the delay before the bubble actually gets shown. + base::ElapsedTimer timer_; + + // Whether or not we have already shown the bubble. + bool has_shown_bubble_view_; // The reset bubble, if we're currently showing one. GlobalErrorBubbleViewBase* bubble_view_; |