Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Unified Diff: chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm

Issue 2174973002: [Mac][MD User Menu] Revamped signin/sync error surfacing UI (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
index 2b8dbf2e385f24fcc263f5e6a2abb4d259376a46..305837f176096291be946312e55b20fd276495a8 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
@@ -32,6 +32,7 @@
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
@@ -51,11 +52,13 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/browser/profile_sync_service.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/signin/core/browser/signin_metrics.h"
#include "components/signin/core/common/profile_management_switches.h"
+#include "components/sync_driver/sync_error_controller.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_widget_host_view.h"
@@ -931,10 +934,11 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
// Builds the regular profile chooser view.
- (void)buildProfileChooserViewWithProfileView:(NSView*)currentProfileView
tutorialView:(NSView*)tutorialView
+ syncErrorView:(NSView*)syncErrorView
otherProfiles:(NSArray*)otherProfiles
atYOffset:(CGFloat)yOffset
inContainer:(NSView*)container
- showLock:(bool)showLock;
+ showLock:(bool)showLock;
// Builds the profile chooser view.
- (NSView*)buildProfileChooserView;
@@ -957,6 +961,12 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
linkAction:(SEL)linkAction
buttonAction:(SEL)buttonAction;
+// Builds a header for signin and sync error surfacing on the user menu.
+- (NSView*)buildSyncErrorViewIfNeeded;
+- (NSView*)buildSyncErrorViewWithContent:(int)contentStringId
+ buttonStringId:(int)buttonStringId
+ buttonAction:(SEL)buttonAction;
+
// Builds a tutorial card to introduce an upgrade user to the new avatar menu if
// needed. |tutorial_shown| indicates if the tutorial has already been shown in
// the previous active view. |avatar_item| refers to the current profile.
@@ -1165,11 +1175,32 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
[self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL];
}
+- (IBAction)showSignoutView:(id)sender {
+ chrome::ShowSettingsSubPage(browser_, chrome::kSignOutSubPage);
+}
+
+- (IBAction)showSignoutSigninView:(id)sender {
+ if (ProfileSyncServiceFactory::GetForProfile(browser_->profile()))
+ ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
+ SigninManagerFactory::GetForProfile(browser_->profile())
+ ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+ [self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN];
+}
+
- (IBAction)showAccountReauthenticationView:(id)sender {
DCHECK(!isGuestSession_);
[self showSigninUIForMode:profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH];
}
+- (IBAction)showUpdateChromeView:(id)sender {
+ chrome::OpenUpdateChromeDialog(browser_);
+}
+
+- (IBAction)showSyncPassphraseSetupView:(id)sender {
+ chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
+}
+
- (IBAction)removeAccount:(id)sender {
DCHECK(!accountIdToRemove_.empty());
ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile())
@@ -1404,10 +1435,11 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
- (void)buildProfileChooserViewWithProfileView:(NSView*)currentProfileView
tutorialView:(NSView*)tutorialView
+ syncErrorView:(NSView*)syncErrorView
otherProfiles:(NSArray*)otherProfiles
atYOffset:(CGFloat)yOffset
inContainer:(NSView*)container
- showLock:(bool)showLock {
+ showLock:(bool)showLock {
if (switches::IsMaterialDesignUserMenu())
yOffset += kRelatedControllVerticalSpacing;
@@ -1450,14 +1482,23 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
}
}
+ const AvatarMenu::Item& item =
+ avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex());
if (viewMode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) {
- NSView* currentProfileAccountsView = [self
- createCurrentProfileAccountsView:NSMakeRect(0, yOffset,
- GetFixedMenuWidth(), 0)];
- [container addSubview:currentProfileAccountsView];
- yOffset = NSMaxY([currentProfileAccountsView frame]);
+ if (item.signed_in) {
+ NSView* currentProfileAccountsView = [self
+ createCurrentProfileAccountsView:NSMakeRect(0, yOffset,
+ GetFixedMenuWidth(), 0)];
+ [container addSubview:currentProfileAccountsView];
+ yOffset = NSMaxY([currentProfileAccountsView frame]);
- yOffset = [self addSeparatorToContainer:container atYOffset: yOffset];
+ yOffset = [self addSeparatorToContainer:container atYOffset:yOffset];
+ } else {
+ // This is the case when the user selects the sign out option in the user
+ // menu upon encountering unrecoverable errors. Afterwards, the profile
+ // chooser view is shown instead of the account management view.
+ viewMode_ = profiles::BUBBLE_VIEW_MODE_FAST_PROFILE_CHOOSER;
+ }
}
// Active profile card.
@@ -1471,6 +1512,13 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
yOffset = NSMaxY([currentProfileView frame]) + verticalSpacing;
}
+ if (syncErrorView) {
+ yOffset = [self addSeparatorToContainer:container atYOffset:yOffset];
+ [syncErrorView setFrameOrigin:NSMakePoint(0, yOffset)];
+ [container addSubview:syncErrorView];
+ yOffset = NSMaxY([syncErrorView frame]);
+ }
+
if (tutorialView) {
[tutorialView setFrameOrigin:NSMakePoint(0, yOffset)];
[container addSubview:tutorialView];
@@ -1488,6 +1536,7 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
[[NSView alloc] initWithFrame:NSZeroRect]);
NSView* tutorialView = nil;
+ NSView* syncErrorView = nil;
NSView* currentProfileView = nil;
base::scoped_nsobject<NSMutableArray> otherProfiles(
[[NSMutableArray alloc] init]);
@@ -1507,7 +1556,9 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
for (int i = avatarMenu_->GetNumberOfItems() - 1; i >= 0; --i) {
const AvatarMenu::Item& item = avatarMenu_->GetItemAt(i);
if (item.active) {
- if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
+ if (switches::IsMaterialDesignUserMenu()) {
+ syncErrorView = [self buildSyncErrorViewIfNeeded];
+ } else if (viewMode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
tutorialView = [self buildTutorialViewIfNeededForItem:item];
}
currentProfileView =
@@ -1537,10 +1588,11 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
} else {
[self buildProfileChooserViewWithProfileView:currentProfileView
tutorialView:tutorialView
+ syncErrorView:syncErrorView
otherProfiles:otherProfiles.get()
atYOffset:yOffset
inContainer:container
- showLock:showLock];
+ showLock:showLock];
}
return container.autorelease();
@@ -1803,6 +1855,135 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver,
return container.autorelease();
}
+- (NSView*)buildSyncErrorViewIfNeeded {
+ ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(browser_->profile());
+
+ // The order or priority is going to be: 1. Unrecoverable errors.
+ // 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
+ if (service && service->HasUnrecoverableError()) {
groby-ooo-7-16 2016/07/22 23:53:16 It seems a ton of this logic is shared with the Vi
+ // An unrecoverable error is sometimes accompanied by an actionable error.
+ // If an actionable error is not set to be UPGRADE_CLIENT, then show a
+ // generic unrecoverable error message.
+ ProfileSyncService::Status status;
+ service->QueryDetailedSyncStatus(&status);
+ if (status.sync_protocol_error.action != syncer::UPGRADE_CLIENT) {
+ // Display different messages and buttons for managed accounts.
+ if (SigninManagerFactory::GetForProfile(browser_->profile())
+ ->IsSignoutProhibited()) {
+ // For a managed user, the user is directed to the signout
+ // confirmation dialogue in the settings page.
+ return [self buildSyncErrorViewWithContent:
+ IDS_SYNC_ERROR_USER_MENU_SIGNOUT_MESSAGE
+ buttonStringId:
+ IDS_SYNC_ERROR_USER_MENU_SIGNOUT_BUTTON
+ buttonAction:@selector(showSignoutView:)];
+ }
+ // For a non-managed user, we sign out on the user's behalf and prompt
+ // the user to sign in again.
+ return [self
+ buildSyncErrorViewWithContent:
+ IDS_SYNC_ERROR_USER_MENU_SIGNIN_AGAIN_MESSAGE
+ buttonStringId:
+ IDS_SYNC_ERROR_USER_MENU_SIGNIN_AGAIN_BUTTON
+ buttonAction:@selector(showSignoutSigninView:)];
+ }
+ }
+
+ // Check for an auth error.
+ if (HasAuthError(browser_->profile())) {
+ return [self
+ buildSyncErrorViewWithContent:IDS_SYNC_ERROR_USER_MENU_SIGNIN_MESSAGE
+ buttonStringId:IDS_SYNC_ERROR_USER_MENU_SIGNIN_BUTTON
+ buttonAction:@selector(
+ showAccountReauthenticationView:)];
+ }
+
+ // Check for sync errors if the sync service is enabled.
+ if (service) {
+ // Check for an actionable UPGRADE_CLIENT error.
+ ProfileSyncService::Status status;
+ service->QueryDetailedSyncStatus(&status);
+ if (status.sync_protocol_error.action == syncer::UPGRADE_CLIENT) {
+ return [self
+ buildSyncErrorViewWithContent:IDS_SYNC_ERROR_USER_MENU_UPGRADE_MESSAGE
+ buttonStringId:IDS_SYNC_ERROR_USER_MENU_UPGRADE_BUTTON
+ buttonAction:@selector(showUpdateChromeView:)];
+ }
+
+ // Check for a sync passphrase error.
+ SyncErrorController* sync_error_controller =
+ service->sync_error_controller();
+ if (sync_error_controller && sync_error_controller->HasError()) {
+ return [self
+ buildSyncErrorViewWithContent:
+ IDS_SYNC_ERROR_USER_MENU_PASSPHRASE_MESSAGE
+ buttonStringId:
+ IDS_SYNC_ERROR_USER_MENU_PASSPHRASE_BUTTON
+ buttonAction:@selector(
+ showSyncPassphraseSetupView:)];
+ }
+ }
+
+ // There is no error.
+ return nil;
+}
+
+- (NSView*)buildSyncErrorViewWithContent:(int)contentStringId
+ buttonStringId:(int)buttonStringId
+ buttonAction:(SEL)buttonAction {
+ base::scoped_nsobject<NSView> container(
+ [[NSView alloc] initWithFrame:NSMakeRect(0, 0, GetFixedMenuWidth(), 0)]);
+ CGFloat iconSize = 20.0;
+ CGFloat xOffset = kHorizontalSpacing + iconSize + 12.0;
+ CGFloat availableWidth = GetFixedMenuWidth() - xOffset - kHorizontalSpacing;
+ CGFloat yOffset = 20.0;
+
+ // Adds an action button for resolving the error at the bottom.
+ base::scoped_nsobject<NSButton> resolveErrorButton(
+ [[BlueLabelButton alloc] initWithFrame:NSZeroRect]);
+ [resolveErrorButton setTitle:l10n_util::GetNSString(buttonStringId)];
+ [resolveErrorButton setTarget:self];
+ [resolveErrorButton setAction:buttonAction];
+ [resolveErrorButton setAlignment:NSCenterTextAlignment];
+ [resolveErrorButton sizeToFit];
+ [resolveErrorButton setFrameOrigin:NSMakePoint(xOffset, yOffset)];
+ [container addSubview:resolveErrorButton];
+ yOffset = NSMaxY([resolveErrorButton frame]) + kVerticalSpacing;
+
+ // Adds the error message content.
+ NSTextField* contentLabel =
+ BuildLabel(l10n_util::GetNSString(contentStringId),
+ NSMakePoint(xOffset, yOffset), nil);
+ [contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
+ [container addSubview:contentLabel];
+ yOffset = NSMaxY([contentLabel frame]) + 4;
+
+ // Adds the title for the error card.
+ NSTextField* titleLabel =
+ BuildLabel(l10n_util::GetNSString(IDS_SYNC_ERROR_USER_MENU_TITLE),
+ NSMakePoint(xOffset, yOffset),
+ skia::SkColorToCalibratedNSColor(gfx::kGoogleRed700));
+ [titleLabel setFrameSize:NSMakeSize(availableWidth, 0)];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleLabel];
+ [container addSubview:titleLabel];
+ yOffset = NSMaxY([titleLabel frame]);
+
+ // Adds the sync problem icon.
+ base::scoped_nsobject<NSImageView> syncProblemIcon([[NSImageView alloc]
+ initWithFrame:NSMakeRect(kHorizontalSpacing, yOffset - iconSize, iconSize,
+ iconSize)]);
+ [syncProblemIcon setImage:NSImageFromImageSkia(gfx::CreateVectorIcon(
+ gfx::VectorIconId::SYNC_PROBLEM, iconSize,
+ gfx::kGoogleRed700))];
+ [container addSubview:syncProblemIcon];
+
+ [container
+ setFrameSize:NSMakeSize(GetFixedMenuWidth(), yOffset + kVerticalSpacing)];
+ return container.autorelease();
+}
+
- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item {
base::scoped_nsobject<NSView> container([[NSView alloc]
initWithFrame:NSZeroRect]);

Powered by Google App Engine
This is Rietveld 408576698