Commit d89125b5 authored by cpu@chromium.org's avatar cpu@chromium.org

The new toast experiments.

I've done some generalizing of the code and added the bits to show the new
UI designs. Also fixed some bugs. Gone is the idea of 'compact' toast and now
we just specify the flags we want.

BUG=129499
TEST=see bug
Review URL: https://chromiumcodereview.appspot.com/10821007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149257 0039d316-1c4b-4281-b951-d872f2087c98
parent 9514bd91
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/radio_button.h" #include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/button/text_button.h" #include "ui/views/controls/button/text_button.h"
...@@ -32,6 +33,14 @@ namespace { ...@@ -32,6 +33,14 @@ namespace {
const wchar_t kHelpCenterUrl[] = const wchar_t kHelpCenterUrl[] =
L"https://www.google.com/support/chrome/bin/answer.py?answer=150752"; L"https://www.google.com/support/chrome/bin/answer.py?answer=150752";
enum ButtonTags {
BT_NONE,
BT_CLOSE_BUTTON,
BT_OK_BUTTON,
BT_TRY_IT_RADIO,
BT_DONT_BUG_RADIO
};
} // namespace } // namespace
// static // static
...@@ -53,7 +62,8 @@ TryChromeDialogView::TryChromeDialogView(size_t flavor) ...@@ -53,7 +62,8 @@ TryChromeDialogView::TryChromeDialogView(size_t flavor)
try_chrome_(NULL), try_chrome_(NULL),
kill_chrome_(NULL), kill_chrome_(NULL),
dont_try_chrome_(NULL), dont_try_chrome_(NULL),
result_(COUNT) { make_default_(NULL),
result_(COUNT) {
} }
TryChromeDialogView::~TryChromeDialogView() { TryChromeDialogView::~TryChromeDialogView() {
...@@ -90,8 +100,8 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( ...@@ -90,8 +100,8 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
return DIALOG_ERROR; return DIALOG_ERROR;
} }
root_view->SetLayoutManager(layout); root_view->SetLayoutManager(layout);
views::ColumnSet* columns; views::ColumnSet* columns;
// First row: [icon][pad][text][button]. // First row: [icon][pad][text][button].
columns = layout->AddColumnSet(0); columns = layout->AddColumnSet(0);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0,
...@@ -102,37 +112,49 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( ...@@ -102,37 +112,49 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1, columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Second row: [pad][pad][radio 1].
// Optional second row: [pad][pad][radio 1].
columns = layout->AddColumnSet(1); columns = layout->AddColumnSet(1);
columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, icon_size.width());
columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Third row: [pad][pad][radio 2]. // Third row: [pad][pad][radio 2].
columns = layout->AddColumnSet(2); columns = layout->AddColumnSet(2);
columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, icon_size.width());
columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Fourth row: [pad][pad][button][pad][button]. // Fourth row: [pad][pad][button][pad][button].
columns = layout->AddColumnSet(3); columns = layout->AddColumnSet(3);
columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, icon_size.width());
columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing); columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Fifth row: [pad][pad][link]. // Fifth row: [pad][pad][link].
columns = layout->AddColumnSet(4); columns = layout->AddColumnSet(4);
columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, icon_size.width());
columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Optional fourth row: [button]. // Optional fourth row: [button].
columns = layout->AddColumnSet(5); columns = layout->AddColumnSet(5);
columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1, columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0); views::GridLayout::USE_PREF, 0, 0);
// Optional fourth row: [pad][pad][checkbox].
columns = layout->AddColumnSet(6);
columns->AddPaddingColumn(0, icon_size.width());
columns->AddPaddingColumn(0,
views::kRelatedControlHorizontalSpacing + views::kPanelHorizIndentation);
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
views::GridLayout::USE_PREF, 0, 0);
// First row views. // First row views.
layout->StartRow(0, 0); layout->StartRow(0, 0);
layout->AddView(icon); layout->AddView(icon);
...@@ -171,45 +193,59 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( ...@@ -171,45 +193,59 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal(
const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT)); const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT));
layout->StartRowWithPadding(0, 1, 0, 10); layout->StartRowWithPadding(0, 1, 0, 10);
try_chrome_ = new views::RadioButton(try_it, 1); try_chrome_ = new views::RadioButton(try_it, 1);
layout->AddView(try_chrome_);
try_chrome_->SetChecked(true); try_chrome_->SetChecked(true);
try_chrome_->set_tag(BT_TRY_IT_RADIO);
try_chrome_->set_listener(this);
layout->AddView(try_chrome_);
// Third row views. // Decide if the don't bug me is a button or a radio button.
layout->StartRow(0, 2); bool dont_bug_me_button =
if (experiment.compact_bubble) { experiment.flags & BrowserDistribution::kDontBugMeAsButton ? true : false;
// The compact bubble has, as its second radio button, "Don't bug me".
// Optional third and fourth row views.
if (!dont_bug_me_button) {
layout->StartRow(0, 1);
const string16 decline(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); const string16 decline(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL));
dont_try_chrome_ = new views::RadioButton(decline, 1); dont_try_chrome_ = new views::RadioButton(decline, 1);
dont_try_chrome_->set_tag(BT_DONT_BUG_RADIO);
dont_try_chrome_->set_listener(this);
layout->AddView(dont_try_chrome_); layout->AddView(dont_try_chrome_);
} else { }
// The regular bubble has, as its second radio button, "Uninstall Chrome". if (experiment.flags & BrowserDistribution::kUninstall) {
layout->StartRow(0, 2);
const string16 kill_it(l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME)); const string16 kill_it(l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME));
kill_chrome_ = new views::RadioButton(kill_it, 1); kill_chrome_ = new views::RadioButton(kill_it, 1);
layout->AddView(kill_chrome_); layout->AddView(kill_chrome_);
} }
if (experiment.flags & BrowserDistribution::kMakeDefault) {
layout->StartRow(0, 6);
const string16 default_text(
l10n_util::GetStringUTF16(IDS_TRY_TOAST_SET_DEFAULT));
make_default_ = new views::Checkbox(default_text);
gfx::Font font = make_default_->font().DeriveFont(0, gfx::Font::ITALIC);
make_default_->SetFont(font);
make_default_->SetChecked(true);
layout->AddView(make_default_);
}
// Fourth row views. // Button row, the last or next to last depending on the 'why?' link.
const string16 ok_it(l10n_util::GetStringUTF16(IDS_OK)); const string16 ok_it(l10n_util::GetStringUTF16(IDS_OK));
const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL));
const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY));
views::Button* accept_button = new views::NativeTextButton(this, ok_it); views::Button* accept_button = new views::NativeTextButton(this, ok_it);
accept_button->set_tag(BT_OK_BUTTON); accept_button->set_tag(BT_OK_BUTTON);
// The compact bubble uses a centered button column for buttons, since only layout->StartRowWithPadding(0, dont_bug_me_button ? 3 : 5, 0, 10);
// the OK button appears.
int column_id_buttons = experiment.compact_bubble ? 5 : 3;
layout->StartRowWithPadding(0, column_id_buttons, 0, 10);
layout->AddView(accept_button); layout->AddView(accept_button);
if (!experiment.compact_bubble) { if (dont_bug_me_button) {
// The regular bubble needs a "Don't bug me" as a button, since it is not // The bubble needs a "Don't bug me" as a button or as a radio button, this
// one of the options for the radio buttons. We also decided to include the // this the button case.
// "Why am I seeing this?" link for the regular bubble only. const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL));
views::Button* cancel_button = new views::NativeTextButton(this, cancel_it); views::Button* cancel_button = new views::NativeTextButton(this, cancel_it);
cancel_button->set_tag(BT_CLOSE_BUTTON); cancel_button->set_tag(BT_CLOSE_BUTTON);
layout->AddView(cancel_button); layout->AddView(cancel_button);
}
// Fifth row views. if (experiment.flags & BrowserDistribution::kWhyLink) {
layout->StartRowWithPadding(0, 4, 0, 10); layout->StartRowWithPadding(0, 4, 0, 10);
const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY));
views::Link* link = new views::Link(why_this); views::Link* link = new views::Link(why_this);
link->set_listener(this); link->set_listener(this);
layout->AddView(link); layout->AddView(link);
...@@ -269,14 +305,26 @@ void TryChromeDialogView::SetToastRegion(HWND window, int w, int h) { ...@@ -269,14 +305,26 @@ void TryChromeDialogView::SetToastRegion(HWND window, int w, int h) {
void TryChromeDialogView::ButtonPressed(views::Button* sender, void TryChromeDialogView::ButtonPressed(views::Button* sender,
const views::Event& event) { const views::Event& event) {
if (sender->tag() == BT_CLOSE_BUTTON) { if (sender->tag() == BT_DONT_BUG_RADIO) {
if (make_default_) {
make_default_->SetChecked(false);
make_default_->SetState(views::CustomButton::BS_DISABLED);
}
return;
} else if (sender->tag() == BT_TRY_IT_RADIO) {
if (make_default_) {
make_default_->SetChecked(true);
make_default_->SetState(views::CustomButton::BS_NORMAL);
}
return;
} else if (sender->tag() == BT_CLOSE_BUTTON) {
// The user pressed cancel or the [x] button. // The user pressed cancel or the [x] button.
result_ = NOT_NOW; result_ = NOT_NOW;
} else if (!try_chrome_) { } else if (!try_chrome_) {
// We don't have radio buttons, the user pressed ok. // We don't have radio buttons, the user pressed ok.
result_ = TRY_CHROME; result_ = TRY_CHROME;
} else { } else {
// The outcome is according to the selected ratio button. // The outcome is according to the selected radio button.
if (try_chrome_->checked()) if (try_chrome_->checked())
result_ = TRY_CHROME; result_ = TRY_CHROME;
else if (dont_try_chrome_ && dont_try_chrome_->checked()) else if (dont_try_chrome_ && dont_try_chrome_->checked())
...@@ -286,6 +334,10 @@ void TryChromeDialogView::ButtonPressed(views::Button* sender, ...@@ -286,6 +334,10 @@ void TryChromeDialogView::ButtonPressed(views::Button* sender,
else else
NOTREACHED() << "Unknown radio button selected"; NOTREACHED() << "Unknown radio button selected";
} }
if ((result_ == TRY_CHROME) && make_default_->checked())
result_ = TRY_CHROME_AS_DEFAULT;
popup_->Close(); popup_->Close();
MessageLoop::current()->Quit(); MessageLoop::current()->Quit();
} }
......
...@@ -19,6 +19,7 @@ class Rect; ...@@ -19,6 +19,7 @@ class Rect;
namespace views { namespace views {
class RadioButton; class RadioButton;
class Checkbox;
class Widget; class Widget;
} }
...@@ -27,23 +28,32 @@ class Widget; ...@@ -27,23 +28,32 @@ class Widget;
// resulting actions are up to the caller. One flavor looks like this: // resulting actions are up to the caller. One flavor looks like this:
// //
// +-----------------------------------------------+ // +-----------------------------------------------+
// | |icon| You stopped using Google Chrome [x] | // | |icon| There is a new, safer version [x] |
// | |icon| Would you like to: | // | |icon| of Google Chrome available |
// | [o] Give the new version a try | // | [o] Try it out (already installed) |
// | [ ] Uninstall Google Chrome | // | [ ] Uninstall Google Chrome |
// | [ OK ] [Don't bug me] | // | [ OK ] [Don't bug me] |
// | |
// | _why_am_I_seeing this?_ | // | _why_am_I_seeing this?_ |
// +-----------------------------------------------+ // +-----------------------------------------------+
// //
// Another flavor looks like:
// +-----------------------------------------------+
// | |icon| There is a new, safer version [x] |
// | |icon| of Google Chrome available |
// | [o] Try it out (already installed) |
// | [ ] Don't bug me |
// | [ OK ] |
// +-----------------------------------------------+
//
class TryChromeDialogView : public views::ButtonListener, class TryChromeDialogView : public views::ButtonListener,
public views::LinkListener { public views::LinkListener {
public: public:
enum Result { enum Result {
TRY_CHROME, // Launch chrome right now. TRY_CHROME, // Launch chrome right now.
NOT_NOW, // Don't launch chrome. Exit now. TRY_CHROME_AS_DEFAULT, // Launch chrome and make it the default.
UNINSTALL_CHROME, // Initiate chrome uninstall and exit. NOT_NOW, // Don't launch chrome. Exit now.
DIALOG_ERROR, // An error occurred creating the dialog. UNINSTALL_CHROME, // Initiate chrome uninstall and exit.
DIALOG_ERROR, // An error occurred creating the dialog.
COUNT COUNT
}; };
...@@ -59,12 +69,6 @@ class TryChromeDialogView : public views::ButtonListener, ...@@ -59,12 +69,6 @@ class TryChromeDialogView : public views::ButtonListener,
static Result Show(size_t flavor, ProcessSingleton* process_singleton); static Result Show(size_t flavor, ProcessSingleton* process_singleton);
private: private:
enum ButtonTags {
BT_NONE,
BT_CLOSE_BUTTON,
BT_OK_BUTTON,
};
explicit TryChromeDialogView(size_t flavor); explicit TryChromeDialogView(size_t flavor);
virtual ~TryChromeDialogView(); virtual ~TryChromeDialogView();
...@@ -101,6 +105,7 @@ class TryChromeDialogView : public views::ButtonListener, ...@@ -101,6 +105,7 @@ class TryChromeDialogView : public views::ButtonListener,
views::RadioButton* try_chrome_; views::RadioButton* try_chrome_;
views::RadioButton* kill_chrome_; views::RadioButton* kill_chrome_;
views::RadioButton* dont_try_chrome_; views::RadioButton* dont_try_chrome_;
views::Checkbox* make_default_;
Result result_; Result result_;
DISALLOW_COPY_AND_ASSIGN(TryChromeDialogView); DISALLOW_COPY_AND_ASSIGN(TryChromeDialogView);
......
...@@ -18,18 +18,8 @@ enum Experiment { ...@@ -18,18 +18,8 @@ enum Experiment {
kSkype1 = IDS_TRY_TOAST_HEADING_SKYPE, kSkype1 = IDS_TRY_TOAST_HEADING_SKYPE,
}; };
// This is used to match against locale and brands, and represents any
// locale/brand.
const wchar_t kAll[] = L"*";
// A comma-separated list of brand codes that are associated with Skype. // A comma-separated list of brand codes that are associated with Skype.
const wchar_t kSkype[] = L"SKPC,SKPG,SKPH,SKPI,SKPL,SKPM,SKPN"; const wchar_t kSkypeBrandCode[] = L"SKPC,SKPG,SKPH,SKPI,SKPL,SKPM,SKPN";
// The brand code for enterprise installations.
const wchar_t kEnterprise[] = L"GGRV";
// The brand code for showing more compact bubbles (experimental).
const wchar_t kBrief[] = L"CHMA";
} // namespace } // namespace
......
...@@ -33,6 +33,14 @@ class BrowserDistribution { ...@@ -33,6 +33,14 @@ class BrowserDistribution {
NUM_TYPES 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 // A struct for communicating what a UserExperiment contains. In these
// experiments we show toasts to the user if they are inactive for a certain // experiments we show toasts to the user if they are inactive for a certain
// amount of time. // amount of time.
...@@ -41,7 +49,7 @@ class BrowserDistribution { ...@@ -41,7 +49,7 @@ class BrowserDistribution {
// also known as the 'TV' part in 'TV80'. // also known as the 'TV' part in 'TV80'.
int flavor; // The flavor index for this experiment. int flavor; // The flavor index for this experiment.
int heading; // The heading resource ID to use for this experiment. int heading; // The heading resource ID to use for this experiment.
bool compact_bubble; // Whether to show the compact heading or not. int flags; // See ToastUIFlags above.
int control_group; // Size of the control group (in percentages). Control int control_group; // Size of the control group (in percentages). Control
// group is the group that qualifies for the // group is the group that qualifies for the
// experiment but does not participate. // experiment but does not participate.
......
...@@ -60,14 +60,15 @@ const wchar_t kICommandExecuteImplUuid[] = ...@@ -60,14 +60,15 @@ const wchar_t kICommandExecuteImplUuid[] =
// The following strings are the possible outcomes of the toast experiment // The following strings are the possible outcomes of the toast experiment
// as recorded in the |client| field. // as recorded in the |client| field.
const wchar_t kToastExpControlGroup[] = L"01"; const wchar_t kToastExpControlGroup[] = L"01";
const wchar_t kToastExpCancelGroup[] = L"02"; const wchar_t kToastExpCancelGroup[] = L"02";
const wchar_t kToastExpUninstallGroup[] = L"04"; const wchar_t kToastExpUninstallGroup[] = L"04";
const wchar_t kToastExpTriesOkGroup[] = L"18"; const wchar_t kToastExpTriesOkGroup[] = L"18";
const wchar_t kToastExpTriesErrorGroup[] = L"28"; const wchar_t kToastExpTriesErrorGroup[] = L"28";
const wchar_t kToastActiveGroup[] = L"40"; const wchar_t kToastExpTriesOkDefaultGroup[] = L"48";
const wchar_t kToastUDDirFailure[] = L"40"; const wchar_t kToastActiveGroup[] = L"40";
const wchar_t kToastExpBaseGroup[] = L"80"; const wchar_t kToastUDDirFailure[] = L"40";
const wchar_t kToastExpBaseGroup[] = L"80";
// Substitute the locale parameter in uninstall URL with whatever // Substitute the locale parameter in uninstall URL with whatever
// Google Update tells us is the locale. In case we fail to find // Google Update tells us is the locale. In case we fail to find
...@@ -612,6 +613,10 @@ void SetClient(const string16& experiment_group, bool last_write) { ...@@ -612,6 +613,10 @@ void SetClient(const string16& experiment_group, bool last_write) {
bool GoogleChromeDistribution::GetExperimentDetails( bool GoogleChromeDistribution::GetExperimentDetails(
UserExperiment* experiment, int flavor) { UserExperiment* experiment, int flavor) {
struct FlavorDetails {
int heading_id;
int flags;
};
// Maximum number of experiment flavors we support. // Maximum number of experiment flavors we support.
static const int kMax = 4; static const int kMax = 4;
// This struct determines which experiment flavors we show for each locale and // This struct determines which experiment flavors we show for each locale and
...@@ -624,29 +629,37 @@ bool GoogleChromeDistribution::GetExperimentDetails( ...@@ -624,29 +629,37 @@ bool GoogleChromeDistribution::GetExperimentDetails(
// The big experiment in Feb 2011 used SJxx SKxx SLxx SMxx. // The big experiment in Feb 2011 used SJxx SKxx SLxx SMxx.
// Note: the plugin infobar experiment uses PIxx codes. // Note: the plugin infobar experiment uses PIxx codes.
using namespace attrition_experiments; using namespace attrition_experiments;
static const struct UserExperimentDetails { static const struct UserExperimentDetails {
const wchar_t* locale; // Locale to show this experiment for (* for all). const wchar_t* locale; // Locale to show this experiment for (* for all).
const wchar_t* brands; // Brand codes 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. int control_group; // Size of the control group, in percentages.
const wchar_t prefix1; // The first letter for the experiment code. const wchar_t* prefix; // The two letter experiment code. The second letter
const wchar_t prefix2; // The second letter for the experiment code. This // will be incremented with the flavor.
// will be incremented by one for each additional FlavorDetails flavors[kMax];
// experiment flavor beyond the first. } kExperiments[] = {
int flavors; // Numbers of flavors for this experiment. Should // The first match from top to bottom is used so this list should be ordered
// always be positive and never exceed the number // most-specific rule first.
// of headings (below). { L"*", L"CHMA", // All locales, CHMA brand.
int headings[kMax]; // A list of IDs per experiment. 0 == no heading. 25, // 25 percent control group.
} kExperimentFlavors[] = { L"ZA", // Experiment is ZAxx, ZBxx, ZCxx, ZDxx etc.
// This list should be ordered most-specific rule first (catch-all, like all // Three flavors.
// brands or all locales should be last). { { IDS_TRY_TOAST_HEADING3, kDontBugMeAsButton | kUninstall | kWhyLink },
{ IDS_TRY_TOAST_HEADING3, 0 },
// The experiment with the more compact bubble. This one is a bit special { IDS_TRY_TOAST_HEADING3, kMakeDefault },
// because it is split into two: CAxx is regular style bubble and CBxx is { 0, 0 },
// compact style bubble. See |compact_bubble| below. }
{L"en-US", kBrief, 1, L'C', L'A', 2, { kEnUs3, kEnUs3, 0, 0 } }, },
{ L"*", L"GGRV", // All locales, GGRV is enterprise.
// Catch-all rules. 0, // 0 percent control group.
{kAll, kAll, 1, L'B', L'A', 1, {kEnUs3, 0, 0, 0} }, L"EA", // Experiment is EAxx, EBxx, etc.
// No flavors means no experiment.
{ { 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 }
}
}
}; };
string16 locale; string16 locale;
...@@ -657,50 +670,36 @@ bool GoogleChromeDistribution::GetExperimentDetails( ...@@ -657,50 +670,36 @@ bool GoogleChromeDistribution::GetExperimentDetails(
string16 brand; string16 brand;
if (!GoogleUpdateSettings::GetBrand(&brand)) if (!GoogleUpdateSettings::GetBrand(&brand))
brand = ASCIIToWide(""); // Could still be viable for catch-all rules. brand = ASCIIToWide(""); // Could still be viable for catch-all rules.
if (brand == kEnterprise)
return false;
for (int i = 0; i < arraysize(kExperimentFlavors); ++i) { for (int i = 0; i < arraysize(kExperiments); ++i) {
// A maximum of four flavors are supported at the moment. if (kExperiments[i].locale != locale &&
CHECK_LE(kExperimentFlavors[i].flavors, kMax); kExperiments[i].locale != ASCIIToWide("*"))
CHECK_GT(kExperimentFlavors[i].flavors, 0);
// Make sure each experiment has valid headings.
for (int f = 0; f < kMax; ++f) {
if (f < kExperimentFlavors[i].flavors) {
CHECK_GT(kExperimentFlavors[i].headings[f], 0);
} else {
CHECK_EQ(kExperimentFlavors[i].headings[f], 0);
}
}
// The prefix has to be a valid two letter combo.
CHECK(kExperimentFlavors[i].prefix1 >= 'A');
CHECK(kExperimentFlavors[i].prefix2 >= 'A');
CHECK(kExperimentFlavors[i].prefix2 +
kExperimentFlavors[i].flavors - 1 <= 'Z');
if (kExperimentFlavors[i].locale != locale &&
kExperimentFlavors[i].locale != ASCIIToWide("*"))
continue; continue;
std::vector<string16> brand_codes; std::vector<string16> brand_codes;
base::SplitString(kExperimentFlavors[i].brands, L',', &brand_codes); base::SplitString(kExperiments[i].brands, L',', &brand_codes);
if (brand_codes.empty()) if (brand_codes.empty())
return false; return false;
for (std::vector<string16>::iterator it = brand_codes.begin(); for (std::vector<string16>::iterator it = brand_codes.begin();
it != brand_codes.end(); ++it) { it != brand_codes.end(); ++it) {
if (*it != brand && *it != L"*") if (*it != brand && *it != L"*")
continue; continue;
// We have found our match. // 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) if (flavor < 0)
flavor = base::RandInt(0, kExperimentFlavors[i].flavors - 1); flavor = base::RandInt(0, num_flavors - 1);
experiment->flavor = flavor; experiment->flavor = flavor;
experiment->heading = kExperimentFlavors[i].headings[flavor]; experiment->heading = match.flavors[flavor].heading_id;
experiment->control_group = kExperimentFlavors[i].control_group; experiment->control_group = match.control_group;
experiment->prefix.resize(2); const wchar_t prefix[] = { match.prefix[0], match.prefix[1] + flavor, 0 };
experiment->prefix[0] = kExperimentFlavors[i].prefix1; experiment->prefix = prefix;
experiment->prefix[1] = kExperimentFlavors[i].prefix2 + flavor; experiment->flags = match.flavors[flavor].flags;
experiment->compact_bubble = (brand == kBrief) && (flavor == 1);
return true; return true;
} }
} }
...@@ -853,6 +852,16 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, ...@@ -853,6 +852,16 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor,
outcome = kToastExpTriesErrorGroup; outcome = kToastExpTriesErrorGroup;
}; };
if (outcome == kToastExpTriesOkGroup) {
// User tried chrome, but if it had the default group button it belongs
// to a different outcome group.
UserExperiment experiment;
if (GetExperimentDetails(&experiment, flavor)) {
outcome = experiment.flags & kMakeDefault ? kToastExpTriesOkDefaultGroup :
kToastExpTriesOkGroup;
}
}
// Write to the |client| key for the last time. // Write to the |client| key for the last time.
SetClient(experiment_group + outcome, true); SetClient(experiment_group + outcome, true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment