diff options
author | bcwhite@chromium.org <bcwhite@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 01:17:14 +0000 |
---|---|---|
committer | bcwhite@chromium.org <bcwhite@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-26 01:17:14 +0000 |
commit | 0dfa66afc89ad97e91b2249bf8b357e57445f69b (patch) | |
tree | 8d8168377c04255ace9ee56962acfd5cba6e4c6e | |
parent | ed5eff4235692155eed75b8cfaf5e7340b4c2ce8 (diff) | |
download | chromium_src-0dfa66afc89ad97e91b2249bf8b357e57445f69b.zip chromium_src-0dfa66afc89ad97e91b2249bf8b357e57445f69b.tar.gz chromium_src-0dfa66afc89ad97e91b2249bf8b357e57445f69b.tar.bz2 |
Add ability to sign-out of Google directly from profile switcher menu.
As part of the plan to make sync sign-in more integral to a user's profile, we're adding an explicit ability to "sign out" of the profile in place of signing out of Google inside the content area. This CL adds that basic sign-out functionality by adding a "sign out" button to the avatar menu that, when clicked, goes through the steps of signing out of Google and closing all browser windows associated with that profile.
Future CLs will do the Right Thing(tm) of requiring sign-in before opening that profile so that the tabs can resume where there were, just as though there had been no sign-out event.
The UI included here is temporary. A new profile-switcher menu is being discussed and the core functionality included here will eventually get triggered from that once the design is finalized. In other words, the UI presented here will eventually be replaced.
See it in action:
https://docs.google.com/a/google.com/file/d/0B4S7cIAPVn9XdzJFZXl2SncxMlE/edit?usp=sharing
Previous avatar menu:
https://docs.google.com/a/google.com/file/d/0B4S7cIAPVn9XY3dZMXRUNGtHd3M/edit?usp=sharing
To test:
- launch with --enable-profile-signout
- create a second profile (so Avatar menu appears)
- sign-in to a Google account (like gmail)
- open Avatar menu and click the "sign out" button
- (window will close when sign-out is complete)
- re-open profile
- go to a Google account
- note that you are not signed in
BUG=
Review URL: https://chromiumcodereview.appspot.com/12726012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196569 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 15 | ||||
-rw-r--r-- | chrome/browser/profiles/avatar_menu_model.cc | 84 | ||||
-rw-r--r-- | chrome/browser/profiles/avatar_menu_model.h | 15 | ||||
-rw-r--r-- | chrome/browser/profiles/avatar_menu_model_browsertest.cc | 47 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_cache.cc | 18 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_cache.h | 2 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_info_interface.h | 3 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_manager.h | 5 | ||||
-rw-r--r-- | chrome/browser/ui/views/avatar_menu_bubble_view.cc | 139 | ||||
-rw-r--r-- | chrome/browser/ui/views/avatar_menu_bubble_view.h | 8 | ||||
-rw-r--r-- | chrome/browser/ui/webui/sync_promo/sync_promo_ui.cc | 19 | ||||
-rw-r--r-- | chrome/browser/ui/webui/sync_promo/sync_promo_ui.h | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | google_apis/gaia/gaia_urls.cc | 6 | ||||
-rw-r--r-- | google_apis/gaia/gaia_urls.h | 2 |
17 files changed, 341 insertions, 31 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 5d9d78a..0fcf1b7 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -11577,6 +11577,21 @@ experiment id: "<ph name="EXPERIMENT_ID">$5<ex>ar1</ex></ph>" <message name="IDS_PROFILES_CREATE_NEW_PROFILE_LINK" desc="Link in the avatar menu bubble view to create a new profile."> New user </message> + <message name="IDS_PROFILES_MANAGE_PROFILES_BUTTON" desc="Button in the avatar menu bubble view to manage profiles."> + Manage + </message> + <message name="IDS_PROFILES_MANAGE_PROFILES_BUTTON_TIP" desc="Tool-tip text shown for button in the avatar menu bubble view to manage profiles."> + Create, change, or delete profiles. + </message> + <message name="IDS_PROFILES_PROFILE_SIGNOUT_BUTTON" desc="Button in the avatar menu bubble view to sign-out the current profile."> + Sign-out + </message> + <message name="IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP" desc="Tool-tip text shown for button in the avatar menu bubble view to sign-out the current profile."> + Sign-out user <ph name="PROFILE_USERNAME">$1<ex>user@gmail.com</ex></ph>. + </message> + <message name="IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP_UNAVAILABLE" desc="Tool-tip text shown for button in the avatar menu bubble view to sign-out the current profile when that button is not available because the profile has not been signed-in."> + Sign-out is possible only when signed in. + </message> <message name="IDS_PROFILES_EDIT_PROFILE_LINK" desc="Link in the avatar menu bubble to edit a profile."> edit </message> diff --git a/chrome/browser/profiles/avatar_menu_model.cc b/chrome/browser/profiles/avatar_menu_model.cc index 151acf9..5a29121 100644 --- a/chrome/browser/profiles/avatar_menu_model.cc +++ b/chrome/browser/profiles/avatar_menu_model.cc @@ -26,6 +26,8 @@ #include "chrome/common/url_constants.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" +#include "content/public/browser/site_instance.h" +#include "google_apis/gaia/gaia_urls.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -58,6 +60,47 @@ void OnProfileCreated(bool always_create, const char kShowProfileSwitcherFieldTrialName[] = "ShowProfileSwitcher"; const char kAlwaysShowSwitcherGroupName[] = "AlwaysShow"; + +class SignoutTracker : public content::WebContentsObserver { + public: + SignoutTracker(Profile* profile, const GURL& signout_landing_url, + content::WebContents* contents); + + virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE; + virtual void DidStopLoading(content::RenderViewHost* render_view_host) + OVERRIDE; + + private: + scoped_ptr<content::WebContents> contents_; + GURL signout_landing_url_; + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(SignoutTracker); +}; + + +SignoutTracker::SignoutTracker(Profile* profile, + const GURL& signout_landing_url, + content::WebContents* contents) + : WebContentsObserver(contents), + contents_(contents), + signout_landing_url_(signout_landing_url), + profile_(profile) { +} + +void SignoutTracker::DidStopLoading(content::RenderViewHost* render_view_host) { + // Only close when we reach the final landing; ignore redirects until then. + if (web_contents()->GetURL() == signout_landing_url_) { + Observe(NULL); + BrowserList::CloseAllBrowsersWithProfile(profile_); + delete this; /* success */ + } +} + +void SignoutTracker::WebContentsDestroyed(content::WebContents* contents) { + delete this; /* failure */ +} + } // namespace AvatarMenuModel::AvatarMenuModel(ProfileInfoInterface* profile_cache, @@ -85,6 +128,7 @@ AvatarMenuModel::Item::Item(size_t model_index, const gfx::Image& icon) : icon(icon), active(false), signed_in(false), + signin_required(false), model_index(model_index) { } @@ -139,6 +183,11 @@ void AvatarMenuModel::AddNewProfile(ProfileMetrics::ProfileAdd type) { ProfileMetrics::LogProfileAddNewUser(type); } +base::FilePath AvatarMenuModel::GetProfilePath(size_t index) { + const Item& item = GetItemAt(index); + return profile_info_->GetPathOfProfileAtIndex(item.model_index); +} + size_t AvatarMenuModel::GetNumberOfItems() { return items_.size(); } @@ -227,6 +276,7 @@ void AvatarMenuModel::RebuildMenu() { base::FilePath path = profile_info_->GetPathOfProfileAtIndex(i); item->active = browser_->profile()->GetPath() == path; } + item->signin_required = profile_info_->ProfileIsSigninRequiredAtIndex(i); items_.push_back(item); } } @@ -234,3 +284,37 @@ void AvatarMenuModel::RebuildMenu() { void AvatarMenuModel::ClearMenu() { STLDeleteElements(&items_); } + + +content::WebContents* AvatarMenuModel::BeginSignOut( + const char* logout_override) { + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* current_profile = browser_->profile(); + + ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + size_t index = cache.GetIndexOfProfileWithPath(current_profile->GetPath()); + cache.SetProfileSigninRequiredAtIndex(index, true); + + std::string landing_url = SyncPromoUI::GetSyncLandingURL("close", 1); + GURL logout_url(GaiaUrls::GetInstance()->service_logout_url() + + "?continue=" + landing_url); + if (logout_override) { + // We're testing... + landing_url = logout_override; + logout_url = GURL(logout_override); + } + + content::WebContents::CreateParams create_params(current_profile); + create_params.site_instance = + content::SiteInstance::CreateForURL(current_profile, logout_url); + content::WebContents* contents = content::WebContents::Create(create_params); + contents->GetController().LoadURL( + logout_url, content::Referrer(), + content::PAGE_TRANSITION_GENERATED, std::string()); + + // This object may be destructed when the menu closes but we need something + // around to finish the sign-out process and close the profile windows. + new SignoutTracker(current_profile, GURL(landing_url), contents); + + return contents; // returned for testing purposes +} diff --git a/chrome/browser/profiles/avatar_menu_model.h b/chrome/browser/profiles/avatar_menu_model.h index 308a825..e83af90 100644 --- a/chrome/browser/profiles/avatar_menu_model.h +++ b/chrome/browser/profiles/avatar_menu_model.h @@ -13,10 +13,13 @@ #include "chrome/browser/profiles/profile_metrics.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" #include "ui/gfx/image/image.h" class AvatarMenuModelObserver; class Browser; +class Profile; class ProfileInfoInterface; // This class is the model for the menu-like interface that appears when the @@ -46,6 +49,9 @@ class AvatarMenuModel : public content::NotificationObserver { // expected to be the email of the signed in user. bool signed_in; + // Whether or not the current profile requires sign-in before use. + bool signin_required; + // The index in the |profile_cache| that this Item represents. size_t model_index; }; @@ -69,6 +75,9 @@ class AvatarMenuModel : public content::NotificationObserver { // Creates a new profile. void AddNewProfile(ProfileMetrics::ProfileAdd type); + // Gets the path associated with the profile at |index|. + base::FilePath GetProfilePath(size_t index); + // Gets the number of profiles. size_t GetNumberOfItems(); @@ -93,6 +102,12 @@ class AvatarMenuModel : public content::NotificationObserver { // True if avatar menu should be displayed. static bool ShouldShowAvatarMenu(); + // Start the sign-out process for this profile. + // Parameter |logout_override| alows changing the destination URL for the + // sign-out process and return value (the WebContents executing the sign-out) + // are for testing; pass NULL for normal use. + content::WebContents* BeginSignOut(const char* logout_override); + private: // Rebuilds the menu from the cache and notifies the |observer_|. void RebuildMenu(); diff --git a/chrome/browser/profiles/avatar_menu_model_browsertest.cc b/chrome/browser/profiles/avatar_menu_model_browsertest.cc new file mode 100644 index 0000000..970a660 --- /dev/null +++ b/chrome/browser/profiles/avatar_menu_model_browsertest.cc @@ -0,0 +1,47 @@ +// Copyright (c) 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/browser/profiles/avatar_menu_model.h" + +#include "base/path_service.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/testing_browser_process.h" +#include "content/public/test/test_utils.h" + +namespace { + +typedef InProcessBrowserTest AvatarMenuModelTest; + +IN_PROC_BROWSER_TEST_F(AvatarMenuModelTest, SignOut) { + if (!ProfileManager::IsMultipleProfilesEnabled()) + return; + + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* current_profile = browser()->profile(); + ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + size_t index = cache.GetIndexOfProfileWithPath(current_profile->GetPath()); + + AvatarMenuModel model(&cache, NULL, browser()); + + BrowserList* browser_list = + BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); + EXPECT_EQ(1U, browser_list->size()); + content::WindowedNotificationObserver window_close_observer( + chrome::NOTIFICATION_BROWSER_CLOSED, + content::Source<Browser>(browser())); + + EXPECT_FALSE(cache.ProfileIsSigninRequiredAtIndex(index)); + model.BeginSignOut("about:blank"); + EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(index)); + + window_close_observer.Wait(); // rely on test time-out for failure indication + EXPECT_EQ(0U, browser_list->size()); +} + +} // namespace diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc index 8b62ecd..b3409fd 100644 --- a/chrome/browser/profiles/profile_info_cache.cc +++ b/chrome/browser/profiles/profile_info_cache.cc @@ -48,6 +48,7 @@ const char kBackgroundAppsKey[] = "background_apps"; const char kHasMigratedToGAIAInfoKey[] = "has_migrated_to_gaia_info"; const char kGAIAPictureFileNameKey[] = "gaia_picture_file_name"; const char kIsManagedKey[] = "is_managed"; +const char kSigninRequiredKey[] = "signin_required"; const char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_"; const char kGAIAPictureFileName[] = "Google Profile Picture.png"; @@ -373,6 +374,12 @@ bool ProfileInfoCache::ProfileIsManagedAtIndex(size_t index) const { return value; } +bool ProfileInfoCache::ProfileIsSigninRequiredAtIndex(size_t index) const { + bool value = false; + GetInfoForProfileAtIndex(index)->GetBoolean(kSigninRequiredKey, &value); + return value; +} + void ProfileInfoCache::OnGAIAPictureLoaded(const base::FilePath& path, gfx::Image** image) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -606,6 +613,17 @@ void ProfileInfoCache::SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, OnProfileAvatarChanged(profile_path)); } +void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index, + bool value) { + if (value == ProfileIsSigninRequiredAtIndex(index)) + return; + + scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy()); + info->SetBoolean(kSigninRequiredKey, value); + // This takes ownership of |info|. + SetInfoForProfileAtIndex(index, info.release()); +} + string16 ProfileInfoCache::ChooseNameForNewProfile(size_t icon_index) const { string16 name; for (int name_index = 1; ; ++name_index) { diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h index 82545fd..cd71118 100644 --- a/chrome/browser/profiles/profile_info_cache.h +++ b/chrome/browser/profiles/profile_info_cache.h @@ -75,6 +75,7 @@ class ProfileInfoCache : public ProfileInfoInterface, virtual bool IsUsingGAIAPictureOfProfileAtIndex( size_t index) const OVERRIDE; virtual bool ProfileIsManagedAtIndex(size_t index) const OVERRIDE; + virtual bool ProfileIsSigninRequiredAtIndex(size_t index) const OVERRIDE; size_t GetAvatarIconIndexOfProfileAtIndex(size_t index) const; @@ -88,6 +89,7 @@ class ProfileInfoCache : public ProfileInfoInterface, void SetIsUsingGAIANameOfProfileAtIndex(size_t index, bool value); void SetGAIAPictureOfProfileAtIndex(size_t index, const gfx::Image* image); void SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, bool value); + void SetProfileSigninRequiredAtIndex(size_t index, bool value); // Returns unique name that can be assigned to a newly created profile. string16 ChooseNameForNewProfile(size_t icon_index) const; diff --git a/chrome/browser/profiles/profile_info_interface.h b/chrome/browser/profiles/profile_info_interface.h index 4e3ed99..40d423d 100644 --- a/chrome/browser/profiles/profile_info_interface.h +++ b/chrome/browser/profiles/profile_info_interface.h @@ -51,6 +51,9 @@ class ProfileInfoInterface { virtual bool ProfileIsManagedAtIndex(size_t index) const = 0; + // This profile is associated with an account but has been signed-out. + virtual bool ProfileIsSigninRequiredAtIndex(size_t index) const = 0; + protected: virtual ~ProfileInfoInterface() {} }; diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h index a73ee96..05babfa 100644 --- a/chrome/browser/profiles/profile_manager.h +++ b/chrome/browser/profiles/profile_manager.h @@ -211,7 +211,10 @@ class ProfileManager : public base::NonThreadSafe, // Autoloads profiles if they are running background apps. void AutoloadProfiles(); - // Registers and adds testing profile to the ProfileManager. + // Sign-Out a profile against use until re-authentication. + void SignOutProfile(Profile* profile); + + // Register and add testing profile to the ProfileManager. Use ONLY in tests. // This allows the creation of Profiles outside of the standard creation path // for testing. If |addToCache|, adds to ProfileInfoCache as well. // If |start_deferred_task_runners|, starts the deferred task runners. diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.cc b/chrome/browser/ui/views/avatar_menu_bubble_view.cc index 2505c62..3a861eb 100644 --- a/chrome/browser/ui/views/avatar_menu_bubble_view.cc +++ b/chrome/browser/ui/views/avatar_menu_bubble_view.cc @@ -6,6 +6,8 @@ #include <algorithm> +#include "base/command_line.h" +#include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/browser_process.h" @@ -13,8 +15,17 @@ #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_info_util.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/signin/signin_manager.h" +#include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/chrome_pages.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/url_constants.h" +#include "content/public/browser/page_navigator.h" +#include "content/public/browser/web_contents.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -24,10 +35,12 @@ #include "ui/gfx/image/image.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" #include "ui/views/controls/separator.h" +#include "ui/views/layout/grid_layout.h" #include "ui/views/widget/widget.h" namespace { @@ -219,7 +232,7 @@ class ProfileItemView : public views::CustomButton, EditProfileLink* edit_link() { return edit_link_; } private: - static gfx::ImageSkia GetBadgedIcon(const gfx::ImageSkia& icon); + gfx::ImageSkia GetBadgedIcon(const gfx::ImageSkia& icon); bool IsHighlighted(); @@ -242,7 +255,7 @@ ProfileItemView::ProfileItemView(const AvatarMenuModel::Item& item, image_view_ = new ProfileImageView(); gfx::ImageSkia profile_icon = *item_.icon.ToImageSkia(); - if (item_.active) + if (item_.active || item_.signin_required) image_view_->SetImage(GetBadgedIcon(profile_icon)); else image_view_->SetImage(profile_icon); @@ -366,8 +379,15 @@ void ProfileItemView::OnFocusStateChanged(bool has_focus) { // static gfx::ImageSkia ProfileItemView::GetBadgedIcon(const gfx::ImageSkia& icon) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - const gfx::ImageSkia* badge = rb.GetImageNamed( - IDR_PROFILE_SELECTED).ToImageSkia(); + const gfx::ImageSkia* badge = NULL; + + if (item_.active) + badge = rb.GetImageSkiaNamed(IDR_PROFILE_SELECTED); + else if (item_.signin_required) // TODO(bcwhite): create new icon + badge = rb.GetImageSkiaNamed(IDR_OMNIBOX_HTTPS_VALID); + else + NOTREACHED(); // function should only be called if one of above is true + gfx::Size icon_size = GetCenteredAndScaledRect(icon.width(), icon.height(), 0, 0, profiles::kAvatarIconWidth, kItemHeight).size(); gfx::CanvasImageSource* source = @@ -386,6 +406,67 @@ bool ProfileItemView::IsHighlighted() { } +// ActionButtonView ----------------------------------------------------------- + +// A custom view that manages the "action" buttons at the bottom of the list +// of profiles. +class ActionButtonView : public views::View { + public: + ActionButtonView(views::ButtonListener* listener, Profile* profile); + + private: + views::LabelButton* manage_button_; + views::LabelButton* signout_button_; + + DISALLOW_COPY_AND_ASSIGN(ActionButtonView); +}; + + +ActionButtonView::ActionButtonView(views::ButtonListener* listener, + Profile* profile) + : manage_button_(NULL), + signout_button_(NULL) { + std::string username; + SigninManagerBase* signin = + SigninManagerFactory::GetForProfile(profile); + if (signin != NULL) + username = signin->GetAuthenticatedUsername(); + + manage_button_ = new views::LabelButton( + listener, l10n_util::GetStringUTF16(IDS_PROFILES_MANAGE_PROFILES_BUTTON)); + manage_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + manage_button_->SetTooltipText( + l10n_util::GetStringUTF16(IDS_PROFILES_MANAGE_PROFILES_BUTTON_TIP)); + manage_button_->set_tag(IDS_PROFILES_MANAGE_PROFILES_BUTTON); + + signout_button_ = new views::LabelButton( + listener, l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON)); + signout_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + if (username.empty()) { + signout_button_->SetTooltipText( + l10n_util::GetStringUTF16( + IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP_UNAVAILABLE)); + signout_button_->SetEnabled(false); + } else { + signout_button_->SetTooltipText( + l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP, + UTF8ToUTF16(username))); + } + signout_button_->set_tag(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON); + + views::GridLayout* layout = new views::GridLayout(this); + views::ColumnSet* columns = layout->AddColumnSet(0); + columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + layout->StartRow(0, 0); + layout->AddView(signout_button_); + layout->AddView(manage_button_); + SetLayoutManager(layout); +} + + // AvatarMenuBubbleView ------------------------------------------------------- // static @@ -430,7 +511,7 @@ AvatarMenuBubbleView::AvatarMenuBubbleView( anchor_rect_(anchor_rect), browser_(browser), separator_(NULL), - add_profile_link_(NULL) { + buttons_view_(NULL) { avatar_menu_model_.reset(new AvatarMenuModel( &g_browser_process->profile_manager()->GetProfileInfoCache(), this, browser_)); @@ -448,14 +529,13 @@ gfx::Size AvatarMenuBubbleView::GetPreferredSize() { total_height += size.height() + kItemMarginY; } - if (add_profile_link_) { + if (buttons_view_) { total_height += kSeparatorPaddingY * 2 + separator_->GetPreferredSize().height(); - gfx::Size add_profile_size = add_profile_link_->GetPreferredSize(); - max_width = std::max(max_width, - add_profile_size.width() + profiles::kAvatarIconWidth + kIconMarginX); - total_height += add_profile_link_->GetPreferredSize().height(); + gfx::Size buttons_size = buttons_view_->GetPreferredSize(); + max_width = std::max(max_width, buttons_size.width()); + total_height += buttons_size.height(); } const int kBubbleViewMaxWidth = 800; @@ -475,14 +555,14 @@ void AvatarMenuBubbleView::Layout() { y += item_height + kItemMarginY; } - if (add_profile_link_) { + if (buttons_view_) { y += kSeparatorPaddingY; int separator_height = separator_->GetPreferredSize().height(); separator_->SetBounds(0, y, width(), separator_height); y += kSeparatorPaddingY + separator_height; - add_profile_link_->SetBounds(profiles::kAvatarIconWidth + kIconMarginX, y, - width(), add_profile_link_->GetPreferredSize().height()); + buttons_view_->SetBounds(0, y, + width(), buttons_view_->GetPreferredSize().height()); } } @@ -517,6 +597,15 @@ bool AvatarMenuBubbleView::AcceleratorPressed( void AvatarMenuBubbleView::ButtonPressed(views::Button* sender, const ui::Event& event) { + if (sender->tag() == IDS_PROFILES_MANAGE_PROFILES_BUTTON) { + std::string subpage = chrome::kSearchUsersSubPage; + chrome::ShowSettingsSubPage(browser_, subpage); + return; + } else if (sender->tag() == IDS_PROFILES_PROFILE_SIGNOUT_BUTTON) { + avatar_menu_model_->BeginSignOut(NULL); + return; + } + for (size_t i = 0; i < item_views_.size(); ++i) { ProfileItemView* item_view = item_views_[i]; if (sender == item_view) { @@ -531,7 +620,7 @@ void AvatarMenuBubbleView::ButtonPressed(views::Button* sender, } void AvatarMenuBubbleView::LinkClicked(views::Link* source, int event_flags) { - if (source == add_profile_link_) { + if (source == buttons_view_) { avatar_menu_model_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_ICON); return; } @@ -565,7 +654,7 @@ void AvatarMenuBubbleView::OnAvatarMenuModelChanged( AvatarMenuModel* avatar_menu_model) { // Unset all our child view references and call RemoveAllChildViews() which // will actually delete them. - add_profile_link_ = NULL; + buttons_view_ = NULL; item_views_.clear(); RemoveAllChildViews(true); @@ -579,16 +668,22 @@ void AvatarMenuBubbleView::OnAvatarMenuModelChanged( item_views_.push_back(item_view); } - if (avatar_menu_model_->ShouldShowAddNewProfileLink()) { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNewProfileManagement)) { separator_ = new views::Separator(); AddChildView(separator_); - - add_profile_link_ = new views::Link( + buttons_view_ = new ActionButtonView(this, browser_->profile()); + AddChildView(buttons_view_); + } else if (avatar_menu_model_->ShouldShowAddNewProfileLink()) { + views::Link* add_profile_link = new views::Link( l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_NEW_PROFILE_LINK)); - add_profile_link_->set_listener(this); - add_profile_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - add_profile_link_->SetBackgroundColor(color()); - AddChildView(add_profile_link_); + add_profile_link->set_listener(this); + add_profile_link->SetHorizontalAlignment(gfx::ALIGN_CENTER); + add_profile_link->SetBackgroundColor(color()); + separator_ = new views::Separator(); + AddChildView(separator_); + buttons_view_ = add_profile_link; + AddChildView(buttons_view_); } // If the bubble has already been shown then resize and reposition the bubble. diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.h b/chrome/browser/ui/views/avatar_menu_bubble_view.h index ae2d284..6036c1e 100644 --- a/chrome/browser/ui/views/avatar_menu_bubble_view.h +++ b/chrome/browser/ui/views/avatar_menu_bubble_view.h @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" #include "chrome/browser/profiles/avatar_menu_model_observer.h" #include "ui/views/bubble/bubble_delegate.h" #include "ui/views/controls/button/button.h" @@ -18,6 +19,10 @@ class AvatarMenuModel; class Browser; class ProfileItemView; +namespace content { +class WebContents; +} + namespace views { class CustomButton; class Link; @@ -92,11 +97,12 @@ class AvatarMenuBubbleView : public views::BubbleDelegateView, // avatar_menu_model_->ShouldShowAddNewProfileLink() returns true. See // OnAvatarMenuModelChanged(). views::Separator* separator_; - views::Link* add_profile_link_; + views::View* buttons_view_; static AvatarMenuBubbleView* avatar_bubble_; DISALLOW_COPY_AND_ASSIGN(AvatarMenuBubbleView); + FRIEND_TEST_ALL_PREFIXES(AvatarMenuButtonTest, SignOut); }; #endif // CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_BUBBLE_VIEW_H_ diff --git a/chrome/browser/ui/webui/sync_promo/sync_promo_ui.cc b/chrome/browser/ui/webui/sync_promo/sync_promo_ui.cc index 8f9f468..de6ae01 100644 --- a/chrome/browser/ui/webui/sync_promo/sync_promo_ui.cc +++ b/chrome/browser/ui/webui/sync_promo/sync_promo_ui.cc @@ -56,7 +56,7 @@ const char kSyncPromoQueryKeySource[] = "source"; // Gaia cannot support about:blank as a continue URL, so using a hosted blank // page instead. -const char kContinueUrlPrefix[] = +const char kSyncLandingUrlPrefix[] = "https://www.google.com/intl/%s/chrome/blank.html"; // The maximum number of times we want to show the sync promo at startup. @@ -219,6 +219,14 @@ void SyncPromoUI::SetUserSkippedSyncPromo(Profile* profile) { } // static +std::string SyncPromoUI::GetSyncLandingURL(const char* option, int value) { + const std::string& locale = g_browser_process->GetApplicationLocale(); + std::string url = base::StringPrintf(kSyncLandingUrlPrefix, locale.c_str()); + base::StringAppendF(&url, "?%s=%d", option, value); + return url; +} + +// static GURL SyncPromoUI::GetSyncPromoURL(const GURL& next_page, Source source, bool auto_close) { @@ -242,11 +250,8 @@ GURL SyncPromoUI::GetSyncPromoURL(const GURL& next_page, url_string = GaiaUrls::GetInstance()->service_login_url(); url_string.append("?service=chromiumsync&sarp=1"); - const std::string& locale = g_browser_process->GetApplicationLocale(); - std::string continue_url = base::StringPrintf(kContinueUrlPrefix, - locale.c_str()); - base::StringAppendF(&continue_url, "?%s=%d", kSyncPromoQueryKeySource, - static_cast<int>(source)); + std::string continue_url = GetSyncLandingURL( + kSyncPromoQueryKeySource, static_cast<int>(source)); base::StringAppendF(&url_string, "&%s=%s", kSyncPromoQueryKeyContinue, net::EscapeQueryParamValue( @@ -321,7 +326,7 @@ bool SyncPromoUI::IsContinueUrlForWebBasedSigninFlow(const GURL& url) { replacements.ClearQuery(); const std::string& locale = g_browser_process->GetApplicationLocale(); return url.ReplaceComponents(replacements) == - GURL(base::StringPrintf(kContinueUrlPrefix, locale.c_str())); + GURL(base::StringPrintf(kSyncLandingUrlPrefix, locale.c_str())); } // static diff --git a/chrome/browser/ui/webui/sync_promo/sync_promo_ui.h b/chrome/browser/ui/webui/sync_promo/sync_promo_ui.h index 829ec11..2dc281f 100644 --- a/chrome/browser/ui/webui/sync_promo/sync_promo_ui.h +++ b/chrome/browser/ui/webui/sync_promo/sync_promo_ui.h @@ -52,6 +52,9 @@ class SyncPromoUI : public content::WebUIController { // Registers the preferences the Sync Promo UI needs. static void RegisterUserPrefs(PrefRegistrySyncable* registry); + // Gets the sync landing page URL. + static std::string GetSyncLandingURL(const char* option, int value); + // Returns the sync promo URL wth the given arguments in the query. // |next_page| is the URL to navigate to when the user completes or skips the // promo. If an empty URL is given then the promo will navigate to the NTP. diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index afae846..32597bb 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1457,6 +1457,7 @@ 'browser/printing/print_preview_dialog_controller_browsertest.cc', 'browser/printing/printing_layout_browsertest.cc', 'browser/process_singleton_browsertest.cc', + 'browser/profiles/avatar_menu_model_browsertest.cc', 'browser/profiles/profile_browsertest.cc', 'browser/profiles/profile_manager_browsertest.cc', 'browser/referrer_policy_browsertest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 89842e0..86ed6d8 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -929,6 +929,10 @@ const char kNativeMessagingHosts[] = "native-messaging-hosts"; // Intended primarily for use with --log-net-log. const char kNetLogLevel[] = "net-log-level"; +// Use new profile management system, including profile sign-out and new +// choosers. +const char kNewProfileManagement[] = "new-profile-management"; + // Disables the default browser check. Useful for UI/browser tests where we // want to avoid having the default browser info-bar displayed. const char kNoDefaultBrowserCheck[] = "no-default-browser-check"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 9f441de..e0dbca6 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -250,6 +250,7 @@ extern const char kNaClLoaderCmdPrefix[]; extern const char kNaClLoaderProcess[]; extern const char kNativeMessagingHosts[]; extern const char kNetLogLevel[]; +extern const char kNewProfileManagement[]; extern const char kNoDefaultBrowserCheck[]; extern const char kNoDisplayingInsecureContent[]; extern const char kNoEvents[]; diff --git a/google_apis/gaia/gaia_urls.cc b/google_apis/gaia/gaia_urls.cc index f11ba89..543bd88 100644 --- a/google_apis/gaia/gaia_urls.cc +++ b/google_apis/gaia/gaia_urls.cc @@ -18,6 +18,7 @@ const char kCaptchaUrlPrefixSuffix[] = "/"; // API calls from accounts.google.com const char kClientLoginUrlSuffix[] = "/ClientLogin"; const char kServiceLoginUrlSuffix[] = "/ServiceLogin"; +const char kServiceLogoutUrlSuffix[] = "/Logout"; const char kIssueAuthTokenUrlSuffix[] = "/IssueAuthToken"; const char kGetUserInfoUrlSuffix[] = "/GetUserInfo"; const char kTokenAuthUrlSuffix[] = "/TokenAuth"; @@ -100,6 +101,7 @@ GaiaUrls::GaiaUrls() { gaia_login_form_realm_ = gaia_url_base + "/"; client_login_url_ = gaia_url_base + kClientLoginUrlSuffix; service_login_url_ = gaia_url_base + kServiceLoginUrlSuffix; + service_logout_url_ = gaia_url_base + kServiceLogoutUrlSuffix; issue_auth_token_url_ = gaia_url_base + kIssueAuthTokenUrlSuffix; get_user_info_url_ = gaia_url_base + kGetUserInfoUrlSuffix; token_auth_url_ = gaia_url_base + kTokenAuthUrlSuffix; @@ -164,6 +166,10 @@ const std::string& GaiaUrls::service_login_url() { return service_login_url_; } +const std::string& GaiaUrls::service_logout_url() { + return service_logout_url_; +} + const std::string& GaiaUrls::issue_auth_token_url() { return issue_auth_token_url_; } diff --git a/google_apis/gaia/gaia_urls.h b/google_apis/gaia/gaia_urls.h index 7bdfcfb..5a5cb11 100644 --- a/google_apis/gaia/gaia_urls.h +++ b/google_apis/gaia/gaia_urls.h @@ -20,6 +20,7 @@ class GaiaUrls { const std::string& gaia_origin_url(); const std::string& client_login_url(); const std::string& service_login_url(); + const std::string& service_logout_url(); const std::string& issue_auth_token_url(); const std::string& get_user_info_url(); const std::string& token_auth_url(); @@ -57,6 +58,7 @@ class GaiaUrls { std::string google_apis_origin_url_; std::string client_login_url_; std::string service_login_url_; + std::string service_logout_url_; std::string issue_auth_token_url_; std::string get_user_info_url_; std::string token_auth_url_; |