diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.cc | 104 | ||||
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.h | 33 | ||||
-rw-r--r-- | chrome/common/attrition_experiments.h | 12 | ||||
-rw-r--r-- | chrome/installer/util/browser_distribution.h | 10 | ||||
-rw-r--r-- | chrome/installer/util/google_chrome_distribution.cc | 125 |
5 files changed, 174 insertions, 110 deletions
diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc index 3d74836..be3c7dd 100644 --- a/chrome/browser/first_run/try_chrome_dialog_view.cc +++ b/chrome/browser/first_run/try_chrome_dialog_view.cc @@ -18,6 +18,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.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/radio_button.h" #include "ui/views/controls/button/text_button.h" @@ -32,6 +33,14 @@ namespace { const wchar_t kHelpCenterUrl[] = 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 // static @@ -53,7 +62,8 @@ TryChromeDialogView::TryChromeDialogView(size_t flavor) try_chrome_(NULL), kill_chrome_(NULL), dont_try_chrome_(NULL), - result_(COUNT) { + make_default_(NULL), + result_(COUNT) { } TryChromeDialogView::~TryChromeDialogView() { @@ -90,8 +100,8 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( return DIALOG_ERROR; } root_view->SetLayoutManager(layout); - views::ColumnSet* columns; + // First row: [icon][pad][text][button]. columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, @@ -102,37 +112,49 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( views::GridLayout::USE_PREF, 0, 0); columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); - // Second row: [pad][pad][radio 1]. + + // Optional second row: [pad][pad][radio 1]. columns = layout->AddColumnSet(1); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Third row: [pad][pad][radio 2]. columns = layout->AddColumnSet(2); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Fourth row: [pad][pad][button][pad][button]. columns = layout->AddColumnSet(3); columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); + // Fifth row: [pad][pad][link]. columns = layout->AddColumnSet(4); columns->AddPaddingColumn(0, icon_size.width()); columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); + // Optional fourth row: [button]. columns = layout->AddColumnSet(5); columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1, 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. layout->StartRow(0, 0); layout->AddView(icon); @@ -171,45 +193,59 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT)); layout->StartRowWithPadding(0, 1, 0, 10); try_chrome_ = new views::RadioButton(try_it, 1); - layout->AddView(try_chrome_); try_chrome_->SetChecked(true); + try_chrome_->set_tag(BT_TRY_IT_RADIO); + try_chrome_->set_listener(this); + layout->AddView(try_chrome_); - // Third row views. - layout->StartRow(0, 2); - if (experiment.compact_bubble) { - // The compact bubble has, as its second radio button, "Don't bug me". + // Decide if the don't bug me is a button or a radio button. + bool dont_bug_me_button = + experiment.flags & BrowserDistribution::kDontBugMeAsButton ? true : false; + + // 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)); 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_); - } 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)); kill_chrome_ = new views::RadioButton(kill_it, 1); 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 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); accept_button->set_tag(BT_OK_BUTTON); - // The compact bubble uses a centered button column for buttons, since only - // the OK button appears. - int column_id_buttons = experiment.compact_bubble ? 5 : 3; - layout->StartRowWithPadding(0, column_id_buttons, 0, 10); + layout->StartRowWithPadding(0, dont_bug_me_button ? 3 : 5, 0, 10); layout->AddView(accept_button); - if (!experiment.compact_bubble) { - // The regular bubble needs a "Don't bug me" as a button, since it is not - // one of the options for the radio buttons. We also decided to include the - // "Why am I seeing this?" link for the regular bubble only. + if (dont_bug_me_button) { + // The bubble needs a "Don't bug me" as a button or as a radio button, this + // this the button case. + const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); views::Button* cancel_button = new views::NativeTextButton(this, cancel_it); cancel_button->set_tag(BT_CLOSE_BUTTON); layout->AddView(cancel_button); - - // Fifth row views. + } + if (experiment.flags & BrowserDistribution::kWhyLink) { 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); link->set_listener(this); layout->AddView(link); @@ -269,14 +305,26 @@ void TryChromeDialogView::SetToastRegion(HWND window, int w, int h) { void TryChromeDialogView::ButtonPressed(views::Button* sender, 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. result_ = NOT_NOW; } else if (!try_chrome_) { // We don't have radio buttons, the user pressed ok. result_ = TRY_CHROME; } else { - // The outcome is according to the selected ratio button. + // The outcome is according to the selected radio button. if (try_chrome_->checked()) result_ = TRY_CHROME; else if (dont_try_chrome_ && dont_try_chrome_->checked()) @@ -286,6 +334,10 @@ void TryChromeDialogView::ButtonPressed(views::Button* sender, else NOTREACHED() << "Unknown radio button selected"; } + + if ((result_ == TRY_CHROME) && make_default_->checked()) + result_ = TRY_CHROME_AS_DEFAULT; + popup_->Close(); MessageLoop::current()->Quit(); } diff --git a/chrome/browser/first_run/try_chrome_dialog_view.h b/chrome/browser/first_run/try_chrome_dialog_view.h index 77f90bc..d0a5aa5 100644 --- a/chrome/browser/first_run/try_chrome_dialog_view.h +++ b/chrome/browser/first_run/try_chrome_dialog_view.h @@ -19,6 +19,7 @@ class Rect; namespace views { class RadioButton; +class Checkbox; class Widget; } @@ -27,23 +28,32 @@ class Widget; // resulting actions are up to the caller. One flavor looks like this: // // +-----------------------------------------------+ -// | |icon| You stopped using Google Chrome [x] | -// | |icon| Would you like to: | -// | [o] Give the new version a try | +// | |icon| There is a new, safer version [x] | +// | |icon| of Google Chrome available | +// | [o] Try it out (already installed) | // | [ ] Uninstall Google Chrome | // | [ OK ] [Don't bug me] | -// | | // | _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, public views::LinkListener { public: enum Result { - TRY_CHROME, // Launch chrome right now. - NOT_NOW, // Don't launch chrome. Exit now. - UNINSTALL_CHROME, // Initiate chrome uninstall and exit. - DIALOG_ERROR, // An error occurred creating the dialog. + TRY_CHROME, // Launch chrome right now. + TRY_CHROME_AS_DEFAULT, // Launch chrome and make it the default. + NOT_NOW, // Don't launch chrome. Exit now. + UNINSTALL_CHROME, // Initiate chrome uninstall and exit. + DIALOG_ERROR, // An error occurred creating the dialog. COUNT }; @@ -59,12 +69,6 @@ class TryChromeDialogView : public views::ButtonListener, static Result Show(size_t flavor, ProcessSingleton* process_singleton); private: - enum ButtonTags { - BT_NONE, - BT_CLOSE_BUTTON, - BT_OK_BUTTON, - }; - explicit TryChromeDialogView(size_t flavor); virtual ~TryChromeDialogView(); @@ -101,6 +105,7 @@ class TryChromeDialogView : public views::ButtonListener, views::RadioButton* try_chrome_; views::RadioButton* kill_chrome_; views::RadioButton* dont_try_chrome_; + views::Checkbox* make_default_; Result result_; DISALLOW_COPY_AND_ASSIGN(TryChromeDialogView); diff --git a/chrome/common/attrition_experiments.h b/chrome/common/attrition_experiments.h index e3dec6b..1404b49 100644 --- a/chrome/common/attrition_experiments.h +++ b/chrome/common/attrition_experiments.h @@ -18,18 +18,8 @@ enum Experiment { 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. -const wchar_t kSkype[] = 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"; +const wchar_t kSkypeBrandCode[] = L"SKPC,SKPG,SKPH,SKPI,SKPL,SKPM,SKPN"; } // namespace diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 4f609c9..96360a9 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -33,6 +33,14 @@ 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. @@ -41,7 +49,7 @@ class BrowserDistribution { // 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. - 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 // group is the group that qualifies for the // experiment but does not participate. diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc index 80c160f..b340873 100644 --- a/chrome/installer/util/google_chrome_distribution.cc +++ b/chrome/installer/util/google_chrome_distribution.cc @@ -60,14 +60,15 @@ const wchar_t kICommandExecuteImplUuid[] = // 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"; +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 kToastExpTriesOkDefaultGroup[] = L"48"; +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 @@ -612,6 +613,10 @@ void SetClient(const string16& experiment_group, bool last_write) { 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 @@ -624,29 +629,37 @@ bool GoogleChromeDistribution::GetExperimentDetails( // The big experiment in Feb 2011 used SJxx SKxx SLxx SMxx. // Note: the plugin infobar experiment uses PIxx codes. 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 prefix1; // The first letter for the experiment code. - const wchar_t prefix2; // The second letter for the experiment code. This - // will be incremented by one for each additional - // experiment flavor beyond the first. - int flavors; // Numbers of flavors for this experiment. Should - // always be positive and never exceed the number - // of headings (below). - int headings[kMax]; // A list of IDs per experiment. 0 == no heading. - } kExperimentFlavors[] = { - // This list should be ordered most-specific rule first (catch-all, like all - // brands or all locales should be last). - - // The experiment with the more compact bubble. This one is a bit special - // because it is split into two: CAxx is regular style bubble and CBxx is - // compact style bubble. See |compact_bubble| below. - {L"en-US", kBrief, 1, L'C', L'A', 2, { kEnUs3, kEnUs3, 0, 0 } }, - - // Catch-all rules. - {kAll, kAll, 1, L'B', L'A', 1, {kEnUs3, 0, 0, 0} }, + 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"CHMA", // All locales, CHMA brand. + 25, // 25 percent control group. + L"ZA", // Experiment is ZAxx, ZBxx, ZCxx, ZDxx etc. + // Three flavors. + { { IDS_TRY_TOAST_HEADING3, kDontBugMeAsButton | kUninstall | kWhyLink }, + { IDS_TRY_TOAST_HEADING3, 0 }, + { IDS_TRY_TOAST_HEADING3, kMakeDefault }, + { 0, 0 }, + } + }, + { 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 } + } + } }; string16 locale; @@ -657,50 +670,36 @@ bool GoogleChromeDistribution::GetExperimentDetails( string16 brand; if (!GoogleUpdateSettings::GetBrand(&brand)) brand = ASCIIToWide(""); // Could still be viable for catch-all rules. - if (brand == kEnterprise) - return false; - for (int i = 0; i < arraysize(kExperimentFlavors); ++i) { - // A maximum of four flavors are supported at the moment. - CHECK_LE(kExperimentFlavors[i].flavors, kMax); - 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("*")) + 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(kExperimentFlavors[i].brands, L',', &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, kExperimentFlavors[i].flavors - 1); + flavor = base::RandInt(0, num_flavors - 1); experiment->flavor = flavor; - experiment->heading = kExperimentFlavors[i].headings[flavor]; - experiment->control_group = kExperimentFlavors[i].control_group; - experiment->prefix.resize(2); - experiment->prefix[0] = kExperimentFlavors[i].prefix1; - experiment->prefix[1] = kExperimentFlavors[i].prefix2 + flavor; - experiment->compact_bubble = (brand == kBrief) && (flavor == 1); + 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; } } @@ -853,6 +852,16 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, 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. SetClient(experiment_group + outcome, true); |