summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhuangs@chromium.org <huangs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-15 22:49:12 +0000
committerhuangs@chromium.org <huangs@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-15 22:49:12 +0000
commit899580d4686dbf3d90d1835617ad0cc954d5bca5 (patch)
tree2238af0fb624f78f1941ae5d574ab7b0167833c7
parent6e82aa436518fbbd17702f0abe046ebc5e564bff (diff)
downloadchromium_src-899580d4686dbf3d90d1835617ad0cc954d5bca5.zip
chromium_src-899580d4686dbf3d90d1835617ad0cc954d5bca5.tar.gz
chromium_src-899580d4686dbf3d90d1835617ad0cc954d5bca5.tar.bz2
Pulling user experiment code from BrowserDistribution to a new class.
This is part #2A of the refactoring task to pull out installation_state from installer_util. In the old code, code bloat exists is google_chrome_distribution.cc for user experiments. This brings in lots of dependencies, and prevents BrowserDistribution from being a light-weight information (if not constant) getter class. In this CL we pull: enum ToastUIflags{} struct UserExperiment{} GetExperimentDetails() LaunchUserExperiment() InactiveUserToastExperiment() from BrowserDistribution, and moved into a new class "class UserExperiment", with the following renames: struct UserExperiment => struct ExperimentDetails GetExperimentDetails() => CreateExperimentDetails() The functions are made into static members of UserExperiment. The only use on BrowserDistribution was in InactiveUserToastExperiment(), which now receives it as parameter. Finally, ShouldSetExperimentLabels() is left with BrowserDistributions, since it is light-weight. BUG=170398 TBR=ben Review URL: https://chromiumcodereview.appspot.com/12321061 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188494 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/first_run/try_chrome_dialog_view.cc19
-rw-r--r--chrome/chrome_installer_util.gypi2
-rw-r--r--chrome/installer/setup/setup_main.cc13
-rw-r--r--chrome/installer/util/browser_distribution.cc17
-rw-r--r--chrome/installer/util/browser_distribution.h50
-rw-r--r--chrome/installer/util/chrome_app_host_operations.cc37
-rw-r--r--chrome/installer/util/chrome_app_host_operations.h27
-rw-r--r--chrome/installer/util/chrome_binaries_operations.cc31
-rw-r--r--chrome/installer/util/chrome_binaries_operations.h27
-rw-r--r--chrome/installer/util/chrome_browser_operations.cc38
-rw-r--r--chrome/installer/util/chrome_browser_operations.h27
-rw-r--r--chrome/installer/util/chrome_browser_sxs_operations.cc4
-rw-r--r--chrome/installer/util/chrome_browser_sxs_operations.h10
-rw-r--r--chrome/installer/util/chrome_frame_operations.cc44
-rw-r--r--chrome/installer/util/chrome_frame_operations.h29
-rw-r--r--chrome/installer/util/google_chrome_distribution.cc528
-rw-r--r--chrome/installer/util/google_chrome_distribution.h22
-rw-r--r--chrome/installer/util/google_chrome_distribution_dummy.cc19
-rw-r--r--chrome/installer/util/google_chrome_sxs_distribution.cc4
-rw-r--r--chrome/installer/util/google_chrome_sxs_distribution.h1
-rw-r--r--chrome/installer/util/product.cc12
-rw-r--r--chrome/installer/util/product.h5
-rw-r--r--chrome/installer/util/product_operations.h26
-rw-r--r--chrome/installer/util/user_experiment.cc538
-rw-r--r--chrome/installer/util/user_experiment.h68
25 files changed, 829 insertions, 769 deletions
diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc
index 7db3ee6..11042a7 100644
--- a/chrome/browser/first_run/try_chrome_dialog_view.cc
+++ b/chrome/browser/first_run/try_chrome_dialog_view.cc
@@ -11,6 +11,7 @@
#include "base/string16.h"
#include "chrome/browser/process_singleton.h"
#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/user_experiment.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
@@ -174,13 +175,9 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
layout->AddView(icon);
// Find out what experiment we are conducting.
- BrowserDistribution* dist = BrowserDistribution::GetDistribution();
- if (!dist) {
- NOTREACHED() << "Cannot determine browser distribution";
- return DIALOG_ERROR;
- }
- BrowserDistribution::UserExperiment experiment;
- if (!dist->GetExperimentDetails(&experiment, flavor_) ||
+ installer::ExperimentDetails experiment;
+ if (!BrowserDistribution::GetDistribution()->HasUserExperiments() ||
+ !installer::CreateExperimentDetails(flavor_, &experiment) ||
!experiment.heading) {
NOTREACHED() << "Cannot determine which headline to show.";
return DIALOG_ERROR;
@@ -214,7 +211,7 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
// Decide if the don't bug me is a button or a radio button.
bool dont_bug_me_button =
- ((experiment.flags & BrowserDistribution::kDontBugMeAsButton) != 0);
+ !!(experiment.flags & installer::kToastUiDontBugMeAsButton);
// Optional third and fourth row.
if (!dont_bug_me_button) {
@@ -225,7 +222,7 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
dont_try_chrome_->set_listener(this);
layout->AddView(dont_try_chrome_);
}
- if (experiment.flags & BrowserDistribution::kUninstall) {
+ if (experiment.flags & installer::kToastUiUninstall) {
layout->StartRow(0, 2);
kill_chrome_ = new views::RadioButton(
l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME), kRadioGroupID);
@@ -237,7 +234,7 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
accept_button->set_tag(BT_OK_BUTTON);
views::Separator* separator = NULL;
- if (experiment.flags & BrowserDistribution::kMakeDefault) {
+ if (experiment.flags & installer::kToastUiMakeDefault) {
// In this flavor we have some veritical space, then a separator line
// and the 'make default' checkbox and the OK button on the same row.
layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
@@ -267,7 +264,7 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
}
}
- if (experiment.flags & BrowserDistribution::kWhyLink) {
+ if (experiment.flags & installer::kToastUiWhyLink) {
layout->StartRowWithPadding(0, 4, 0, 10);
views::Link* link = new views::Link(
l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY));
diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi
index ecc0b6a..e9ab6e2 100644
--- a/chrome/chrome_installer_util.gypi
+++ b/chrome/chrome_installer_util.gypi
@@ -154,6 +154,8 @@
'installer/util/self_cleaning_temp_dir.h',
'installer/util/shell_util.cc',
'installer/util/shell_util.h',
+ 'installer/util/user_experiment.cc',
+ 'installer/util/user_experiment.h',
],
'conditions': [
['component=="shared_library"', {
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 073d442..0cf8f843 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -60,6 +60,7 @@
#include "chrome/installer/util/master_preferences_constants.h"
#include "chrome/installer/util/self_cleaning_temp_dir.h"
#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/user_experiment.h"
#include "chrome/installer/util/util_constants.h"
#include "installer_util_strings.h" // NOLINT
@@ -923,8 +924,8 @@ installer::InstallStatus InstallProductsHelper(
for (Products::const_iterator it = products.begin(); it < products.end();
++it) {
const Product& product = **it;
- product.distribution()->LaunchUserExperiment(setup_path,
- install_status, *installer_version, product, system_install);
+ product.LaunchUserExperiment(setup_path, install_status,
+ system_install);
}
}
}
@@ -1428,8 +1429,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
for (Products::const_iterator it = products.begin(); it < products.end();
++it) {
const Product& product = **it;
- BrowserDistribution* browser_dist = product.distribution();
- browser_dist->InactiveUserToastExperiment(
+ installer::InactiveUserToastExperiment(
flavor, ASCIIToUTF16(experiment_group), product,
installer_state->target_path());
}
@@ -1449,9 +1449,8 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
<< browser_dist->GetAppShortCutName()
<< " found for system-level toast.";
} else {
- browser_dist->LaunchUserExperiment(cmd_line.GetProgram(),
- installer::REENTRY_SYS_UPDATE,
- installed_version, product, true);
+ product.LaunchUserExperiment(
+ cmd_line.GetProgram(), installer::REENTRY_SYS_UPDATE, true);
}
}
} else if (cmd_line.HasSwitch(
diff --git a/chrome/installer/util/browser_distribution.cc b/chrome/installer/util/browser_distribution.cc
index bde7d20a..427d520 100644
--- a/chrome/installer/util/browser_distribution.cc
+++ b/chrome/installer/util/browser_distribution.cc
@@ -262,23 +262,10 @@ void BrowserDistribution::UpdateInstallStatus(bool system_install,
installer::InstallStatus install_status) {
}
-bool BrowserDistribution::GetExperimentDetails(
- UserExperiment* experiment, int flavor) {
- return false;
-}
-
-void BrowserDistribution::LaunchUserExperiment(
- const base::FilePath& setup_path, installer::InstallStatus status,
- const Version& version, const installer::Product& product,
- bool system_level) {
-}
-
bool BrowserDistribution::ShouldSetExperimentLabels() {
return false;
}
-void BrowserDistribution::InactiveUserToastExperiment(int flavor,
- const string16& experiment_group,
- const installer::Product& installation,
- const base::FilePath& application_path) {
+bool BrowserDistribution::HasUserExperiments() {
+ return false;
}
diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h
index 025a708..b1a6149 100644
--- a/chrome/installer/util/browser_distribution.h
+++ b/chrome/installer/util/browser_distribution.h
@@ -19,10 +19,6 @@
#include <windows.h> // NOLINT
#endif
-namespace installer {
-class Product;
-}
-
class BrowserDistribution {
public:
enum Type {
@@ -33,28 +29,6 @@ class BrowserDistribution {
NUM_TYPES
};
- // Flags to control what to show in the UserExperiment dialog.
- enum ToastUIflags {
- kUninstall = 1, // Uninstall radio button.
- kDontBugMeAsButton = 2, // Don't bug me is a button, not a radio button.
- kWhyLink = 4, // Has the 'why I am seeing this' link.
- kMakeDefault = 8 // Has the 'make it default' checkbox.
- };
-
- // A struct for communicating what a UserExperiment contains. In these
- // experiments we show toasts to the user if they are inactive for a certain
- // amount of time.
- struct UserExperiment {
- string16 prefix; // The experiment code prefix for this experiment,
- // also known as the 'TV' part in 'TV80'.
- int flavor; // The flavor index for this experiment.
- int heading; // The heading resource ID to use for this experiment.
- int flags; // See ToastUIFlags above.
- int control_group; // Size of the control group (in percentages). Control
- // group is the group that qualifies for the
- // experiment but does not participate.
- };
-
virtual ~BrowserDistribution() {}
static BrowserDistribution* GetDistribution();
@@ -145,32 +119,12 @@ class BrowserDistribution {
installer::ArchiveType archive_type,
installer::InstallStatus install_status);
- // Gets the experiment details for a given language-brand combo. If |flavor|
- // is -1, then a flavor will be selected at random. |experiment| is the struct
- // you want to write the experiment information to. Returns false if no
- // experiment details could be gathered.
- virtual bool GetExperimentDetails(UserExperiment* experiment, int flavor);
-
- // After an install or upgrade the user might qualify to participate in an
- // experiment. This function determines if the user qualifies and if so it
- // sets the wheels in motion or in simple cases does the experiment itself.
- virtual void LaunchUserExperiment(const base::FilePath& setup_path,
- installer::InstallStatus status,
- const Version& version,
- const installer::Product& product,
- bool system_level);
-
- // The user has qualified for the inactive user toast experiment and this
- // function just performs it.
- virtual void InactiveUserToastExperiment(int flavor,
- const string16& experiment_group,
- const installer::Product& installation,
- const base::FilePath& application_path);
-
// Returns true if this distribution should set the Omaha experiment_labels
// registry value.
virtual bool ShouldSetExperimentLabels();
+ virtual bool HasUserExperiments();
+
protected:
explicit BrowserDistribution(Type type);
diff --git a/chrome/installer/util/chrome_app_host_operations.cc b/chrome/installer/util/chrome_app_host_operations.cc
index 3690196..361f952 100644
--- a/chrome/installer/util/chrome_app_host_operations.cc
+++ b/chrome/installer/util/chrome_app_host_operations.cc
@@ -18,9 +18,8 @@
namespace installer {
-void ChromeAppHostOperations::ReadOptions(
- const MasterPreferences& prefs,
- std::set<std::wstring>* options) const {
+void ChromeAppHostOperations::ReadOptions(const MasterPreferences& prefs,
+ std::set<string16>* options) const {
DCHECK(options);
bool pref_value;
@@ -30,9 +29,8 @@ void ChromeAppHostOperations::ReadOptions(
}
}
-void ChromeAppHostOperations::ReadOptions(
- const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const {
+void ChromeAppHostOperations::ReadOptions(const CommandLine& uninstall_command,
+ std::set<string16>* options) const {
DCHECK(options);
if (uninstall_command.HasSwitch(switches::kMultiInstall))
@@ -40,17 +38,17 @@ void ChromeAppHostOperations::ReadOptions(
}
void ChromeAppHostOperations::AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const {
}
void ChromeAppHostOperations::AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const {
}
void ChromeAppHostOperations::AppendProductFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
bool is_multi_install = options.find(kOptionMultiInstall) != options.end();
@@ -67,7 +65,7 @@ void ChromeAppHostOperations::AppendProductFlags(
}
void ChromeAppHostOperations::AppendRenameFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
bool is_multi_install = options.find(kOptionMultiInstall) != options.end();
@@ -80,10 +78,9 @@ void ChromeAppHostOperations::AppendRenameFlags(
cmd_line->AppendSwitch(switches::kMultiInstall);
}
-bool ChromeAppHostOperations::SetChannelFlags(
- const std::set<std::wstring>& options,
- bool set,
- ChannelInfo* channel_info) const {
+bool ChromeAppHostOperations::SetChannelFlags(const std::set<string16>& options,
+ bool set,
+ ChannelInfo* channel_info) const {
#if defined(GOOGLE_CHROME_BUILD)
DCHECK(channel_info);
return channel_info->SetAppLauncher(set);
@@ -93,7 +90,7 @@ bool ChromeAppHostOperations::SetChannelFlags(
}
bool ChromeAppHostOperations::ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const {
+ const std::set<string16>& options) const {
return true;
}
@@ -120,4 +117,14 @@ void ChromeAppHostOperations::AddDefaultShortcutProperties(
}
}
+void ChromeAppHostOperations::LaunchUserExperiment(
+ const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const {
+ // No experiments yet. If adding some in the future, need to have
+ // ChromeAppHostDistribution::HasUserExperiments() return true.
+ NOTREACHED();
+}
+
} // namespace installer
diff --git a/chrome/installer/util/chrome_app_host_operations.h b/chrome/installer/util/chrome_app_host_operations.h
index f462640..3406dfc 100644
--- a/chrome/installer/util/chrome_app_host_operations.h
+++ b/chrome/installer/util/chrome_app_host_operations.h
@@ -18,39 +18,42 @@ class ChromeAppHostOperations : public ProductOperations {
ChromeAppHostOperations() {}
virtual void ReadOptions(const MasterPreferences& prefs,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void ReadOptions(const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const OVERRIDE;
virtual void AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const OVERRIDE;
- virtual void AppendProductFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendProductFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual void AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual bool SetChannelFlags(const std::set<std::wstring>& options,
+ virtual bool SetChannelFlags(const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const OVERRIDE;
virtual bool ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const OVERRIDE;
+ const std::set<string16>& options) const OVERRIDE;
virtual void AddDefaultShortcutProperties(
BrowserDistribution* dist,
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const OVERRIDE;
+ virtual void LaunchUserExperiment(const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const OVERRIDE;
+
private:
DISALLOW_COPY_AND_ASSIGN(ChromeAppHostOperations);
};
diff --git a/chrome/installer/util/chrome_binaries_operations.cc b/chrome/installer/util/chrome_binaries_operations.cc
index 9a00805..b9e4658 100644
--- a/chrome/installer/util/chrome_binaries_operations.cc
+++ b/chrome/installer/util/chrome_binaries_operations.cc
@@ -15,34 +15,32 @@
namespace installer {
-void ChromeBinariesOperations::ReadOptions(
- const MasterPreferences& prefs,
- std::set<std::wstring>* options) const {
+void ChromeBinariesOperations::ReadOptions(const MasterPreferences& prefs,
+ std::set<string16>* options) const {
DCHECK(options);
options->insert(kOptionMultiInstall);
}
-void ChromeBinariesOperations::ReadOptions(
- const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const {
+void ChromeBinariesOperations::ReadOptions(const CommandLine& uninstall_command,
+ std::set<string16>* options) const {
DCHECK(options);
options->insert(kOptionMultiInstall);
}
void ChromeBinariesOperations::AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const {
DCHECK(key_files);
key_files->push_back(base::FilePath(installer::kChromeDll));
}
void ChromeBinariesOperations::AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const {
}
void ChromeBinariesOperations::AppendProductFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
@@ -54,7 +52,7 @@ void ChromeBinariesOperations::AppendProductFlags(
}
void ChromeBinariesOperations::AppendRenameFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
@@ -66,14 +64,14 @@ void ChromeBinariesOperations::AppendRenameFlags(
}
bool ChromeBinariesOperations::SetChannelFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const {
return false;
}
bool ChromeBinariesOperations::ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const {
+ const std::set<string16>& options) const {
return false;
}
@@ -84,4 +82,13 @@ void ChromeBinariesOperations::AddDefaultShortcutProperties(
NOTREACHED() << "Chrome Binaries do not create shortcuts.";
}
+void ChromeBinariesOperations::LaunchUserExperiment(
+ const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const {
+ // Not meaningful to have binaries run experiments.
+ NOTREACHED();
+}
+
} // namespace installer
diff --git a/chrome/installer/util/chrome_binaries_operations.h b/chrome/installer/util/chrome_binaries_operations.h
index a780ba4..09cf09b 100644
--- a/chrome/installer/util/chrome_binaries_operations.h
+++ b/chrome/installer/util/chrome_binaries_operations.h
@@ -18,39 +18,42 @@ class ChromeBinariesOperations : public ProductOperations {
ChromeBinariesOperations() {}
virtual void ReadOptions(const MasterPreferences& prefs,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void ReadOptions(const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const OVERRIDE;
virtual void AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const OVERRIDE;
- virtual void AppendProductFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendProductFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual void AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual bool SetChannelFlags(const std::set<std::wstring>& options,
+ virtual bool SetChannelFlags(const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const OVERRIDE;
virtual bool ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const OVERRIDE;
+ const std::set<string16>& options) const OVERRIDE;
virtual void AddDefaultShortcutProperties(
BrowserDistribution* dist,
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const OVERRIDE;
+ virtual void LaunchUserExperiment(const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const OVERRIDE;
+
private:
DISALLOW_COPY_AND_ASSIGN(ChromeBinariesOperations);
};
diff --git a/chrome/installer/util/chrome_browser_operations.cc b/chrome/installer/util/chrome_browser_operations.cc
index 1f23261..448baa9 100644
--- a/chrome/installer/util/chrome_browser_operations.cc
+++ b/chrome/installer/util/chrome_browser_operations.cc
@@ -16,13 +16,13 @@
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/master_preferences_constants.h"
#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/user_experiment.h"
#include "chrome/installer/util/util_constants.h"
namespace installer {
-void ChromeBrowserOperations::ReadOptions(
- const MasterPreferences& prefs,
- std::set<std::wstring>* options) const {
+void ChromeBrowserOperations::ReadOptions(const MasterPreferences& prefs,
+ std::set<string16>* options) const {
DCHECK(options);
bool pref_value;
@@ -33,9 +33,8 @@ void ChromeBrowserOperations::ReadOptions(
}
}
-void ChromeBrowserOperations::ReadOptions(
- const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const {
+void ChromeBrowserOperations::ReadOptions(const CommandLine& uninstall_command,
+ std::set<string16>* options) const {
DCHECK(options);
if (uninstall_command.HasSwitch(switches::kMultiInstall))
@@ -43,19 +42,19 @@ void ChromeBrowserOperations::ReadOptions(
}
void ChromeBrowserOperations::AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const {
DCHECK(key_files);
key_files->push_back(base::FilePath(installer::kChromeDll));
}
void ChromeBrowserOperations::AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const {
}
void ChromeBrowserOperations::AppendProductFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
@@ -70,7 +69,7 @@ void ChromeBrowserOperations::AppendProductFlags(
}
void ChromeBrowserOperations::AppendRenameFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
@@ -81,10 +80,9 @@ void ChromeBrowserOperations::AppendRenameFlags(
}
}
-bool ChromeBrowserOperations::SetChannelFlags(
- const std::set<std::wstring>& options,
- bool set,
- ChannelInfo* channel_info) const {
+bool ChromeBrowserOperations::SetChannelFlags(const std::set<string16>& options,
+ bool set,
+ ChannelInfo* channel_info) const {
#if defined(GOOGLE_CHROME_BUILD)
DCHECK(channel_info);
return channel_info->SetChrome(set);
@@ -94,7 +92,7 @@ bool ChromeBrowserOperations::SetChannelFlags(
}
bool ChromeBrowserOperations::ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const {
+ const std::set<string16>& options) const {
return true;
}
@@ -136,4 +134,14 @@ void ChromeBrowserOperations::AddDefaultShortcutProperties(
properties->set_description(dist->GetAppDescription());
}
+void ChromeBrowserOperations::LaunchUserExperiment(
+ const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const {
+ CommandLine base_command(setup_path);
+ AppendProductFlags(options, &base_command);
+ installer::LaunchBrowserUserExperiment(base_command, status, system_level);
+}
+
} // namespace installer
diff --git a/chrome/installer/util/chrome_browser_operations.h b/chrome/installer/util/chrome_browser_operations.h
index 9c75a4e..573b39a 100644
--- a/chrome/installer/util/chrome_browser_operations.h
+++ b/chrome/installer/util/chrome_browser_operations.h
@@ -17,39 +17,42 @@ class ChromeBrowserOperations : public ProductOperations {
ChromeBrowserOperations() {}
virtual void ReadOptions(const MasterPreferences& prefs,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void ReadOptions(const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const OVERRIDE;
virtual void AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const OVERRIDE;
- virtual void AppendProductFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendProductFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual void AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual bool SetChannelFlags(const std::set<std::wstring>& options,
+ virtual bool SetChannelFlags(const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const OVERRIDE;
virtual bool ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const OVERRIDE;
+ const std::set<string16>& options) const OVERRIDE;
virtual void AddDefaultShortcutProperties(
BrowserDistribution* dist,
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const OVERRIDE;
+ virtual void LaunchUserExperiment(const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const OVERRIDE;
+
private:
DISALLOW_COPY_AND_ASSIGN(ChromeBrowserOperations);
};
diff --git a/chrome/installer/util/chrome_browser_sxs_operations.cc b/chrome/installer/util/chrome_browser_sxs_operations.cc
index 00aabf9..ad7cc5e 100644
--- a/chrome/installer/util/chrome_browser_sxs_operations.cc
+++ b/chrome/installer/util/chrome_browser_sxs_operations.cc
@@ -11,7 +11,7 @@
namespace installer {
void ChromeBrowserSxSOperations::AppendProductFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
@@ -20,7 +20,7 @@ void ChromeBrowserSxSOperations::AppendProductFlags(
}
void ChromeBrowserSxSOperations::AppendRenameFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
diff --git a/chrome/installer/util/chrome_browser_sxs_operations.h b/chrome/installer/util/chrome_browser_sxs_operations.h
index 37ffba0..f457012 100644
--- a/chrome/installer/util/chrome_browser_sxs_operations.h
+++ b/chrome/installer/util/chrome_browser_sxs_operations.h
@@ -16,13 +16,11 @@ class ChromeBrowserSxSOperations : public ChromeBrowserOperations {
public:
ChromeBrowserSxSOperations() {}
- virtual void AppendProductFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendProductFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual void AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(ChromeBrowserSxSOperations);
diff --git a/chrome/installer/util/chrome_frame_operations.cc b/chrome/installer/util/chrome_frame_operations.cc
index 5783da9..9642ab4 100644
--- a/chrome/installer/util/chrome_frame_operations.cc
+++ b/chrome/installer/util/chrome_frame_operations.cc
@@ -17,8 +17,8 @@ namespace installer {
// Remove ready-mode if not multi-install.
void ChromeFrameOperations::NormalizeOptions(
- std::set<std::wstring>* options) const {
- std::set<std::wstring>::iterator ready_mode(options->find(kOptionReadyMode));
+ std::set<string16>* options) const {
+ std::set<string16>::iterator ready_mode(options->find(kOptionReadyMode));
if (ready_mode != options->end() &&
options->find(kOptionMultiInstall) == options->end()) {
LOG(WARNING) << "--ready-mode option does not apply when --multi-install "
@@ -27,9 +27,8 @@ void ChromeFrameOperations::NormalizeOptions(
}
}
-void ChromeFrameOperations::ReadOptions(
- const MasterPreferences& prefs,
- std::set<std::wstring>* options) const {
+void ChromeFrameOperations::ReadOptions(const MasterPreferences& prefs,
+ std::set<string16>* options) const {
DCHECK(options);
static const struct PrefToOption {
@@ -51,9 +50,8 @@ void ChromeFrameOperations::ReadOptions(
NormalizeOptions(options);
}
-void ChromeFrameOperations::ReadOptions(
- const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const {
+void ChromeFrameOperations::ReadOptions(const CommandLine& uninstall_command,
+ std::set<string16>* options) const {
DCHECK(options);
static const struct FlagToOption {
@@ -74,7 +72,7 @@ void ChromeFrameOperations::ReadOptions(
}
void ChromeFrameOperations::AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const {
DCHECK(key_files);
key_files->push_back(base::FilePath(installer::kChromeFrameDll));
@@ -82,14 +80,14 @@ void ChromeFrameOperations::AddKeyFiles(
}
void ChromeFrameOperations::AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const {
DCHECK(com_dll_list);
com_dll_list->push_back(base::FilePath(installer::kChromeFrameDll));
}
void ChromeFrameOperations::AppendProductFlags(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
CommandLine* cmd_line) const {
DCHECK(cmd_line);
bool is_multi_install = options.find(kOptionMultiInstall) != options.end();
@@ -106,9 +104,8 @@ void ChromeFrameOperations::AppendProductFlags(
cmd_line->AppendSwitch(switches::kChromeFrameReadyMode);
}
-void ChromeFrameOperations::AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const {
+void ChromeFrameOperations::AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const {
DCHECK(cmd_line);
bool is_multi_install = options.find(kOptionMultiInstall) != options.end();
@@ -121,10 +118,9 @@ void ChromeFrameOperations::AppendRenameFlags(
cmd_line->AppendSwitch(switches::kChromeFrame);
}
-bool ChromeFrameOperations::SetChannelFlags(
- const std::set<std::wstring>& options,
- bool set,
- ChannelInfo* channel_info) const {
+bool ChromeFrameOperations::SetChannelFlags(const std::set<string16>& options,
+ bool set,
+ ChannelInfo* channel_info) const {
#if defined(GOOGLE_CHROME_BUILD)
DCHECK(channel_info);
bool modified = channel_info->SetChromeFrame(set);
@@ -141,7 +137,7 @@ bool ChromeFrameOperations::SetChannelFlags(
}
bool ChromeFrameOperations::ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const {
+ const std::set<string16>& options) const {
return options.find(kOptionReadyMode) == options.end();
}
@@ -152,4 +148,14 @@ void ChromeFrameOperations::AddDefaultShortcutProperties(
NOTREACHED() << "Chrome Frame does not create shortcuts.";
}
+void ChromeFrameOperations::LaunchUserExperiment(
+ const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const {
+ // No experiments yet. If adding some in the future, need to have
+ // ChromeFrameDistribution::HasUserExperiments() return true.
+ NOTREACHED();
+}
+
} // namespace installer
diff --git a/chrome/installer/util/chrome_frame_operations.h b/chrome/installer/util/chrome_frame_operations.h
index dac2016..5f117c6 100644
--- a/chrome/installer/util/chrome_frame_operations.h
+++ b/chrome/installer/util/chrome_frame_operations.h
@@ -17,41 +17,44 @@ class ChromeFrameOperations : public ProductOperations {
ChromeFrameOperations() {}
virtual void ReadOptions(const MasterPreferences& prefs,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void ReadOptions(const CommandLine& uninstall_command,
- std::set<std::wstring>* options) const OVERRIDE;
+ std::set<string16>* options) const OVERRIDE;
virtual void AddKeyFiles(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const OVERRIDE;
virtual void AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const OVERRIDE;
- virtual void AppendProductFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendProductFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual void AppendRenameFlags(
- const std::set<std::wstring>& options,
- CommandLine* cmd_line) const OVERRIDE;
+ virtual void AppendRenameFlags(const std::set<string16>& options,
+ CommandLine* cmd_line) const OVERRIDE;
- virtual bool SetChannelFlags(const std::set<std::wstring>& options,
+ virtual bool SetChannelFlags(const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const OVERRIDE;
virtual bool ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const OVERRIDE;
+ const std::set<string16>& options) const OVERRIDE;
virtual void AddDefaultShortcutProperties(
BrowserDistribution* dist,
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const OVERRIDE;
+ virtual void LaunchUserExperiment(const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const OVERRIDE;
+
protected:
- void NormalizeOptions(std::set<std::wstring>* options) const;
+ void NormalizeOptions(std::set<string16>* options) const;
private:
DISALLOW_COPY_AND_ASSIGN(ChromeFrameOperations);
diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc
index a01e4b02..8a506a35 100644
--- a/chrome/installer/util/google_chrome_distribution.cc
+++ b/chrome/installer/util/google_chrome_distribution.cc
@@ -9,27 +9,16 @@
#include <windows.h>
#include <msi.h>
-#include <sddl.h>
-#include <wtsapi32.h>
-#include <vector>
-#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
-#include "base/process_util.h"
-#include "base/rand_util.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
-#include "chrome/common/attrition_experiments.h"
-#include "chrome/common/chrome_result_codes.h"
-#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/test_server_locations.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/channel_info.h"
@@ -38,15 +27,12 @@
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/l10n_string_util.h"
-#include "chrome/installer/util/product.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/wmi.h"
#include "content/public/common/result_codes.h"
#include "installer_util_strings.h" // NOLINT
-#pragma comment(lib, "wtsapi32.lib")
-
namespace {
const wchar_t kChromeGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
@@ -54,17 +40,6 @@ const wchar_t kBrowserAppId[] = L"Chrome";
const wchar_t kCommandExecuteImplUuid[] =
L"{5C65F4B0-3651-4514-B207-D10CB699B14B}";
-// The following strings are the possible outcomes of the toast experiment
-// as recorded in the |client| field.
-const wchar_t kToastExpControlGroup[] = L"01";
-const wchar_t kToastExpCancelGroup[] = L"02";
-const wchar_t kToastExpUninstallGroup[] = L"04";
-const wchar_t kToastExpTriesOkGroup[] = L"18";
-const wchar_t kToastExpTriesErrorGroup[] = L"28";
-const wchar_t kToastActiveGroup[] = L"40";
-const wchar_t kToastUDDirFailure[] = L"40";
-const wchar_t kToastExpBaseGroup[] = L"80";
-
// Substitute the locale parameter in uninstall URL with whatever
// Google Update tells us is the locale. In case we fail to find
// the locale, we use US English.
@@ -81,209 +56,6 @@ string16 GetUninstallSurveyUrl() {
return LocalizeUrl(kSurveyUrl);
}
-string16 GetWelcomeBackUrl() {
- const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/"
- L"welcomeback-new.html";
- return LocalizeUrl(kWelcomeUrl);
-}
-
-// Converts FILETIME to hours. FILETIME times are absolute times in
-// 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464.
-int FileTimeToHours(const FILETIME& time) {
- const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60;
- ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime};
- return static_cast<int>(uli.QuadPart / k100sNanoSecsToHours);
-}
-
-// Returns the directory last write time in hours since January 1, 1601.
-// Returns -1 if there was an error retrieving the directory time.
-int GetDirectoryWriteTimeInHours(const wchar_t* path) {
- // To open a directory you need to pass FILE_FLAG_BACKUP_SEMANTICS.
- DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
- HANDLE file = ::CreateFileW(path, 0, share, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (INVALID_HANDLE_VALUE == file)
- return -1;
- FILETIME time;
- if (!::GetFileTime(file, NULL, NULL, &time)) {
- ::CloseHandle(file);
- return -1;
- }
-
- ::CloseHandle(file);
- return FileTimeToHours(time);
-}
-
-// Returns the directory last-write time age in hours, relative to current
-// time, so if it returns 14 it means that the directory was last written 14
-// hours ago. Returns -1 if there was an error retrieving the directory.
-int GetDirectoryWriteAgeInHours(const wchar_t* path) {
- int dir_time = GetDirectoryWriteTimeInHours(path);
- if (dir_time < 0)
- return dir_time;
- FILETIME time;
- GetSystemTimeAsFileTime(&time);
- int now_time = FileTimeToHours(time);
- if (dir_time >= now_time)
- return 0;
- return (now_time - dir_time);
-}
-
-// Launches setup.exe (located at |setup_path|) with |cmd_line|.
-// If system_level_toast is true, appends --system-level-toast.
-// If handle to experiment result key was given at startup, re-add it.
-// Does not wait for the process to terminate.
-// |cmd_line| may be modified as a result of this call.
-bool LaunchSetup(CommandLine* cmd_line,
- const installer::Product& product,
- bool system_level_toast) {
- const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
-
- // Propagate --verbose-logging to the invoked setup.exe.
- if (current_cmd_line.HasSwitch(installer::switches::kVerboseLogging))
- cmd_line->AppendSwitch(installer::switches::kVerboseLogging);
-
- // Pass along product-specific options.
- product.AppendProductFlags(cmd_line);
-
- // Re-add the system level toast flag.
- if (system_level_toast) {
- cmd_line->AppendSwitch(installer::switches::kSystemLevel);
- cmd_line->AppendSwitch(installer::switches::kSystemLevelToast);
-
- // Re-add the toast result key. We need to do this because Setup running as
- // system passes the key to Setup running as user, but that child process
- // does not perform the actual toasting, it launches another Setup (as user)
- // to do so. That is the process that needs the key.
- std::string key(installer::switches::kToastResultsKey);
- std::string toast_key = current_cmd_line.GetSwitchValueASCII(key);
- if (!toast_key.empty()) {
- cmd_line->AppendSwitchASCII(key, toast_key);
-
- // Use handle inheritance to make sure the duplicated toast results key
- // gets inherited by the child process.
- base::LaunchOptions options;
- options.inherit_handles = true;
- return base::LaunchProcess(*cmd_line, options, NULL);
- }
- }
-
- return base::LaunchProcess(*cmd_line, base::LaunchOptions(), NULL);
-}
-
-// For System level installs, setup.exe lives in the system temp, which
-// is normally c:\windows\temp. In many cases files inside this folder
-// are not accessible for execution by regular user accounts.
-// This function changes the permissions so that any authenticated user
-// can launch |exe| later on. This function should only be called if the
-// code is running at the system level.
-bool FixDACLsForExecute(const base::FilePath& exe) {
- // The general strategy to is to add an ACE to the exe DACL the quick
- // and dirty way: a) read the DACL b) convert it to sddl string c) add the
- // new ACE to the string d) convert sddl string back to DACL and finally
- // e) write new dacl.
- char buff[1024];
- DWORD len = sizeof(buff);
- PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff);
- if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
- sd, len, &len)) {
- return false;
- }
- wchar_t* sddl = 0;
- if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd,
- SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL))
- return false;
- string16 new_sddl(sddl);
- ::LocalFree(sddl);
- sd = NULL;
- // See MSDN for the security descriptor definition language (SDDL) syntax,
- // in our case we add "A;" generic read 'GR' and generic execute 'GX' for
- // the nt\authenticated_users 'AU' group, that becomes:
- const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)";
- // We should check that there are no special ACES for the group we
- // are interested, which is nt\authenticated_users.
- if (string16::npos != new_sddl.find(L";AU)"))
- return false;
- // Specific ACEs (not inherited) need to go to the front. It is ok if we
- // are the very first one.
- size_t pos_insert = new_sddl.find(L"(");
- if (string16::npos == pos_insert)
- return false;
- // All good, time to change the dacl.
- new_sddl.insert(pos_insert, kAllowACE);
- if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(),
- SDDL_REVISION_1, &sd, NULL))
- return false;
- bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
- sd) == TRUE;
- ::LocalFree(sd);
- return rv;
-}
-
-// This function launches setup as the currently logged-in interactive
-// user that is the user whose logon session is attached to winsta0\default.
-// It assumes that currently we are running as SYSTEM in a non-interactive
-// windowstation.
-// The function fails if there is no interactive session active, basically
-// the computer is on but nobody has logged in locally.
-// Remote Desktop sessions do not count as interactive sessions; running this
-// method as a user logged in via remote desktop will do nothing.
-bool LaunchSetupAsConsoleUser(const base::FilePath& setup_path,
- const installer::Product& product,
- const std::string& flag) {
- CommandLine cmd_line(setup_path);
- cmd_line.AppendSwitch(flag);
-
- // Pass along product-specific options.
- product.AppendProductFlags(&cmd_line);
-
- // Convey to the invoked setup.exe that it's operating on a system-level
- // installation.
- cmd_line.AppendSwitch(installer::switches::kSystemLevel);
-
- // Propagate --verbose-logging to the invoked setup.exe.
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- installer::switches::kVerboseLogging)) {
- cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
- }
-
- // Get the Google Update results key, and pass it on the command line to
- // the child process.
- int key = GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey();
- cmd_line.AppendSwitchASCII(installer::switches::kToastResultsKey,
- base::IntToString(key));
-
- if (base::win::GetVersion() > base::win::VERSION_XP) {
- // Make sure that in Vista and Above we have the proper DACLs so
- // the interactive user can launch it.
- if (!FixDACLsForExecute(setup_path))
- NOTREACHED();
- }
-
- DWORD console_id = ::WTSGetActiveConsoleSessionId();
- if (console_id == 0xFFFFFFFF) {
- PLOG(ERROR) << __FUNCTION__ << " failed to get active session id";
- return false;
- }
- HANDLE user_token;
- if (!::WTSQueryUserToken(console_id, &user_token)) {
- PLOG(ERROR) << __FUNCTION__ << " failed to get user token for console_id "
- << console_id;
- return false;
- }
- // Note: Handle inheritance must be true in order for the child process to be
- // able to use the duplicated handle above (Google Update results).
- base::LaunchOptions options;
- options.as_user = user_token;
- options.inherit_handles = true;
- options.empty_desktop_name = true;
- VLOG(1) << __FUNCTION__ << " launching " << cmd_line.GetCommandLineString();
- bool launched = base::LaunchProcess(cmd_line, options, NULL);
- ::CloseHandle(user_token);
- VLOG(1) << __FUNCTION__ << " result: " << launched;
- return launched;
-}
-
} // namespace
GoogleChromeDistribution::GoogleChromeDistribution()
@@ -569,304 +341,10 @@ void GoogleChromeDistribution::UpdateInstallStatus(bool system_install,
product_guid());
}
-// A helper function that writes to HKLM if the handle was passed through the
-// command line, but HKCU otherwise. |experiment_group| is the value to write
-// and |last_write| is used when writing to HKLM to determine whether to close
-// the handle when done.
-void SetClient(const string16& experiment_group, bool last_write) {
- static int reg_key_handle = -1;
- if (reg_key_handle == -1) {
- // If a specific Toast Results key handle (presumably to our HKLM key) was
- // passed in to the command line (such as for system level installs), we use
- // it. Otherwise, we write to the key under HKCU.
- const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
- if (cmd_line.HasSwitch(installer::switches::kToastResultsKey)) {
- // Get the handle to the key under HKLM.
- base::StringToInt(cmd_line.GetSwitchValueASCII(
- installer::switches::kToastResultsKey).c_str(),
- &reg_key_handle);
- } else {
- reg_key_handle = 0;
- }
- }
-
- if (reg_key_handle) {
- // Use it to write the experiment results.
- GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
- reg_key_handle, google_update::kRegClientField, experiment_group);
- if (last_write)
- CloseHandle((HANDLE) reg_key_handle);
- } else {
- // Write to HKCU.
- GoogleUpdateSettings::SetClient(experiment_group);
- }
-}
-
-bool GoogleChromeDistribution::GetExperimentDetails(
- UserExperiment* experiment, int flavor) {
- struct FlavorDetails {
- int heading_id;
- int flags;
- };
- // Maximum number of experiment flavors we support.
- static const int kMax = 4;
- // This struct determines which experiment flavors we show for each locale and
- // brand.
- //
- // Plugin infobar experiment:
- // The experiment in 2011 used PIxx codes.
- //
- // Inactive user toast experiment:
- // The experiment in Dec 2009 used TGxx and THxx.
- // The experiment in Feb 2010 used TKxx and TLxx.
- // The experiment in Apr 2010 used TMxx and TNxx.
- // The experiment in Oct 2010 used TVxx TWxx TXxx TYxx.
- // The experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
- // The experiment in Mar 2012 used ZAxx ZBxx ZCxx.
- // The experiment in Jan 2013 uses DAxx.
- using namespace attrition_experiments;
-
- static const struct UserExperimentDetails {
- const wchar_t* locale; // Locale to show this experiment for (* for all).
- const wchar_t* brands; // Brand codes show this experiment for (* for all).
- int control_group; // Size of the control group, in percentages.
- const wchar_t* prefix; // The two letter experiment code. The second letter
- // will be incremented with the flavor.
- FlavorDetails flavors[kMax];
- } kExperiments[] = {
- // The first match from top to bottom is used so this list should be ordered
- // most-specific rule first.
- { L"*", L"GGRV", // All locales, GGRV is enterprise.
- 0, // 0 percent control group.
- L"EA", // Experiment is EAxx, EBxx, etc.
- // No flavors means no experiment.
- { { 0, 0 },
- { 0, 0 },
- { 0, 0 },
- { 0, 0 }
- }
- },
- { L"*", L"*", // All locales, all brands.
- 5, // 5 percent control group.
- L"DA", // Experiment is DAxx.
- // One single flavor.
- { { IDS_TRY_TOAST_HEADING3, kMakeDefault },
- { 0, 0 },
- { 0, 0 },
- { 0, 0 }
- }
- }
- };
-
- string16 locale;
- GoogleUpdateSettings::GetLanguage(&locale);
- if (locale.empty() || (locale == ASCIIToWide("en")))
- locale = ASCIIToWide("en-US");
-
- string16 brand;
- if (!GoogleUpdateSettings::GetBrand(&brand))
- brand = ASCIIToWide(""); // Could still be viable for catch-all rules.
-
- for (int i = 0; i < arraysize(kExperiments); ++i) {
- if (kExperiments[i].locale != locale &&
- kExperiments[i].locale != ASCIIToWide("*"))
- continue;
-
- std::vector<string16> brand_codes;
- base::SplitString(kExperiments[i].brands, L',', &brand_codes);
- if (brand_codes.empty())
- return false;
- for (std::vector<string16>::iterator it = brand_codes.begin();
- it != brand_codes.end(); ++it) {
- if (*it != brand && *it != L"*")
- continue;
- // We have found our match.
- const UserExperimentDetails& match = kExperiments[i];
- // Find out how many flavors we have. Zero means no experiment.
- int num_flavors = 0;
- while (match.flavors[num_flavors].heading_id) { ++num_flavors; }
- if (!num_flavors)
- return false;
-
- if (flavor < 0)
- flavor = base::RandInt(0, num_flavors - 1);
- experiment->flavor = flavor;
- experiment->heading = match.flavors[flavor].heading_id;
- experiment->control_group = match.control_group;
- const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
- experiment->prefix = prefix;
- experiment->flags = match.flavors[flavor].flags;
- return true;
- }
- }
-
- return false;
-}
-
-// Currently we only have one experiment: the inactive user toast. Which only
-// applies for users doing upgrades.
-
-//
-// There are three scenarios when this function is called:
-// 1- Is a per-user-install and it updated: perform the experiment
-// 2- Is a system-install and it updated : relaunch as the interactive user
-// 3- It has been re-launched from the #2 case. In this case we enter
-// this function with |system_install| true and a REENTRY_SYS_UPDATE status.
-void GoogleChromeDistribution::LaunchUserExperiment(
- const base::FilePath& setup_path, installer::InstallStatus status,
- const Version& version, const installer::Product& product,
- bool system_level) {
- VLOG(1) << "LaunchUserExperiment status: " << status << " product: "
- << product.distribution()->GetAppShortCutName()
- << " system_level: " << system_level;
-
- if (system_level) {
- if (installer::NEW_VERSION_UPDATED == status) {
- // We need to relaunch as the interactive user.
- LaunchSetupAsConsoleUser(setup_path, product,
- installer::switches::kSystemLevelToast);
- return;
- }
- } else {
- if ((installer::NEW_VERSION_UPDATED != status) &&
- (installer::REENTRY_SYS_UPDATE != status)) {
- // We are not updating or in re-launch. Exit.
- return;
- }
- }
-
- // The |flavor| value ends up being processed by TryChromeDialogView to show
- // different experiments.
- UserExperiment experiment;
- if (!GetExperimentDetails(&experiment, -1)) {
- VLOG(1) << "Failed to get experiment details.";
- return;
- }
- int flavor = experiment.flavor;
- string16 base_group = experiment.prefix;
-
- string16 brand;
- if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) {
- // Testing only: the user automatically qualifies for the experiment.
- VLOG(1) << "Experiment qualification bypass";
- } else {
- // Check that the user was not already drafted in this experiment.
- string16 client;
- GoogleUpdateSettings::GetClient(&client);
- if (client.size() > 2) {
- if (base_group == client.substr(0, 2)) {
- VLOG(1) << "User already participated in this experiment";
- return;
- }
- }
- // Check browser usage inactivity by the age of the last-write time of the
- // most recently-used chrome user data directory.
- std::vector<base::FilePath> user_data_dirs;
- product.GetUserDataPaths(&user_data_dirs);
- int dir_age_hours = -1;
- for (size_t i = 0; i < user_data_dirs.size(); ++i) {
- int this_age = GetDirectoryWriteAgeInHours(
- user_data_dirs[i].value().c_str());
- if (this_age >= 0 && (dir_age_hours < 0 || this_age < dir_age_hours))
- dir_age_hours = this_age;
- }
-
- const bool experiment_enabled = false;
- const int kThirtyDays = 30 * 24;
-
- if (!experiment_enabled) {
- VLOG(1) << "Toast experiment is disabled.";
- return;
- } else if (dir_age_hours < 0) {
- // This means that we failed to find the user data dir. The most likely
- // cause is that this user has not ever used chrome at all which can
- // happen in a system-level install.
- SetClient(base_group + kToastUDDirFailure, true);
- return;
- } else if (dir_age_hours < kThirtyDays) {
- // An active user, so it does not qualify.
- VLOG(1) << "Chrome used in last " << dir_age_hours << " hours";
- SetClient(base_group + kToastActiveGroup, true);
- return;
- }
- // Check to see if this user belongs to the control group.
- double control_group = 1.0 * (100 - experiment.control_group) / 100;
- if (base::RandDouble() > control_group) {
- SetClient(base_group + kToastExpControlGroup, true);
- VLOG(1) << "User is control group";
- return;
- }
- }
-
- VLOG(1) << "User drafted for toast experiment " << flavor;
- SetClient(base_group + kToastExpBaseGroup, false);
- // User level: The experiment needs to be performed in a different process
- // because google_update expects the upgrade process to be quick and nimble.
- // System level: We have already been relaunched, so we don't need to be
- // quick, but we relaunch to follow the exact same codepath.
- CommandLine cmd_line(setup_path);
- cmd_line.AppendSwitchASCII(installer::switches::kInactiveUserToast,
- base::IntToString(flavor));
- cmd_line.AppendSwitchASCII(installer::switches::kExperimentGroup,
- WideToASCII(base_group));
- LaunchSetup(&cmd_line, product, system_level);
-}
-
-// User qualifies for the experiment. To test, use --try-chrome-again=|flavor|
-// as a parameter to chrome.exe.
-void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor,
- const string16& experiment_group,
- const installer::Product& installation,
- const base::FilePath& application_path) {
- // Add the 'welcome back' url for chrome to show.
- CommandLine options(CommandLine::NO_PROGRAM);
- options.AppendSwitchNative(switches::kTryChromeAgain,
- base::IntToString16(flavor));
- // Prepend the url with a space.
- string16 url(GetWelcomeBackUrl());
- options.AppendArg("--");
- options.AppendArgNative(url);
- // The command line should now have the url added as:
- // "chrome.exe -- <url>"
- DCHECK_NE(string16::npos,
- options.GetCommandLineString().find(L" -- " + url));
-
- // Launch chrome now. It will show the toast UI.
- int32 exit_code = 0;
- if (!installation.LaunchChromeAndWait(application_path, options, &exit_code))
- return;
-
- // The chrome process has exited, figure out what happened.
- const wchar_t* outcome = NULL;
- switch (exit_code) {
- case content::RESULT_CODE_NORMAL_EXIT:
- outcome = kToastExpTriesOkGroup;
- break;
- case chrome::RESULT_CODE_NORMAL_EXIT_CANCEL:
- outcome = kToastExpCancelGroup;
- break;
- case chrome::RESULT_CODE_NORMAL_EXIT_EXP2:
- outcome = kToastExpUninstallGroup;
- break;
- default:
- outcome = kToastExpTriesErrorGroup;
- };
- // Write to the |client| key for the last time.
- SetClient(experiment_group + outcome, true);
-
- if (outcome != kToastExpUninstallGroup)
- return;
- // The user wants to uninstall. This is a best effort operation. Note that
- // we waited for chrome to exit so the uninstall would not detect chrome
- // running.
- bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch(
- installer::switches::kSystemLevelToast);
-
- CommandLine cmd(InstallUtil::GetChromeUninstallCmd(system_level_toast,
- GetType()));
- base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
+bool GoogleChromeDistribution::ShouldSetExperimentLabels() {
+ return true;
}
-bool GoogleChromeDistribution::ShouldSetExperimentLabels() {
+bool GoogleChromeDistribution::HasUserExperiments() {
return true;
}
diff --git a/chrome/installer/util/google_chrome_distribution.h b/chrome/installer/util/google_chrome_distribution.h
index 32bacd0..4e0012f 100644
--- a/chrome/installer/util/google_chrome_distribution.h
+++ b/chrome/installer/util/google_chrome_distribution.h
@@ -11,7 +11,6 @@
#include "base/string16.h"
#include "base/gtest_prod_util.h"
#include "chrome/installer/util/browser_distribution.h"
-#include "chrome/installer/util/util_constants.h"
namespace base {
class DictionaryValue;
@@ -87,27 +86,10 @@ class GoogleChromeDistribution : public BrowserDistribution {
installer::ArchiveType archive_type,
installer::InstallStatus install_status) OVERRIDE;
- virtual bool GetExperimentDetails(UserExperiment* experiment,
- int flavor) OVERRIDE;
-
- virtual void LaunchUserExperiment(
- const base::FilePath& setup_path,
- installer::InstallStatus status,
- const Version& version,
- const installer::Product& product,
- bool system_level) OVERRIDE;
-
- // Assuming that the user qualifies, this function performs the inactive user
- // toast experiment. It will use chrome to show the UI and it will record the
- // outcome in the registry.
- virtual void InactiveUserToastExperiment(
- int flavor,
- const string16& experiment_group,
- const installer::Product& installation,
- const base::FilePath& application_path) OVERRIDE;
-
virtual bool ShouldSetExperimentLabels() OVERRIDE;
+ virtual bool HasUserExperiments() OVERRIDE;
+
const string16& product_guid() { return product_guid_; }
protected:
diff --git a/chrome/installer/util/google_chrome_distribution_dummy.cc b/chrome/installer/util/google_chrome_distribution_dummy.cc
index 5ac90f7..e517cc1 100644
--- a/chrome/installer/util/google_chrome_distribution_dummy.cc
+++ b/chrome/installer/util/google_chrome_distribution_dummy.cc
@@ -143,26 +143,11 @@ void GoogleChromeDistribution::UpdateInstallStatus(bool system_install,
NOTREACHED();
}
-bool GoogleChromeDistribution::GetExperimentDetails(
- UserExperiment* experiment, int flavor) {
+bool GoogleChromeDistribution::ShouldSetExperimentLabels() {
NOTREACHED();
return false;
}
-void GoogleChromeDistribution::LaunchUserExperiment(
- const base::FilePath& setup_path, installer::InstallStatus status,
- const Version& version, const installer::Product& product,
- bool system_level) {
- NOTREACHED();
-}
-
-void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor,
- const string16& experiment_group,
- const installer::Product& installation,
- const base::FilePath& application_path) {
- NOTREACHED();
-}
-
bool GoogleChromeDistribution::ExtractUninstallMetricsFromFile(
const base::FilePath& file_path, string16* uninstall_metrics_string) {
NOTREACHED();
@@ -181,7 +166,7 @@ bool GoogleChromeDistribution::BuildUninstallMetricsString(
return false;
}
-bool GoogleChromeDistribution::ShouldSetExperimentLabels() {
+bool GoogleChromeDistribution::HasUserExperiments() {
NOTREACHED();
return false;
}
diff --git a/chrome/installer/util/google_chrome_sxs_distribution.cc b/chrome/installer/util/google_chrome_sxs_distribution.cc
index c1c7610..5224a8c 100644
--- a/chrome/installer/util/google_chrome_sxs_distribution.cc
+++ b/chrome/installer/util/google_chrome_sxs_distribution.cc
@@ -75,6 +75,10 @@ bool GoogleChromeSxSDistribution::ShouldSetExperimentLabels() {
return true;
}
+bool GoogleChromeSxSDistribution::HasUserExperiments() {
+ return true;
+}
+
string16 GoogleChromeSxSDistribution::ChannelName() {
return kChannelName;
}
diff --git a/chrome/installer/util/google_chrome_sxs_distribution.h b/chrome/installer/util/google_chrome_sxs_distribution.h
index 9f5e14e..c1e4df5 100644
--- a/chrome/installer/util/google_chrome_sxs_distribution.h
+++ b/chrome/installer/util/google_chrome_sxs_distribution.h
@@ -31,6 +31,7 @@ class GoogleChromeSxSDistribution : public GoogleChromeDistribution {
string16* handler_class_uuid) OVERRIDE;
virtual bool AppHostIsSupported() OVERRIDE;
virtual bool ShouldSetExperimentLabels() OVERRIDE;
+ virtual bool HasUserExperiments() OVERRIDE;
// returns the channel name for GoogleChromeSxSDistribution
static string16 ChannelName();
private:
diff --git a/chrome/installer/util/product.cc b/chrome/installer/util/product.cc
index bc3e6b3..bb8c963 100644
--- a/chrome/installer/util/product.cc
+++ b/chrome/installer/util/product.cc
@@ -163,4 +163,16 @@ void Product::AddDefaultShortcutProperties(
distribution_, target_exe, properties);
}
+void Product::LaunchUserExperiment(const base::FilePath& setup_path,
+ InstallStatus status,
+ bool system_level) const {
+ if (distribution_->HasUserExperiments()) {
+ VLOG(1) << "LaunchUserExperiment status: " << status << " product: "
+ << distribution_->GetAppShortCutName()
+ << " system_level: " << system_level;
+ operations_->LaunchUserExperiment(
+ setup_path, options_, status, system_level);
+ }
+}
+
} // namespace installer
diff --git a/chrome/installer/util/product.h b/chrome/installer/util/product.h
index cba5bc2..9ff763b 100644
--- a/chrome/installer/util/product.h
+++ b/chrome/installer/util/product.h
@@ -12,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/util_constants.h"
class CommandLine;
@@ -129,6 +130,10 @@ class Product {
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const;
+ void LaunchUserExperiment(const base::FilePath& setup_path,
+ InstallStatus status,
+ bool system_level) const;
+
protected:
enum CacheStateFlags {
MSI_STATE = 0x01
diff --git a/chrome/installer/util/product_operations.h b/chrome/installer/util/product_operations.h
index 77d094b..c826c7e 100644
--- a/chrome/installer/util/product_operations.h
+++ b/chrome/installer/util/product_operations.h
@@ -10,7 +10,9 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/string16.h"
#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/util_constants.h"
class BrowserDistribution;
class CommandLine;
@@ -30,11 +32,11 @@ class ProductOperations {
// Reads product-specific options from |prefs|, adding them to |options|.
virtual void ReadOptions(const MasterPreferences& prefs,
- std::set<std::wstring>* options) const = 0;
+ std::set<string16>* options) const = 0;
// Reads product-specific options from |command|, adding them to |options|.
virtual void ReadOptions(const CommandLine& command,
- std::set<std::wstring>* options) const = 0;
+ std::set<string16>* options) const = 0;
// A key-file is a file such as a DLL on Windows that is expected to be in use
// when the product is being used. For example "chrome.dll" for Chrome.
@@ -44,28 +46,28 @@ class ProductOperations {
// none of the key files are in use, can the folder be deleted. Note that
// this function does not return a full path to the key file(s), only (a) file
// name(s).
- virtual void AddKeyFiles(const std::set<std::wstring>& options,
+ virtual void AddKeyFiles(const std::set<string16>& options,
std::vector<base::FilePath>* key_files) const = 0;
// Adds to |com_dll_list| the list of COM DLLs that are to be registered
// and/or unregistered. The list may be empty.
virtual void AddComDllList(
- const std::set<std::wstring>& options,
+ const std::set<string16>& options,
std::vector<base::FilePath>* com_dll_list) const = 0;
// Given a command line, appends the set of product-specific flags. These are
// required for product-specific uninstall commands, but are of use for any
// invocation of setup.exe for the product.
- virtual void AppendProductFlags(const std::set<std::wstring>& options,
+ virtual void AppendProductFlags(const std::set<string16>& options,
CommandLine* cmd_line) const = 0;
// Given a command line, appends the set of product-specific rename flags.
- virtual void AppendRenameFlags(const std::set<std::wstring>& options,
+ virtual void AppendRenameFlags(const std::set<string16>& options,
CommandLine* cmd_line) const = 0;
// Adds or removes product-specific flags in |channel_info|. Returns true if
// |channel_info| is modified.
- virtual bool SetChannelFlags(const std::set<std::wstring>& options,
+ virtual bool SetChannelFlags(const std::set<string16>& options,
bool set,
ChannelInfo* channel_info) const = 0;
@@ -73,7 +75,7 @@ class ProductOperations {
// of installed applications for this product. This does not test for use of
// MSI; see InstallerState::is_msi.
virtual bool ShouldCreateUninstallEntry(
- const std::set<std::wstring>& options) const = 0;
+ const std::set<string16>& options) const = 0;
// Modifies a ShellUtil::ShortcutProperties object by assigning default values
// to unintialized members.
@@ -81,6 +83,14 @@ class ProductOperations {
BrowserDistribution* dist,
const base::FilePath& target_exe,
ShellUtil::ShortcutProperties* properties) const = 0;
+
+ // After an install or upgrade the user might qualify to participate in an
+ // experiment. This function determines if the user qualifies and if so it
+ // sets the wheels in motion or in simple cases does the experiment itself.
+ virtual void LaunchUserExperiment(const base::FilePath& setup_path,
+ const std::set<string16>& options,
+ InstallStatus status,
+ bool system_level) const = 0;
};
} // namespace installer
diff --git a/chrome/installer/util/user_experiment.cc b/chrome/installer/util/user_experiment.cc
new file mode 100644
index 0000000..27eca57
--- /dev/null
+++ b/chrome/installer/util/user_experiment.cc
@@ -0,0 +1,538 @@
+// Copyright 2013 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/installer/util/user_experiment.h"
+
+#include <windows.h>
+#include <sddl.h>
+#include <wtsapi32.h>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/attrition_experiments.h"
+#include "chrome/common/chrome_result_codes.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "chrome/installer/util/google_update_settings.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/product.h"
+#include "content/public/common/result_codes.h"
+
+#pragma comment(lib, "wtsapi32.lib")
+
+namespace installer {
+
+namespace {
+
+// The following strings are the possible outcomes of the toast experiment
+// as recorded in the |client| field.
+const wchar_t kToastExpControlGroup[] = L"01";
+const wchar_t kToastExpCancelGroup[] = L"02";
+const wchar_t kToastExpUninstallGroup[] = L"04";
+const wchar_t kToastExpTriesOkGroup[] = L"18";
+const wchar_t kToastExpTriesErrorGroup[] = L"28";
+const wchar_t kToastActiveGroup[] = L"40";
+const wchar_t kToastUDDirFailure[] = L"40";
+const wchar_t kToastExpBaseGroup[] = L"80";
+
+// Substitute the locale parameter in uninstall URL with whatever
+// Google Update tells us is the locale. In case we fail to find
+// the locale, we use US English.
+string16 LocalizeUrl(const wchar_t* url) {
+ string16 language;
+ if (!GoogleUpdateSettings::GetLanguage(&language))
+ language = L"en-US"; // Default to US English.
+ return ReplaceStringPlaceholders(url, language.c_str(), NULL);
+}
+
+string16 GetWelcomeBackUrl() {
+ const wchar_t kWelcomeUrl[] = L"http://www.google.com/chrome/intl/$1/"
+ L"welcomeback-new.html";
+ return LocalizeUrl(kWelcomeUrl);
+}
+
+// Converts FILETIME to hours. FILETIME times are absolute times in
+// 100 nanosecond units. For example 5:30 pm of June 15, 2009 is 3580464.
+int FileTimeToHours(const FILETIME& time) {
+ const ULONGLONG k100sNanoSecsToHours = 10000000LL * 60 * 60;
+ ULARGE_INTEGER uli = {time.dwLowDateTime, time.dwHighDateTime};
+ return static_cast<int>(uli.QuadPart / k100sNanoSecsToHours);
+}
+
+// Returns the directory last write time in hours since January 1, 1601.
+// Returns -1 if there was an error retrieving the directory time.
+int GetDirectoryWriteTimeInHours(const wchar_t* path) {
+ // To open a directory you need to pass FILE_FLAG_BACKUP_SEMANTICS.
+ DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ base::win::ScopedHandle file(::CreateFileW(path, 0, share, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
+ if (!file.IsValid())
+ return -1;
+
+ FILETIME time;
+ return ::GetFileTime(file, NULL, NULL, &time) ? FileTimeToHours(time) : -1;
+}
+
+// Returns the directory last-write time age in hours, relative to current
+// time, so if it returns 14 it means that the directory was last written 14
+// hours ago. Returns -1 if there was an error retrieving the directory.
+int GetDirectoryWriteAgeInHours(const wchar_t* path) {
+ int dir_time = GetDirectoryWriteTimeInHours(path);
+ if (dir_time < 0)
+ return dir_time;
+ FILETIME time;
+ GetSystemTimeAsFileTime(&time);
+ int now_time = FileTimeToHours(time);
+ if (dir_time >= now_time)
+ return 0;
+ return (now_time - dir_time);
+}
+
+// Launches setup.exe (located at |setup_path|) with |cmd_line|.
+// If system_level_toast is true, appends --system-level-toast.
+// If handle to experiment result key was given at startup, re-add it.
+// Does not wait for the process to terminate.
+// |cmd_line| may be modified as a result of this call.
+bool LaunchSetup(CommandLine* cmd_line, bool system_level_toast) {
+ const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
+
+ // Propagate --verbose-logging to the invoked setup.exe.
+ if (current_cmd_line.HasSwitch(switches::kVerboseLogging))
+ cmd_line->AppendSwitch(switches::kVerboseLogging);
+
+ // Re-add the system level toast flag.
+ if (system_level_toast) {
+ cmd_line->AppendSwitch(switches::kSystemLevel);
+ cmd_line->AppendSwitch(switches::kSystemLevelToast);
+
+ // Re-add the toast result key. We need to do this because Setup running as
+ // system passes the key to Setup running as user, but that child process
+ // does not perform the actual toasting, it launches another Setup (as user)
+ // to do so. That is the process that needs the key.
+ std::string key(switches::kToastResultsKey);
+ std::string toast_key = current_cmd_line.GetSwitchValueASCII(key);
+ if (!toast_key.empty()) {
+ cmd_line->AppendSwitchASCII(key, toast_key);
+
+ // Use handle inheritance to make sure the duplicated toast results key
+ // gets inherited by the child process.
+ base::LaunchOptions options;
+ options.inherit_handles = true;
+ return base::LaunchProcess(*cmd_line, options, NULL);
+ }
+ }
+
+ return base::LaunchProcess(*cmd_line, base::LaunchOptions(), NULL);
+}
+
+// For System level installs, setup.exe lives in the system temp, which
+// is normally c:\windows\temp. In many cases files inside this folder
+// are not accessible for execution by regular user accounts.
+// This function changes the permissions so that any authenticated user
+// can launch |exe| later on. This function should only be called if the
+// code is running at the system level.
+bool FixDACLsForExecute(const base::FilePath& exe) {
+ // The general strategy to is to add an ACE to the exe DACL the quick
+ // and dirty way: a) read the DACL b) convert it to sddl string c) add the
+ // new ACE to the string d) convert sddl string back to DACL and finally
+ // e) write new dacl.
+ char buff[1024];
+ DWORD len = sizeof(buff);
+ PSECURITY_DESCRIPTOR sd = reinterpret_cast<PSECURITY_DESCRIPTOR>(buff);
+ if (!::GetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
+ sd, len, &len)) {
+ return false;
+ }
+ wchar_t* sddl = 0;
+ if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd,
+ SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &sddl, NULL))
+ return false;
+ string16 new_sddl(sddl);
+ ::LocalFree(sddl);
+ sd = NULL;
+ // See MSDN for the security descriptor definition language (SDDL) syntax,
+ // in our case we add "A;" generic read 'GR' and generic execute 'GX' for
+ // the nt\authenticated_users 'AU' group, that becomes:
+ const wchar_t kAllowACE[] = L"(A;;GRGX;;;AU)";
+ // We should check that there are no special ACES for the group we
+ // are interested, which is nt\authenticated_users.
+ if (string16::npos != new_sddl.find(L";AU)"))
+ return false;
+ // Specific ACEs (not inherited) need to go to the front. It is ok if we
+ // are the very first one.
+ size_t pos_insert = new_sddl.find(L"(");
+ if (string16::npos == pos_insert)
+ return false;
+ // All good, time to change the dacl.
+ new_sddl.insert(pos_insert, kAllowACE);
+ if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(new_sddl.c_str(),
+ SDDL_REVISION_1, &sd, NULL))
+ return false;
+ bool rv = ::SetFileSecurityW(exe.value().c_str(), DACL_SECURITY_INFORMATION,
+ sd) == TRUE;
+ ::LocalFree(sd);
+ return rv;
+}
+
+// This function launches setup as the currently logged-in interactive
+// user that is the user whose logon session is attached to winsta0\default.
+// It assumes that currently we are running as SYSTEM in a non-interactive
+// windowstation.
+// The function fails if there is no interactive session active, basically
+// the computer is on but nobody has logged in locally.
+// Remote Desktop sessions do not count as interactive sessions; running this
+// method as a user logged in via remote desktop will do nothing.
+bool LaunchSetupAsConsoleUser(CommandLine* cmd_line) {
+ // Convey to the invoked setup.exe that it's operating on a system-level
+ // installation.
+ cmd_line->AppendSwitch(switches::kSystemLevel);
+
+ // Propagate --verbose-logging to the invoked setup.exe.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kVerboseLogging))
+ cmd_line->AppendSwitch(switches::kVerboseLogging);
+
+ // Get the Google Update results key, and pass it on the command line to
+ // the child process.
+ int key = GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey();
+ cmd_line->AppendSwitchASCII(switches::kToastResultsKey,
+ base::IntToString(key));
+
+ if (base::win::GetVersion() > base::win::VERSION_XP) {
+ // Make sure that in Vista and Above we have the proper DACLs so
+ // the interactive user can launch it.
+ if (!FixDACLsForExecute(cmd_line->GetProgram()))
+ NOTREACHED();
+ }
+
+ DWORD console_id = ::WTSGetActiveConsoleSessionId();
+ if (console_id == 0xFFFFFFFF) {
+ PLOG(ERROR) << __FUNCTION__ << " failed to get active session id";
+ return false;
+ }
+ HANDLE user_token;
+ if (!::WTSQueryUserToken(console_id, &user_token)) {
+ PLOG(ERROR) << __FUNCTION__ << " failed to get user token for console_id "
+ << console_id;
+ return false;
+ }
+ // Note: Handle inheritance must be true in order for the child process to be
+ // able to use the duplicated handle above (Google Update results).
+ base::LaunchOptions options;
+ options.as_user = user_token;
+ options.inherit_handles = true;
+ options.empty_desktop_name = true;
+ VLOG(1) << __FUNCTION__ << " launching " << cmd_line->GetCommandLineString();
+ bool launched = base::LaunchProcess(*cmd_line, options, NULL);
+ ::CloseHandle(user_token);
+ VLOG(1) << __FUNCTION__ << " result: " << launched;
+ return launched;
+}
+
+// A helper function that writes to HKLM if the handle was passed through the
+// command line, but HKCU otherwise. |experiment_group| is the value to write
+// and |last_write| is used when writing to HKLM to determine whether to close
+// the handle when done.
+void SetClient(const string16& experiment_group, bool last_write) {
+ static int reg_key_handle = -1;
+ if (reg_key_handle == -1) {
+ // If a specific Toast Results key handle (presumably to our HKLM key) was
+ // passed in to the command line (such as for system level installs), we use
+ // it. Otherwise, we write to the key under HKCU.
+ const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
+ if (cmd_line.HasSwitch(switches::kToastResultsKey)) {
+ // Get the handle to the key under HKLM.
+ base::StringToInt(
+ cmd_line.GetSwitchValueNative(switches::kToastResultsKey),
+ &reg_key_handle);
+ } else {
+ reg_key_handle = 0;
+ }
+ }
+
+ if (reg_key_handle) {
+ // Use it to write the experiment results.
+ GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
+ reg_key_handle, google_update::kRegClientField, experiment_group);
+ if (last_write)
+ CloseHandle((HANDLE) reg_key_handle);
+ } else {
+ // Write to HKCU.
+ GoogleUpdateSettings::SetClient(experiment_group);
+ }
+}
+
+} // namespace
+
+bool CreateExperimentDetails(int flavor, ExperimentDetails* experiment) {
+ struct FlavorDetails {
+ int heading_id;
+ int flags;
+ };
+ // Maximum number of experiment flavors we support.
+ static const int kMax = 4;
+ // This struct determines which experiment flavors we show for each locale and
+ // brand.
+ //
+ // Plugin infobar experiment:
+ // The experiment in 2011 used PIxx codes.
+ //
+ // Inactive user toast experiment:
+ // The experiment in Dec 2009 used TGxx and THxx.
+ // The experiment in Feb 2010 used TKxx and TLxx.
+ // The experiment in Apr 2010 used TMxx and TNxx.
+ // The experiment in Oct 2010 used TVxx TWxx TXxx TYxx.
+ // The experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
+ // The experiment in Mar 2012 used ZAxx ZBxx ZCxx.
+ // The experiment in Jan 2013 uses DAxx.
+ using namespace attrition_experiments;
+
+ static const struct UserExperimentSpecs {
+ const wchar_t* locale; // Locale to show this experiment for (* for all).
+ const wchar_t* brands; // Brand codes show this experiment for (* for all).
+ int control_group; // Size of the control group, in percentages.
+ const wchar_t* prefix; // The two letter experiment code. The second letter
+ // will be incremented with the flavor.
+ FlavorDetails flavors[kMax];
+ } kExperiments[] = {
+ // The first match from top to bottom is used so this list should be ordered
+ // most-specific rule first.
+ { L"*", L"GGRV", // All locales, GGRV is enterprise.
+ 0, // 0 percent control group.
+ L"EA", // Experiment is EAxx, EBxx, etc.
+ // No flavors means no experiment.
+ { { 0, 0 },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 }
+ }
+ },
+ { L"*", L"*", // All locales, all brands.
+ 5, // 5 percent control group.
+ L"DA", // Experiment is DAxx.
+ // One single flavor.
+ { { IDS_TRY_TOAST_HEADING3, kToastUiMakeDefault },
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 }
+ }
+ }
+ };
+
+ string16 locale;
+ GoogleUpdateSettings::GetLanguage(&locale);
+ if (locale.empty() || (locale == ASCIIToWide("en")))
+ locale = ASCIIToWide("en-US");
+
+ string16 brand;
+ if (!GoogleUpdateSettings::GetBrand(&brand))
+ brand = ASCIIToWide(""); // Could still be viable for catch-all rules.
+
+ for (int i = 0; i < arraysize(kExperiments); ++i) {
+ if (kExperiments[i].locale != locale &&
+ kExperiments[i].locale != ASCIIToWide("*"))
+ continue;
+
+ std::vector<string16> brand_codes;
+ base::SplitString(kExperiments[i].brands, L',', &brand_codes);
+ if (brand_codes.empty())
+ return false;
+ for (std::vector<string16>::iterator it = brand_codes.begin();
+ it != brand_codes.end(); ++it) {
+ if (*it != brand && *it != L"*")
+ continue;
+ // We have found our match.
+ const UserExperimentSpecs& match = kExperiments[i];
+ // Find out how many flavors we have. Zero means no experiment.
+ int num_flavors = 0;
+ while (match.flavors[num_flavors].heading_id) { ++num_flavors; }
+ if (!num_flavors)
+ return false;
+
+ if (flavor < 0)
+ flavor = base::RandInt(0, num_flavors - 1);
+ experiment->flavor = flavor;
+ experiment->heading = match.flavors[flavor].heading_id;
+ experiment->control_group = match.control_group;
+ const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
+ experiment->prefix = prefix;
+ experiment->flags = match.flavors[flavor].flags;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Currently we only have one experiment: the inactive user toast. Which only
+// applies for users doing upgrades.
+
+// There are three scenarios when this function is called:
+// 1- Is a per-user-install and it updated: perform the experiment
+// 2- Is a system-install and it updated : relaunch as the interactive user
+// 3- It has been re-launched from the #2 case. In this case we enter
+// this function with |system_install| true and a REENTRY_SYS_UPDATE status.
+void LaunchBrowserUserExperiment(const CommandLine& base_cmd_line,
+ InstallStatus status,
+ bool system_level) {
+ if (system_level) {
+ if (NEW_VERSION_UPDATED == status) {
+ CommandLine cmd_line(base_cmd_line);
+ cmd_line.AppendSwitch(switches::kSystemLevelToast);
+ // We need to relaunch as the interactive user.
+ LaunchSetupAsConsoleUser(&cmd_line);
+ return;
+ }
+ } else {
+ if ((NEW_VERSION_UPDATED != status) && (REENTRY_SYS_UPDATE != status)) {
+ // We are not updating or in re-launch. Exit.
+ return;
+ }
+ }
+
+ // The |flavor| value ends up being processed by TryChromeDialogView to show
+ // different experiments.
+ ExperimentDetails experiment;
+ if (!CreateExperimentDetails(-1, &experiment)) {
+ VLOG(1) << "Failed to get experiment details.";
+ return;
+ }
+ int flavor = experiment.flavor;
+ string16 base_group = experiment.prefix;
+
+ string16 brand;
+ if (GoogleUpdateSettings::GetBrand(&brand) && (brand == L"CHXX")) {
+ // Testing only: the user automatically qualifies for the experiment.
+ VLOG(1) << "Experiment qualification bypass";
+ } else {
+ // Check that the user was not already drafted in this experiment.
+ string16 client;
+ GoogleUpdateSettings::GetClient(&client);
+ if (client.size() > 2) {
+ if (base_group == client.substr(0, 2)) {
+ VLOG(1) << "User already participated in this experiment";
+ return;
+ }
+ }
+ // Check browser usage inactivity by the age of the last-write time of the
+ // most recently-used chrome user data directory.
+ std::vector<base::FilePath> user_data_dirs;
+ BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_BROWSER);
+ GetChromeUserDataPaths(dist, &user_data_dirs);
+ int dir_age_hours = -1;
+ for (size_t i = 0; i < user_data_dirs.size(); ++i) {
+ int this_age = GetDirectoryWriteAgeInHours(
+ user_data_dirs[i].value().c_str());
+ if (this_age >= 0 && (dir_age_hours < 0 || this_age < dir_age_hours))
+ dir_age_hours = this_age;
+ }
+
+ const bool experiment_enabled = false;
+ const int kThirtyDays = 30 * 24;
+
+ if (!experiment_enabled) {
+ VLOG(1) << "Toast experiment is disabled.";
+ return;
+ } else if (dir_age_hours < 0) {
+ // This means that we failed to find the user data dir. The most likely
+ // cause is that this user has not ever used chrome at all which can
+ // happen in a system-level install.
+ SetClient(base_group + kToastUDDirFailure, true);
+ return;
+ } else if (dir_age_hours < kThirtyDays) {
+ // An active user, so it does not qualify.
+ VLOG(1) << "Chrome used in last " << dir_age_hours << " hours";
+ SetClient(base_group + kToastActiveGroup, true);
+ return;
+ }
+ // Check to see if this user belongs to the control group.
+ double control_group = 1.0 * (100 - experiment.control_group) / 100;
+ if (base::RandDouble() > control_group) {
+ SetClient(base_group + kToastExpControlGroup, true);
+ VLOG(1) << "User is control group";
+ return;
+ }
+ }
+
+ VLOG(1) << "User drafted for toast experiment " << flavor;
+ SetClient(base_group + kToastExpBaseGroup, false);
+ // User level: The experiment needs to be performed in a different process
+ // because google_update expects the upgrade process to be quick and nimble.
+ // System level: We have already been relaunched, so we don't need to be
+ // quick, but we relaunch to follow the exact same codepath.
+ CommandLine cmd_line(base_cmd_line);
+ cmd_line.AppendSwitchASCII(switches::kInactiveUserToast,
+ base::IntToString(flavor));
+ cmd_line.AppendSwitchASCII(switches::kExperimentGroup,
+ WideToASCII(base_group));
+ LaunchSetup(&cmd_line, system_level);
+}
+
+// User qualifies for the experiment. To test, use --try-chrome-again=|flavor|
+// as a parameter to chrome.exe.
+void InactiveUserToastExperiment(int flavor,
+ const string16& experiment_group,
+ const Product& installation,
+ const base::FilePath& application_path) {
+ // Add the 'welcome back' url for chrome to show.
+ CommandLine options(CommandLine::NO_PROGRAM);
+ options.AppendSwitchNative(::switches::kTryChromeAgain,
+ base::IntToString16(flavor));
+ // Prepend the url with a space.
+ string16 url(GetWelcomeBackUrl());
+ options.AppendArg("--");
+ options.AppendArgNative(url);
+ // The command line should now have the url added as:
+ // "chrome.exe -- <url>"
+ DCHECK_NE(string16::npos,
+ options.GetCommandLineString().find(L" -- " + url));
+
+ // Launch chrome now. It will show the toast UI.
+ int32 exit_code = 0;
+ if (!installation.LaunchChromeAndWait(application_path, options, &exit_code))
+ return;
+
+ // The chrome process has exited, figure out what happened.
+ const wchar_t* outcome = NULL;
+ switch (exit_code) {
+ case content::RESULT_CODE_NORMAL_EXIT:
+ outcome = kToastExpTriesOkGroup;
+ break;
+ case chrome::RESULT_CODE_NORMAL_EXIT_CANCEL:
+ outcome = kToastExpCancelGroup;
+ break;
+ case chrome::RESULT_CODE_NORMAL_EXIT_EXP2:
+ outcome = kToastExpUninstallGroup;
+ break;
+ default:
+ outcome = kToastExpTriesErrorGroup;
+ };
+ // Write to the |client| key for the last time.
+ SetClient(experiment_group + outcome, true);
+
+ if (outcome != kToastExpUninstallGroup)
+ return;
+ // The user wants to uninstall. This is a best effort operation. Note that
+ // we waited for chrome to exit so the uninstall would not detect chrome
+ // running.
+ bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSystemLevelToast);
+
+ CommandLine cmd(InstallUtil::GetChromeUninstallCmd(system_level_toast,
+ installation.distribution()->GetType()));
+ base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
+}
+
+} // namespace installer
diff --git a/chrome/installer/util/user_experiment.h b/chrome/installer/util/user_experiment.h
new file mode 100644
index 0000000..c7da899
--- /dev/null
+++ b/chrome/installer/util/user_experiment.h
@@ -0,0 +1,68 @@
+// Copyright 2013 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.
+//
+// This files declares a class that contains methods and data to conduct
+// for user expeirments.
+
+#ifndef CHROME_INSTALLER_UTIL_USER_EXPERIMENT_H_
+#define CHROME_INSTALLER_UTIL_USER_EXPERIMENT_H_
+
+#include "base/string16.h"
+#include "chrome/installer/util/util_constants.h"
+
+class CommandLine;
+
+namespace base {
+class FilePath;
+}
+
+namespace installer {
+
+class Product;
+
+// Flags to control what to show in the UserExperiment dialog.
+enum ToastUiFlags {
+ kToastUiUninstall = 1 << 0, // Uninstall radio button.
+ kToastUiDontBugMeAsButton = 1 << 1, // This is a button, not a radio button.
+ kToastUiWhyLink = 1 << 2, // Has the 'why I am seeing this' link.
+ kToastUiMakeDefault = 1 << 3, // Has the 'make it default' checkbox.
+};
+
+// A struct for communicating what a UserExperiment contains. In these
+// experiments we show toasts to the user if they are inactive for a certain
+// amount of time.
+struct ExperimentDetails {
+ string16 prefix; // The experiment code prefix for this experiment,
+ // also known as the 'TV' part in 'TV80'.
+ int flavor; // The flavor index for this experiment.
+ int heading; // The heading resource ID to use for this experiment.
+ int flags; // See ToastUIFlags above.
+ int control_group; // Size of the control group (in percentages). Control
+ // group is the group that qualifies for the
+ // experiment but does not participate.
+};
+
+// Creates the experiment details for a given language-brand combo.
+// If |flavor| is -1, then a flavor will be selected at random. |experiment|
+// is the struct you want to write the experiment information to.
+// Returns false if no experiment details could be gathered.
+bool CreateExperimentDetails(int flavor, ExperimentDetails* experiment);
+
+// After an install or upgrade the user might qualify to participate in an
+// experiment. This function determines if the user qualifies and if so it
+// sets the wheels in motion or in simple cases does the experiment itself.
+void LaunchBrowserUserExperiment(const CommandLine& base_command,
+ InstallStatus status,
+ bool system_level);
+
+// The user has qualified for the inactive user toast experiment and this
+// function just performs it.
+void InactiveUserToastExperiment(int flavor,
+ const string16& experiment_group,
+ const Product& installation,
+ const base::FilePath& application_path);
+
+} // namespace installer
+
+#endif // CHROME_INSTALLER_UTIL_USER_EXPERIMENT_H_