diff options
Diffstat (limited to 'chrome/browser/ui/webui/sync_promo/sync_promo_handler.cc')
-rw-r--r-- | chrome/browser/ui/webui/sync_promo/sync_promo_handler.cc | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/chrome/browser/ui/webui/sync_promo/sync_promo_handler.cc b/chrome/browser/ui/webui/sync_promo/sync_promo_handler.cc new file mode 100644 index 0000000..86e9454 --- /dev/null +++ b/chrome/browser/ui/webui/sync_promo/sync_promo_handler.cc @@ -0,0 +1,298 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/sync_promo/sync_promo_handler.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/metrics/histogram.h" +#include "base/time.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/sync_setup_flow.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/webui/sync_promo/sync_promo_trial.h" +#include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" + +namespace { + +// User actions on the sync promo (aka "Sign in to Chrome"). +enum SyncPromoUserFlowActionEnums { + SYNC_PROMO_VIEWED, + SYNC_PROMO_LEARN_MORE_CLICKED, + SYNC_PROMO_ACCOUNT_HELP_CLICKED, + SYNC_PROMO_CREATE_ACCOUNT_CLICKED, + SYNC_PROMO_SKIP_CLICKED, + SYNC_PROMO_SIGN_IN_ATTEMPTED, + SYNC_PROMO_SIGNED_IN_SUCCESSFULLY, + SYNC_PROMO_ADVANCED_CLICKED, + SYNC_PROMO_ENCRYPTION_HELP_CLICKED, + SYNC_PROMO_CANCELLED_AFTER_SIGN_IN, + SYNC_PROMO_CONFIRMED_AFTER_SIGN_IN, + SYNC_PROMO_CLOSED_TAB, + SYNC_PROMO_CLOSED_WINDOW, + SYNC_PROMO_LEFT_DURING_THROBBER, + SYNC_PROMO_BUCKET_BOUNDARY, + SYNC_PROMO_FIRST_VALID_JS_ACTION = SYNC_PROMO_LEARN_MORE_CLICKED, + SYNC_PROMO_LAST_VALID_JS_ACTION = SYNC_PROMO_CONFIRMED_AFTER_SIGN_IN, +}; + +// This was added because of the need to change the existing UMA enum for the +// sync promo mid-flight. Ideally these values would be contiguous, but the +// real world is not always ideal. +static bool IsValidUserFlowAction(int action) { + return (action >= SYNC_PROMO_FIRST_VALID_JS_ACTION && + action <= SYNC_PROMO_LAST_VALID_JS_ACTION) || + action == SYNC_PROMO_LEFT_DURING_THROBBER; +} + +} // namespace + +SyncPromoHandler::SyncPromoHandler(ProfileManager* profile_manager) + : SyncSetupHandler(profile_manager), + window_already_closed_(false) { +} + +SyncPromoHandler::~SyncPromoHandler() { +} + +// static +void SyncPromoHandler::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterIntegerPref(prefs::kSyncPromoViewCount, 0, + PrefService::UNSYNCABLE_PREF); + prefs->RegisterBooleanPref(prefs::kSyncPromoShowNTPBubble, false, + PrefService::UNSYNCABLE_PREF); +} + +WebUIMessageHandler* SyncPromoHandler::Attach(WebUI* web_ui) { + DCHECK(web_ui); + // Keep a reference to the preferences service for convenience and it's + // probably a little faster that getting it via Profile::FromWebUI() every + // time we need to interact with preferences. + prefs_ = Profile::FromWebUI(web_ui)->GetPrefs(); + DCHECK(prefs_); + // Ignore events from view-source:chrome://syncpromo. + if (!web_ui->tab_contents()->controller().GetActiveEntry()-> + IsViewSourceMode()) { + // Listen to see if the tab we're in gets closed. + registrar_.Add(this, content::NOTIFICATION_TAB_CLOSING, + content::Source<NavigationController>( + &web_ui->tab_contents()->controller())); + // Listen to see if the window we're in gets closed. + registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSING, + content::NotificationService::AllSources()); + } + return SyncSetupHandler::Attach(web_ui); +} + +void SyncPromoHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("SyncPromo:Close", + base::Bind(&SyncPromoHandler::HandleCloseSyncPromo, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:Initialize", + base::Bind(&SyncPromoHandler::HandleInitializeSyncPromo, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:RecordSignInAttempts", + base::Bind(&SyncPromoHandler::HandleRecordSignInAttempts, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:RecordThrobberTime", + base::Bind(&SyncPromoHandler::HandleRecordThrobberTime, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:ShowAdvancedSettings", + base::Bind(&SyncPromoHandler::HandleShowAdvancedSettings, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:UserFlowAction", + base::Bind(&SyncPromoHandler::HandleUserFlowAction, + base::Unretained(this))); + web_ui_->RegisterMessageCallback("SyncPromo:UserSkipped", + base::Bind(&SyncPromoHandler::HandleUserSkipped, + base::Unretained(this))); + SyncSetupHandler::RegisterMessages(); +} + +void SyncPromoHandler::ShowGaiaSuccessAndClose() { + RecordExperimentOutcomesOnSignIn(); + SyncSetupHandler::ShowGaiaSuccessAndClose(); +} + +void SyncPromoHandler::ShowGaiaSuccessAndSettingUp() { + RecordExperimentOutcomesOnSignIn(); + SyncSetupHandler::ShowGaiaSuccessAndSettingUp(); +} + +void SyncPromoHandler::ShowConfigure(const base::DictionaryValue& args) { + bool usePassphrase = false; + args.GetBoolean("usePassphrase", &usePassphrase); + + if (usePassphrase) { + // If a passphrase is required then we must show the configure pane. + SyncSetupHandler::ShowConfigure(args); + } else { + // If no passphrase is required then skip the configure pane and sync + // everything by default. This makes the first run experience simpler. + // Note, there's an advanced link in the sync promo that takes users + // to Settings where the configure pane is not skipped. + SyncConfiguration configuration; + configuration.sync_everything = true; + DCHECK(flow()); + flow()->OnUserConfigured(configuration); + } +} + +void SyncPromoHandler::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_TAB_CLOSING: { + if (!window_already_closed_) + RecordUserFlowAction(SYNC_PROMO_CLOSED_TAB); + break; + } + case chrome::NOTIFICATION_BROWSER_CLOSING: { + // Make sure we're in the tab strip of the closing window. + Browser* browser = content::Source<Browser>(source).ptr(); + if (browser->tabstrip_model()->GetWrapperIndex( + web_ui_->tab_contents()) != TabStripModel::kNoTab) { + RecordUserFlowAction(SYNC_PROMO_CLOSED_WINDOW); + window_already_closed_ = true; + } + break; + } + default: { + NOTREACHED(); + } + } +} + +void SyncPromoHandler::StepWizardForShowSetupUI() { +} + +void SyncPromoHandler::ShowSetupUI() { + // SyncSetupWizard::Step should be called in StepWizardForShowSetupUI above, + // but it causes the sync promo page to not set focus properly to the login + // email address. This happens because focus is lost between the call to + // StepWizardForShowSetupUI and ShowSetupUI. + // TODO(binji): Move this function back and fix the focus the right way. + ProfileSyncService* service = + Profile::FromWebUI(web_ui_)->GetProfileSyncService(); + service->get_wizard().Step(SyncSetupWizard::GetLoginState()); +} + +void SyncPromoHandler::HandleCloseSyncPromo(const base::ListValue* args) { + CloseSyncSetup(); + + // If the user has signed in then set the pref to show them NTP bubble + // confirming that they're signed in. + std::string username = prefs_->GetString(prefs::kGoogleServicesUsername); + if (!username.empty()) + prefs_->SetBoolean(prefs::kSyncPromoShowNTPBubble, true); + + GURL url = SyncPromoUI::GetNextPageURLForSyncPromoURL( + web_ui_->tab_contents()->GetURL()); + web_ui_->tab_contents()->OpenURL(url, GURL(), CURRENT_TAB, + content::PAGE_TRANSITION_LINK); +} + +void SyncPromoHandler::HandleInitializeSyncPromo(const base::ListValue* args) { + // If the promo is also the Chrome launch page, we want to show the title and + // log an event if we are running an experiment. + bool is_launch_page = SyncPromoUI::GetIsLaunchPageForSyncPromoURL( + web_ui_->tab_contents()->GetURL()); + if (is_launch_page && sync_promo_trial::IsExperimentActive()) + sync_promo_trial::RecordUserSawMessage(); + base::FundamentalValue visible(is_launch_page); + web_ui_->CallJavascriptFunction("SyncSetupOverlay.setPromoTitleVisible", + visible); + + OpenSyncSetup(); + // We don't need to compute anything for this, just do this every time. + RecordUserFlowAction(SYNC_PROMO_VIEWED); + // Increment view count first and show natural numbers in stats rather than 0 + // based starting point (if it happened to be our first time showing this). + IncrementViewCountBy(1); + // Record +1 for every view. This is the only thing we record that's not part + // of the user flow histogram. + UMA_HISTOGRAM_COUNTS("SyncPromo.NumTimesViewed", GetViewCount()); +} + +void SyncPromoHandler::HandleShowAdvancedSettings( + const base::ListValue* args) { + CloseSyncSetup(); + std::string url(chrome::kChromeUISettingsURL); + url += chrome::kSyncSetupSubPage; + web_ui_->tab_contents()->OpenURL(GURL(url), GURL(), CURRENT_TAB, + content::PAGE_TRANSITION_LINK); + RecordUserFlowAction(SYNC_PROMO_ADVANCED_CLICKED); +} + +// TODO(dbeam): Replace with metricsHandler:recordHistogramTime when it exists. +void SyncPromoHandler::HandleRecordThrobberTime(const base::ListValue* args) { + double time_double; + CHECK(args->GetDouble(0, &time_double)); + UMA_HISTOGRAM_TIMES("SyncPromo.ThrobberTime", + base::TimeDelta::FromMilliseconds(time_double)); +} + +// TODO(dbeam): Replace with metricsHandler:recordHistogramCount when it exists. +void SyncPromoHandler::HandleRecordSignInAttempts(const base::ListValue* args) { + double count_double; + CHECK(args->GetDouble(0, &count_double)); + UMA_HISTOGRAM_COUNTS("SyncPromo.SignInAttempts", count_double); +} + +void SyncPromoHandler::HandleUserFlowAction(const base::ListValue* args) { + double action_double; + CHECK(args->GetDouble(0, &action_double)); + int action = static_cast<int>(action_double); + + if (IsValidUserFlowAction(action)) + RecordUserFlowAction(action); + else + NOTREACHED() << "Attempt to record invalid user flow action on sync promo."; +} + +void SyncPromoHandler::HandleUserSkipped(const base::ListValue* args) { + SyncPromoUI::SetUserSkippedSyncPromo(Profile::FromWebUI(web_ui_)); + RecordUserFlowAction(SYNC_PROMO_SKIP_CLICKED); +} + +int SyncPromoHandler::GetViewCount() const { + // The locally persistent number of times the user has seen the sync promo. + return prefs_->GetInteger(prefs::kSyncPromoViewCount); +} + +int SyncPromoHandler::IncrementViewCountBy(size_t amount) { + // Let the user increment by 0 if they really want. It might be useful for a + // weird way of sending preference change notifications... + int adjusted = GetViewCount() + amount; + prefs_->SetInteger(prefs::kSyncPromoViewCount, adjusted); + return adjusted; +} + +void SyncPromoHandler::RecordExperimentOutcomesOnSignIn() { + if (sync_promo_trial::IsExperimentActive()) + sync_promo_trial::RecordUserSignedIn(); + if (sync_promo_trial::IsPartOfBrandTrialToEnable()) { + bool is_start_up = SyncPromoUI::GetIsLaunchPageForSyncPromoURL( + web_ui_->tab_contents()->GetURL()); + Profile* profile = Profile::FromWebUI(web_ui_); + sync_promo_trial::RecordUserSignedInWithTrialBrand(is_start_up, profile); + } +} + +void SyncPromoHandler::RecordUserFlowAction(int action) { + // Send an enumeration to our single user flow histogram. + UMA_HISTOGRAM_ENUMERATION("SyncPromo.UserFlow", action, + SYNC_PROMO_BUCKET_BOUNDARY); +} |