diff options
| author | anthonyvd <anthonyvd@chromium.org> | 2016-02-04 15:16:56 -0800 |
|---|---|---|
| committer | Commit bot <commit-bot@chromium.org> | 2016-02-04 23:18:06 +0000 |
| commit | e659ee93a6090c6d8f52ef45b62da65e08e93154 (patch) | |
| tree | 0293a00d23489b7b1484b3c6533a9567976787a4 | |
| parent | 85bb4f9320cda939d467cab50e846ac21830bb71 (diff) | |
| download | chromium_src-e659ee93a6090c6d8f52ef45b62da65e08e93154.zip chromium_src-e659ee93a6090c6d8f52ef45b62da65e08e93154.tar.gz chromium_src-e659ee93a6090c6d8f52ef45b62da65e08e93154.tar.bz2 | |
Implement the tab-modal sign in flow on Mac.
BUG=533004
TEST=
1. Enable the #enable-password-separated-signin-flow flags in chrome://flags and restart Chrome
2. In a non-signed in profile, open the User Menu and click "Sign In to Chrome"
3. The signin flow should open in a tab modal dialog
4. After entering valid credentials, the dialog should close and the Sync Confirmation dialog should be shown
Review URL: https://codereview.chromium.org/1604043004
Cr-Commit-Position: refs/heads/master@{#373653}
24 files changed, 916 insertions, 428 deletions
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 66891b1..03a8170 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1348,7 +1348,7 @@ const FeatureEntry kFeatureEntries[] = { {"enable-password-separated-signin-flow", IDS_FLAGS_ENABLE_PASSWORD_SEPARATED_SIGNIN_FLOW_NAME, IDS_FLAGS_ENABLE_PASSWORD_SEPARATED_SIGNIN_FLOW_DESCRIPTION, - kOsWin | kOsLinux, + kOsMac | kOsWin | kOsLinux, ENABLE_DISABLE_VALUE_TYPE(switches::kEnablePasswordSeparatedSigninFlow, switches::kDisablePasswordSeparatedSigninFlow)}, {"enable-google-profile-info", diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 8ceee96..942b4ab 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -952,6 +952,19 @@ void Browser::UpdateUIForNavigationInTab(WebContents* contents, contents->SetInitialFocus(); } +void Browser::ShowModalSigninWindow(profiles::BubbleViewMode mode, + signin_metrics::AccessPoint access_point) { + signin_view_controller_.ShowModalSignin(mode, this, access_point); +} + +void Browser::CloseModalSigninWindow() { + signin_view_controller_.CloseModalSignin(); +} + +void Browser::ShowModalSyncConfirmationWindow() { + signin_view_controller_.ShowModalSyncConfirmationDialog(this); +} + /////////////////////////////////////////////////////////////////////////////// // Browser, PageNavigator implementation: diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 37d23b3..b9813b8 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -28,8 +28,10 @@ #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h" #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" #include "chrome/browser/ui/host_desktop.h" +#include "chrome/browser/ui/profile_chooser_constants.h" #include "chrome/browser/ui/search/search_tab_helper_delegate.h" #include "chrome/browser/ui/search_engines/search_engine_tab_helper_delegate.h" +#include "chrome/browser/ui/signin_view_controller.h" #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "components/content_settings/core/common/content_settings.h" @@ -412,6 +414,21 @@ class Browser : public TabStripModelObserver, ui::PageTransition transition, bool user_initiated); + // Shows the signin flow for |mode| in a tab-modal dialog. + // |access_point| indicates the access point used to open the Gaia sign in + // page. + void ShowModalSigninWindow(profiles::BubbleViewMode mode, + signin_metrics::AccessPoint access_point); + + // Closes the tab-modal signin flow opened with ShowModalSigninWindow, if it's + // open. Does nothing otherwise. + void CloseModalSigninWindow(); + + // Shows the tab modal sync confirmation dialog that informs the user about + // sync and gives them a chance to abort signin under the tab modal signin + // flow. + void ShowModalSyncConfirmationWindow(); + // Interface implementations //////////////////////////////////////////////// // Overridden from content::PageNavigator: @@ -992,6 +1009,8 @@ class Browser : public TabStripModelObserver, scoped_ptr<ValidationMessageBubble> validation_message_bubble_; + SigninViewController signin_view_controller_; + // The following factory is used for chrome update coalescing. base::WeakPtrFactory<Browser> chrome_updater_factory_; diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 4b99840..467a1ca 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h @@ -375,22 +375,6 @@ class BrowserWindow : public ui::BaseWindow { const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) = 0; - // Shows the signin flow for |mode| in a tab-modal dialog. - // |access_point| indicates the access point used to open the Gaia sign in - // page. - virtual void ShowModalSigninWindow( - AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) = 0; - - // Closes the tab-modal signin flow opened with ShowModalSigninWindow, if it's - // open. Does nothing otherwise. - virtual void CloseModalSigninWindow() = 0; - - // Shows the tab modal sync confirmation dialog that informs the user about - // sync and gives them a chance to abort signin under the tab modal signin - // flow. - virtual void ShowModalSyncConfirmationWindow() = 0; - // Returns the height inset for RenderView when detached bookmark bar is // shown. Invoked when a new RenderHostView is created for a non-NTP // navigation entry and the bookmark bar is detached. diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h index 88f4eca..17eadf0 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.h +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h @@ -148,10 +148,6 @@ class BrowserWindowCocoa AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) override; - void ShowModalSigninWindow(AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) override; - void CloseModalSigninWindow() override; - void ShowModalSyncConfirmationWindow() override; int GetRenderViewHeightInsetWithDetachedBookmarkBar() override; void ExecuteExtensionCommand(const extensions::Extension* extension, const extensions::Command& command) override; diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm index c69b89b..c425643 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm @@ -17,6 +17,7 @@ #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/signin/chrome_signin_helper.h" #include "chrome/browser/translate/chrome_translate_client.h" @@ -47,6 +48,7 @@ #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" #import "chrome/browser/ui/cocoa/website_settings/website_settings_bubble_controller.h" #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" +#include "chrome/browser/ui/profile_chooser_constants.h" #include "chrome/browser/ui/search/search_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app.h" @@ -803,28 +805,23 @@ void BrowserWindowCocoa::ShowAvatarBubbleFromAvatarButton( AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) { - AvatarBaseController* controller = [controller_ avatarButtonController]; - NSView* anchor = [controller buttonView]; - if ([anchor isHiddenOrHasHiddenAncestor]) - anchor = [[controller_ toolbarController] appMenuButton]; - [controller showAvatarBubbleAnchoredAt:anchor - withMode:mode - withServiceType:manage_accounts_params.service_type - fromAccessPoint:access_point]; -} - -void BrowserWindowCocoa::ShowModalSigninWindow( - AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) { - NOTREACHED(); -} + profiles::BubbleViewMode bubble_view_mode; + profiles::TutorialMode tutorial_mode; + profiles::BubbleViewModeFromAvatarBubbleMode(mode, &bubble_view_mode, + &tutorial_mode); -void BrowserWindowCocoa::CloseModalSigninWindow() { - NOTREACHED(); -} - -void BrowserWindowCocoa::ShowModalSyncConfirmationWindow() { - NOTREACHED(); + if (SigninViewController::ShouldShowModalSigninForMode(bubble_view_mode)) { + browser_->ShowModalSigninWindow(bubble_view_mode, access_point); + } else { + AvatarBaseController* controller = [controller_ avatarButtonController]; + NSView* anchor = [controller buttonView]; + if ([anchor isHiddenOrHasHiddenAncestor]) + anchor = [[controller_ toolbarController] appMenuButton]; + [controller showAvatarBubbleAnchoredAt:anchor + withMode:mode + withServiceType:manage_accounts_params.service_type + fromAccessPoint:access_point]; + } } int diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm index 07833b1..35f6e03 100644 --- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm +++ b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm @@ -41,6 +41,7 @@ #import "chrome/browser/ui/cocoa/browser_window_utils.h" #import "chrome/browser/ui/cocoa/info_bubble_view.h" #import "chrome/browser/ui/cocoa/info_bubble_window.h" +#include "chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h" #import "chrome/browser/ui/cocoa/profiles/user_manager_mac.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/user_manager.h" @@ -101,7 +102,6 @@ const CGFloat kFocusRingLineWidth = 2; // Fixed size for embedded sign in pages as defined in Gaia. const CGFloat kFixedGaiaViewWidth = 360; -const CGFloat kFixedGaiaViewHeight = 440; // Fixed size for the account removal view. const CGFloat kFixedAccountRemovalViewWidth = 280; @@ -1059,11 +1059,17 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver, } - (IBAction)showInlineSigninPage:(id)sender { - [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN]; + signin::ManageAccountsParams params; + params.service_type = serviceType_; + browser_->window()->ShowAvatarBubbleFromAvatarButton( + BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN, params, accessPoint_); } - (IBAction)addAccount:(id)sender { - [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT]; + signin::ManageAccountsParams params; + params.service_type = serviceType_; + browser_->window()->ShowAvatarBubbleFromAvatarButton( + BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT, params, accessPoint_); [self postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT]; } @@ -1096,7 +1102,10 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver, - (IBAction)showAccountReauthenticationView:(id)sender { DCHECK(!isGuestSession_); - [self initMenuContentsWithView:profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH]; + signin::ManageAccountsParams params; + params.service_type = serviceType_; + browser_->window()->ShowAvatarBubbleFromAvatarButton( + BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH, params, accessPoint_); } - (IBAction)removeAccount:(id)sender { @@ -2082,26 +2091,16 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver, [[NSView alloc] initWithFrame:NSZeroRect]); CGFloat yOffset = 0; - GURL url; int messageId = -1; switch (viewMode_) { case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN: - url = signin::GetPromoURL( - accessPoint_, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, - false /* auto_close */, true /* is_constrained */); messageId = IDS_PROFILES_GAIA_SIGNIN_TITLE; break; case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: - url = signin::GetPromoURL( - accessPoint_, signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT, - false /* auto_close */, true /* is_constrained */); messageId = IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE; break; case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: DCHECK(HasAuthError(browser_->profile())); - url = signin::GetReauthURL( - accessPoint_, signin_metrics::Reason::REASON_REAUTHENTICATION, - browser_->profile(), GetAuthErrorAccountId(browser_->profile())); messageId = IDS_PROFILES_GAIA_REAUTH_TITLE; break; default: @@ -2109,21 +2108,13 @@ class ActiveProfileObserverBridge : public AvatarMenuObserver, break; } - webContents_.reset(content::WebContents::Create( - content::WebContents::CreateParams(browser_->profile()))); - webContentsDelegate_.reset(new GaiaWebContentsDelegate()); - webContents_->SetDelegate(webContentsDelegate_.get()); - webContents_->GetController().LoadURL(url, - content::Referrer(), - ui::PAGE_TRANSITION_AUTO_TOPLEVEL, - std::string()); + webContents_ = SigninViewControllerDelegateMac::CreateGaiaWebContents( + webContentsDelegate_.get(), viewMode_, browser_->profile(), accessPoint_); + NSView* webview = webContents_->GetNativeView(); - [webview setFrameSize:NSMakeSize(kFixedGaiaViewWidth, kFixedGaiaViewHeight)]; + [container addSubview:webview]; - content::RenderWidgetHostView* rwhv = webContents_->GetRenderWidgetHostView(); - if (rwhv) - rwhv->SetBackgroundColor(profiles::kAvatarBubbleGaiaBackgroundColor); yOffset = NSMaxY([webview frame]); // Adds the title card. diff --git a/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h b/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h new file mode 100644 index 0000000..c0f69600 --- /dev/null +++ b/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h @@ -0,0 +1,82 @@ +// Copyright 2016 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. + +#ifndef CHROME_BROWSER_UI_COCOA_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_MAC_H_ +#define CHROME_BROWSER_UI_COCOA_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_MAC_H_ + +#import <Cocoa/Cocoa.h> + +#include "base/mac/scoped_nsobject.h" +#include "base/macros.h" +#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h" +#include "chrome/browser/ui/profile_chooser_constants.h" +#include "chrome/browser/ui/signin_view_controller_delegate.h" + +@class ConstrainedWindowCustomWindow; +class ConstrainedWindowMac; +@class NavigationButtonClickedHandler; +class Profile; + +namespace content { +class WebContents; +class WebContentsDelegate; +} + +namespace signin_metrics { +enum class AccessPoint; +} + +// Cocoa implementation of SigninViewControllerDelegate. It's responsible for +// managing the Signin and Sync Confirmation tab-modal dialogs. +// Instances of this class delete themselves when the window they manage is +// closed (in the OnConstrainedWindowClosed callback). +class SigninViewControllerDelegateMac : public ConstrainedWindowMacDelegate, + public SigninViewControllerDelegate { + public: + SigninViewControllerDelegateMac(SigninViewController* signin_view_controller, + scoped_ptr<content::WebContents> web_contents, + content::WebContents* host_web_contents, + NSRect frame); + + static + SigninViewControllerDelegateMac* CreateModalSigninDelegateWithNavigation( + SigninViewController* signin_view_controller, + scoped_ptr<content::WebContents> web_contents, + content::WebContents* host_web_contents, + NSRect frame); + + void ButtonClicked(); + void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override; + + // Creates the web view that contains the signin flow in |mode| using + // |profile| as the web content's profile, then sets |delegate| as the created + // web content's delegate. + static scoped_ptr<content::WebContents> CreateGaiaWebContents( + content::WebContentsDelegate* delegate, + profiles::BubbleViewMode mode, + Profile* profile, + signin_metrics::AccessPoint access_point); + + static scoped_ptr<content::WebContents> CreateSyncConfirmationWebContents( + Profile* profile); + + private: + void ShowBackArrow() override; + void ShowCloseButton() override; + void PerformClose() override; + + ~SigninViewControllerDelegateMac() override; + void AddNavigationButton(); + + base::scoped_nsobject<NSButton> back_button_; + base::scoped_nsobject<NSView> host_view_; + scoped_ptr<ConstrainedWindowMac> constrained_window_; + scoped_ptr<content::WebContents> web_contents_; + base::scoped_nsobject<ConstrainedWindowCustomWindow> window_; + base::scoped_nsobject<NavigationButtonClickedHandler> handler_; + + DISALLOW_COPY_AND_ASSIGN(SigninViewControllerDelegateMac); +}; + +#endif // CHROME_BROWSER_UI_COCOA_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_MAC_H_ diff --git a/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.mm b/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.mm new file mode 100644 index 0000000..e968d8a --- /dev/null +++ b/chrome/browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.mm @@ -0,0 +1,235 @@ +// Copyright 2016 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/ui/cocoa/profiles/signin_view_controller_delegate_mac.h" + +#import <Cocoa/Cocoa.h> + +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_avatar_icon_util.h" +#include "chrome/browser/signin/signin_promo.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h" +#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/url_constants.h" +#include "components/signin/core/common/profile_management_switches.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "ui/gfx/image/image_skia_util_mac.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icons.h" + +namespace { + +// Dimensions of the web contents containing the old-style signin flow with the +// username and password challenge on the same form. +const CGFloat kPasswordCombinedFixedGaiaViewHeight = 440; +const CGFloat kPasswordCombinedFixedGaiaViewWidth = 360; + +// Dimensions of the tab-modal dialog displaying the password-separated signin +// flow. These match the dimensions of the server content they display. +const CGFloat kFixedGaiaViewHeight = 512; +const CGFloat kFixedGaiaViewWidth = 448; + +// Dimensions of the sync confirmation tab-modal dialog. These are taken from +// the design spec for this feature. +const int kSyncConfirmationDialogWidth = 448; +const int kSyncConfirmationDialogHeight = 351; + +} // namespace + +class ModalSigninDelegateMac; + +@interface NavigationButtonClickedHandler : NSObject { + @private + SigninViewControllerDelegateMac* delegate_; +} +- (id)initWithModalSigninDelegate:(SigninViewControllerDelegateMac*)delegate; +- (void)buttonClicked:(id)sender; +@end + +@implementation NavigationButtonClickedHandler + +- (id)initWithModalSigninDelegate:(SigninViewControllerDelegateMac*)delegate { + if (self = [super init]) { + delegate_ = delegate; + } + + return self; +} + +- (void)buttonClicked:(id)sender { + delegate_->ButtonClicked(); +} + +@end + +SigninViewControllerDelegateMac::SigninViewControllerDelegateMac( + SigninViewController* signin_view_controller, + scoped_ptr<content::WebContents> web_contents, + content::WebContents* host_web_contents, + NSRect frame) + : SigninViewControllerDelegate(signin_view_controller, web_contents.get()), + host_view_([[NSView alloc] initWithFrame:frame]), + web_contents_(std::move(web_contents)), + window_( + [[ConstrainedWindowCustomWindow alloc] initWithContentRect:frame]) { + window_.get().contentView = host_view_; + [host_view_ addSubview:web_contents_->GetNativeView()]; + + base::scoped_nsobject<CustomConstrainedWindowSheet> sheet( + [[CustomConstrainedWindowSheet alloc] initWithCustomWindow:window_]); + constrained_window_.reset( + new ConstrainedWindowMac(this, host_web_contents, sheet)); +} + +SigninViewControllerDelegateMac::~SigninViewControllerDelegateMac() {} + +SigninViewControllerDelegateMac* +SigninViewControllerDelegateMac::CreateModalSigninDelegateWithNavigation( + SigninViewController* signin_view_controller, + scoped_ptr<content::WebContents> web_contents, + content::WebContents* host_web_contents, + NSRect frame) { + SigninViewControllerDelegateMac* delegate = + new SigninViewControllerDelegateMac(signin_view_controller, + std::move(web_contents), + host_web_contents, frame); + delegate->AddNavigationButton(); + return delegate; +} + +void SigninViewControllerDelegateMac::ButtonClicked() { + NavigationButtonClicked(web_contents_.get()); +} + +void SigninViewControllerDelegateMac::OnConstrainedWindowClosed( + ConstrainedWindowMac* window) { + ResetSigninViewControllerDelegate(); + delete this; +} + +// static +scoped_ptr<content::WebContents> +SigninViewControllerDelegateMac::CreateGaiaWebContents( + content::WebContentsDelegate* delegate, + profiles::BubbleViewMode mode, + Profile* profile, + signin_metrics::AccessPoint access_point) { + GURL url = + signin::GetSigninURLFromBubbleViewMode(profile, mode, access_point); + + scoped_ptr<content::WebContents> web_contents(content::WebContents::Create( + content::WebContents::CreateParams(profile))); + + if (delegate) + web_contents->SetDelegate(delegate); + + web_contents->GetController().LoadURL(url, content::Referrer(), + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, + std::string()); + NSView* webview = web_contents->GetNativeView(); + [webview + setFrameSize:switches::UsePasswordSeparatedSigninFlow() + ? NSMakeSize(kFixedGaiaViewWidth, kFixedGaiaViewHeight) + : NSMakeSize(kPasswordCombinedFixedGaiaViewWidth, + kPasswordCombinedFixedGaiaViewHeight)]; + + content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetBackgroundColor(profiles::kAvatarBubbleGaiaBackgroundColor); + + return web_contents; +} + +// static +scoped_ptr<content::WebContents> +SigninViewControllerDelegateMac::CreateSyncConfirmationWebContents( + Profile* profile) { + scoped_ptr<content::WebContents> web_contents(content::WebContents::Create( + content::WebContents::CreateParams(profile))); + web_contents->GetController().LoadURL( + GURL(chrome::kChromeUISyncConfirmationURL), content::Referrer(), + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); + + NSView* webview = web_contents->GetNativeView(); + [webview setFrameSize:NSMakeSize(kSyncConfirmationDialogWidth, + kSyncConfirmationDialogHeight)]; + + return web_contents; +} + +void SigninViewControllerDelegateMac::ShowBackArrow() { + gfx::ImageSkia image = gfx::CreateVectorIcon(gfx::VectorIconId::NAVIGATE_BACK, + 16, SK_ColorWHITE); + NSImage* converted_image = NSImageFromImageSkia(image); + [back_button_ setImage:converted_image]; + [back_button_ setNeedsDisplay:YES]; +} + +void SigninViewControllerDelegateMac::ShowCloseButton() { + gfx::ImageSkia image = gfx::CreateVectorIcon(gfx::VectorIconId::NAVIGATE_STOP, + 16, SK_ColorWHITE); + NSImage* converted_image = NSImageFromImageSkia(image); + [back_button_ setImage:converted_image]; + [back_button_ setNeedsDisplay:YES]; +} + +void SigninViewControllerDelegateMac::PerformClose() { + constrained_window_->CloseWebContentsModalDialog(); +} + +void SigninViewControllerDelegateMac::AddNavigationButton() { + NSRect button_frame = NSMakeRect(16, kFixedGaiaViewHeight - 32, 16, 16); + back_button_.reset([[NSButton alloc] initWithFrame:button_frame]); + [back_button_ setImagePosition:NSImageOnly]; + [back_button_ setBordered:NO]; + [back_button_ setButtonType:NSMomentaryChangeButton]; + ShowCloseButton(); + + handler_.reset([[NavigationButtonClickedHandler alloc] + initWithModalSigninDelegate:this]); + [back_button_ setTarget:handler_]; + [back_button_ setAction:@selector(buttonClicked:)]; + + // NSView clipping/drawing/invalidating is undefined between overlapping + // views if one is layer-backed and the other is not. Since we want to + // overlap an NSView on top of what is ultimately a + // RenderWidgetHostViewCocoa, which itself is layer-backed, we must request + // that our back button be layer-backed as well. + [back_button_ setWantsLayer:YES]; + + [host_view_ addSubview:back_button_]; +} + +// static +SigninViewControllerDelegate* +SigninViewControllerDelegate::CreateModalSigninDelegate( + SigninViewController* signin_view_controller, + profiles::BubbleViewMode mode, + Browser* browser, + signin_metrics::AccessPoint access_point) { + return SigninViewControllerDelegateMac:: + CreateModalSigninDelegateWithNavigation( + signin_view_controller, + SigninViewControllerDelegateMac::CreateGaiaWebContents( + nullptr, mode, browser->profile(), access_point), + browser->tab_strip_model()->GetActiveWebContents(), + NSMakeRect(0, 0, kFixedGaiaViewWidth, kFixedGaiaViewHeight)); +} + +// static +SigninViewControllerDelegate* +SigninViewControllerDelegate::CreateSyncConfirmationDelegate( + SigninViewController* signin_view_controller, + Browser* browser) { + return new SigninViewControllerDelegateMac( + signin_view_controller, + SigninViewControllerDelegateMac::CreateSyncConfirmationWebContents( + browser->profile()), + browser->tab_strip_model()->GetActiveWebContents(), + NSMakeRect(0, 0, kSyncConfirmationDialogWidth, + kSyncConfirmationDialogHeight)); +} diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc new file mode 100644 index 0000000..07c92d4 --- /dev/null +++ b/chrome/browser/ui/signin_view_controller.cc @@ -0,0 +1,56 @@ +// Copyright 2016 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/ui/signin_view_controller.h" + +#include "chrome/browser/ui/signin_view_controller_delegate.h" +#include "components/signin/core/common/profile_management_switches.h" + +SigninViewController::SigninViewController() + : signin_view_controller_delegate_(nullptr) {} + +SigninViewController::~SigninViewController() { + CloseModalSignin(); +} + +void SigninViewController::ShowModalSignin( + profiles::BubbleViewMode mode, + Browser* browser, + signin_metrics::AccessPoint access_point) { + CloseModalSignin(); + // The delegate will delete itself on request of the UI code when the widget + // is closed. + signin_view_controller_delegate_ = + SigninViewControllerDelegate::CreateModalSigninDelegate( + this, mode, browser, access_point); +} + +void SigninViewController::ShowModalSyncConfirmationDialog(Browser* browser) { + CloseModalSignin(); + // The delegate will delete itself on request of the UI code when the widget + // is closed. + signin_view_controller_delegate_ = + SigninViewControllerDelegate::CreateSyncConfirmationDelegate(this, + browser); +} + +void SigninViewController::CloseModalSignin() { + if (signin_view_controller_delegate_) + signin_view_controller_delegate_->CloseModalSignin(); + + DCHECK(!signin_view_controller_delegate_); +} + +void SigninViewController::ResetModalSigninDelegate() { + signin_view_controller_delegate_ = nullptr; +} + +// static +bool SigninViewController::ShouldShowModalSigninForMode( + profiles::BubbleViewMode mode) { + return switches::UsePasswordSeparatedSigninFlow() && + (mode == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN || + mode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || + mode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH); +} diff --git a/chrome/browser/ui/views/profiles/signin_view_controller.h b/chrome/browser/ui/signin_view_controller.h index 0372acc..8ffafda 100644 --- a/chrome/browser/ui/views/profiles/signin_view_controller.h +++ b/chrome/browser/ui/signin_view_controller.h @@ -1,49 +1,31 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2016 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. -#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_H_ -#define CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_H_ +#ifndef CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_H_ +#define CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_H_ #include "base/macros.h" -#include "base/observer_list.h" #include "chrome/browser/ui/profile_chooser_constants.h" class Browser; -class ModalSigninDelegate; +class SigninViewControllerDelegate; class Profile; -namespace content { -class WebContentsDelegate; -} - namespace signin_metrics { enum class AccessPoint; } -namespace views { -class WebView; -} - +// Class responsible for showing and hiding the Signin and Sync Confirmation +// tab-modal dialogs. class SigninViewController { public: SigninViewController(); - ~SigninViewController(); + virtual ~SigninViewController(); // Returns true if the signin flow should be shown as tab-modal for |mode|. static bool ShouldShowModalSigninForMode(profiles::BubbleViewMode mode); - // Creates the web view that contains the signin flow in |mode| using - // |profile| as the web content's profile, then sets |delegate| as the created - // web content's delegate. - static views::WebView* CreateGaiaWebView( - content::WebContentsDelegate* delegate, - profiles::BubbleViewMode mode, - Profile* profile, - signin_metrics::AccessPoint access_point); - - static views::WebView* CreateSyncConfirmationWebView(Profile* profile); - // Shows the signin flow as a tab modal dialog attached to |browser|'s active // web contents. // |access_point| indicates the access point used to open the Gaia sign in @@ -51,21 +33,20 @@ class SigninViewController { void ShowModalSignin(profiles::BubbleViewMode mode, Browser* browser, signin_metrics::AccessPoint access_point); - void ShowModalSyncConfirmationDialog(Browser* browser); // Closes the tab-modal signin flow previously shown using this // SigninViewController, if one exists. Does nothing otherwise. void CloseModalSignin(); - // Notifies this object that it's |modal_signin_delegate_| member has become - // invalid. + // Notifies this object that it's |signin_view_controller_delegate_| + // member has become invalid. void ResetModalSigninDelegate(); private: - ModalSigninDelegate* modal_signin_delegate_; + SigninViewControllerDelegate* signin_view_controller_delegate_; DISALLOW_COPY_AND_ASSIGN(SigninViewController); }; -#endif // CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_H_ +#endif // CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_H_ diff --git a/chrome/browser/ui/signin_view_controller_delegate.cc b/chrome/browser/ui/signin_view_controller_delegate.cc new file mode 100644 index 0000000..d03c46d --- /dev/null +++ b/chrome/browser/ui/signin_view_controller_delegate.cc @@ -0,0 +1,65 @@ +// Copyright 2016 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/ui/signin_view_controller_delegate.h" + +#include "chrome/browser/ui/signin_view_controller.h" +#include "chrome/browser/ui/webui/signin/get_auth_frame.h" +#include "content/public/browser/web_contents.h" + +namespace { + +content::WebContents* GetAuthFrameWebContents( + content::WebContents* web_ui_web_contents) { + return signin::GetAuthFrameWebContents(web_ui_web_contents, "signin-frame"); +} + +} // namespace + +SigninViewControllerDelegate::SigninViewControllerDelegate( + SigninViewController* signin_view_controller, + content::WebContents* web_contents) + : signin_view_controller_(signin_view_controller) { + web_contents->SetDelegate(this); +} + +SigninViewControllerDelegate::~SigninViewControllerDelegate() {} + +void SigninViewControllerDelegate::CloseModalSignin() { + ResetSigninViewControllerDelegate(); + PerformClose(); +} + +void SigninViewControllerDelegate::ResetSigninViewControllerDelegate() { + if (signin_view_controller_) { + signin_view_controller_->ResetModalSigninDelegate(); + signin_view_controller_ = nullptr; + } +} + +void SigninViewControllerDelegate::NavigationButtonClicked( + content::WebContents* web_contents) { + if (CanGoBack(web_contents)) { + auto auth_web_contents = GetAuthFrameWebContents(web_contents); + auth_web_contents->GetController().GoBack(); + } else { + CloseModalSignin(); + } +} + +bool SigninViewControllerDelegate::CanGoBack( + content::WebContents* web_ui_web_contents) const { + auto auth_web_contents = GetAuthFrameWebContents(web_ui_web_contents); + return auth_web_contents && auth_web_contents->GetController().CanGoBack(); +} + +// content::WebContentsDelegate +void SigninViewControllerDelegate::LoadingStateChanged( + content::WebContents* source, + bool to_different_document) { + if (CanGoBack(source)) + ShowBackArrow(); + else + ShowCloseButton(); +} diff --git a/chrome/browser/ui/signin_view_controller_delegate.h b/chrome/browser/ui/signin_view_controller_delegate.h new file mode 100644 index 0000000..55c3f36 --- /dev/null +++ b/chrome/browser/ui/signin_view_controller_delegate.h @@ -0,0 +1,74 @@ +// Copyright 2016 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. + +#ifndef CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_DELEGATE_H_ +#define CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_DELEGATE_H_ + +#include "chrome/browser/ui/profile_chooser_constants.h" +#include "content/public/browser/web_contents_delegate.h" + +class Browser; +class ModalSigninDelegate; +class SigninViewController; + +namespace signin_metrics { +enum class AccessPoint; +} + +// Abstract base class to the platform-specific managers of the Signin and Sync +// confirmation tab-modal dialogs. This and its platform-specific +// implementations are responsible for actually creating and owning the dialogs, +// as well as managing the navigation inside them. +// Subclasses are responsible for deleting themselves when the window they're +// managing closes. +class SigninViewControllerDelegate : public content::WebContentsDelegate { + public: + static SigninViewControllerDelegate* CreateModalSigninDelegate( + SigninViewController* signin_view_controller, + profiles::BubbleViewMode mode, + Browser* browser, + signin_metrics::AccessPoint access_point); + + static SigninViewControllerDelegate* CreateSyncConfirmationDelegate( + SigninViewController* signin_view_controller, + Browser* browser); + + void CloseModalSignin(); + void NavigationButtonClicked(content::WebContents* web_contents); + + protected: + SigninViewControllerDelegate(SigninViewController* signin_view_controller, + content::WebContents* web_contents); + ~SigninViewControllerDelegate() override; + + // Notifies the SigninViewController that this instance is being deleted. + void ResetSigninViewControllerDelegate(); + + // content::WebContentsDelegate + void LoadingStateChanged(content::WebContents* source, + bool to_different_document) override; + + // This will be called by this base class when the navigation state of the + // sign in page allows for backwards navigation. It should display a "back" + // navigation button to the user. + virtual void ShowBackArrow() = 0; + + // This will be called by this base class when the navigation state of the + // sign in page doesn't allow for backwards navigation. It should display a + // "close" button to the user. + virtual void ShowCloseButton() = 0; + + // This will be called by this base class when the tab-modal window must be + // closed. This should close the platform-specific window that is currently + // showing the sign in flow or the sync confirmation dialog. + virtual void PerformClose() = 0; + + private: + bool CanGoBack(content::WebContents* web_ui_web_contents) const; + + SigninViewController* signin_view_controller_; // Not owned. + DISALLOW_COPY_AND_ASSIGN(SigninViewControllerDelegate); +}; + +#endif // CHROME_BROWSER_UI_SIGNIN_VIEW_CONTROLLER_DELEGATE_H_ diff --git a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc index 4061c1c..d81c05e 100644 --- a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc +++ b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc @@ -533,7 +533,7 @@ void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble( void OneClickSigninSyncStarter::DisplayModalSyncConfirmationWindow() { browser_ = EnsureBrowser(browser_, profile_, desktop_type_); - browser_->window()->ShowModalSyncConfirmationWindow(); + browser_->ShowModalSyncConfirmationWindow(); } // static diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index a9d0508..f2b7401 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -2564,7 +2564,7 @@ void BrowserView::ShowAvatarBubbleFromAvatarButton( profiles::BubbleViewModeFromAvatarBubbleMode(mode, &bubble_view_mode, &tutorial_mode); if (SigninViewController::ShouldShowModalSigninForMode(bubble_view_mode)) { - ShowModalSigninWindow(mode, access_point); + browser_->ShowModalSigninWindow(bubble_view_mode, access_point); } else { ProfileChooserView::ShowBubble( bubble_view_mode, tutorial_mode, manage_accounts_params, access_point, @@ -2577,25 +2577,6 @@ void BrowserView::ShowAvatarBubbleFromAvatarButton( #endif } -void BrowserView::CloseModalSigninWindow() { - signin_view_controller_.CloseModalSignin(); -} - -void BrowserView::ShowModalSigninWindow( - AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) { - profiles::BubbleViewMode bubble_view_mode; - profiles::TutorialMode tutorial_mode; - profiles::BubbleViewModeFromAvatarBubbleMode(mode, &bubble_view_mode, - &tutorial_mode); - signin_view_controller_.ShowModalSignin(bubble_view_mode, browser(), - access_point); -} - -void BrowserView::ShowModalSyncConfirmationWindow() { - signin_view_controller_.ShowModalSyncConfirmationDialog(browser()); -} - int BrowserView::GetRenderViewHeightInsetWithDetachedBookmarkBar() { if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED || !bookmark_bar_view_ || !bookmark_bar_view_->IsDetached()) { diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index 5d4341a..0580950 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h @@ -20,6 +20,7 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" #include "chrome/browser/ui/infobar_container_delegate.h" +#include "chrome/browser/ui/signin_view_controller.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "chrome/browser/ui/views/exclusive_access_bubble_views_context.h" #include "chrome/browser/ui/views/frame/browser_frame.h" @@ -27,7 +28,6 @@ #include "chrome/browser/ui/views/frame/immersive_mode_controller.h" #include "chrome/browser/ui/views/frame/web_contents_close_handler.h" #include "chrome/browser/ui/views/load_complete_listener.h" -#include "chrome/browser/ui/views/profiles/signin_view_controller.h" #include "components/omnibox/browser/omnibox_popup_model_observer.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/models/simple_menu_model.h" @@ -365,10 +365,6 @@ class BrowserView : public BrowserWindow, AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) override; - void ShowModalSigninWindow(AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) override; - void CloseModalSigninWindow() override; - void ShowModalSyncConfirmationWindow() override; int GetRenderViewHeightInsetWithDetachedBookmarkBar() override; void ExecuteExtensionCommand(const extensions::Extension* extension, const extensions::Command& command) override; @@ -719,8 +715,6 @@ class BrowserView : public BrowserWindow, scoped_ptr<WebContentsCloseHandler> web_contents_close_handler_; - SigninViewController signin_view_controller_; - mutable base::WeakPtrFactory<BrowserView> activate_modal_dialog_factory_; DISALLOW_COPY_AND_ASSIGN(BrowserView); diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc index 8f4c5a3..8149ce9 100644 --- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc +++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc @@ -28,7 +28,7 @@ #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/singleton_tabs.h" #include "chrome/browser/ui/user_manager.h" -#include "chrome/browser/ui/views/profiles/signin_view_controller.h" +#include "chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h" #include "chrome/browser/ui/views/profiles/user_manager_view.h" #include "chrome/browser/ui/webui/signin/login_ui_service.h" #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" @@ -846,17 +846,7 @@ void ProfileChooserView::ShowView(profiles::BubbleViewMode view_to_display, void ProfileChooserView::ShowViewFromMode(profiles::BubbleViewMode mode) { if (SigninViewController::ShouldShowModalSigninForMode(mode)) { - BrowserWindow::AvatarBubbleMode converted_mode = - BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT; - if (mode == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN) { - converted_mode = BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN; - } else if (mode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT) { - converted_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT; - } else if (mode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) { - converted_mode = BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH; - } - - browser_->window()->ShowModalSigninWindow(converted_mode, access_point_); + browser_->ShowModalSigninWindow(mode, access_point_); } else { ShowView(mode, avatar_menu_.get()); } @@ -1651,8 +1641,9 @@ void ProfileChooserView::CreateAccountButton(views::GridLayout* layout, views::View* ProfileChooserView::CreateGaiaSigninView( views::View** signin_content_view) { - views::WebView* web_view = SigninViewController::CreateGaiaWebView( - this, view_mode_, browser_->profile(), access_point_); + views::WebView* web_view = + SigninViewControllerDelegateViews::CreateGaiaWebView( + this, view_mode_, browser_->profile(), access_point_); int message_id; switch (view_mode_) { diff --git a/chrome/browser/ui/views/profiles/signin_view_controller.cc b/chrome/browser/ui/views/profiles/signin_view_controller.cc deleted file mode 100644 index 850e752..0000000 --- a/chrome/browser/ui/views/profiles/signin_view_controller.cc +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2015 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/ui/views/profiles/signin_view_controller.h" - -#include "base/macros.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/signin/signin_error_controller_factory.h" -#include "chrome/browser/signin/signin_promo.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/webui/signin/get_auth_frame.h" -#include "chrome/common/url_constants.h" -#include "components/constrained_window/constrained_window_views.h" -#include "components/signin/core/browser/signin_error_controller.h" -#include "components/signin/core/common/profile_management_switches.h" -#include "content/public/browser/navigation_controller.h" -#include "content/public/browser/render_widget_host_view.h" -#include "content/public/browser/web_contents.h" -#include "grit/theme_resources.h" -#include "ui/gfx/paint_vector_icon.h" -#include "ui/gfx/vector_icons.h" -#include "ui/views/controls/button/image_button.h" -#include "ui/views/controls/webview/webview.h" -#include "ui/views/layout/fill_layout.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_delegate.h" -#include "ui/views/window/dialog_delegate.h" - -const int kPasswordCombinedFixedGaiaViewHeight = 440; -const int kPasswordCombinedFixedGaiaViewWidth = 360; -const int kFixedGaiaViewHeight = 512; -const int kFixedGaiaViewWidth = 448; -const int kNavigationButtonSize = 16; -const int kNavigationButtonOffset = 16; -const int kSyncConfirmationDialogWidth = 448; -const int kSyncConfirmationDialogHeight = 351; - -// View that contains the signin web contents and the back/close overlay button. -class HostView : public views::View { - public: - HostView(views::View* contents, views::ButtonListener* button_listener) - : contents_(contents), - back_button_(new views::ImageButton(button_listener)) { - back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, - views::ImageButton::ALIGN_MIDDLE); - back_button_->SetFocusable(true); - ShowCloseButton(); - AddChildView(contents_); - SetLayoutManager(new views::FillLayout); - } - - gfx::Size GetPreferredSize() const override { - return contents_->GetPreferredSize(); - } - - void ShowCloseButton() { - gfx::ImageSkia image = gfx::CreateVectorIcon( - gfx::VectorIconId::NAVIGATE_STOP, kNavigationButtonSize, SK_ColorWHITE); - back_button_->SetImage(views::Button::STATE_NORMAL, &image); - back_button_->SchedulePaint(); - } - - void ShowBackArrow() { - gfx::ImageSkia image = gfx::CreateVectorIcon( - gfx::VectorIconId::NAVIGATE_BACK, kNavigationButtonSize, SK_ColorWHITE); - back_button_->SetImage(views::Button::STATE_NORMAL, &image); - back_button_->SchedulePaint(); - } - -private: - // views::View: - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override { - if (back_button_widget_ || !GetWidget()) - return; - - views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); - params.parent = GetWidget()->GetNativeView(); - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - - back_button_widget_.reset(new views::Widget); - back_button_widget_->Init(params); - back_button_widget_->SetContentsView(back_button_); - - gfx::Rect bounds(back_button_->GetPreferredSize()); - back_button_->SetBoundsRect(bounds); - bounds.Offset(kNavigationButtonOffset, kNavigationButtonOffset); - back_button_widget_->SetBounds(bounds); - } - - views::View* contents_; - views::ImageButton* back_button_; - scoped_ptr<views::Widget> back_button_widget_; - DISALLOW_COPY_AND_ASSIGN(HostView); -}; - -class ModalSigninDelegate : public views::DialogDelegateView, - public views::ButtonListener, - public content::WebContentsDelegate { - public: - ModalSigninDelegate(SigninViewController* signin_view_controller, - views::WebView* content_view, - Browser* browser) - : content_view_(content_view), - host_view_(new HostView(content_view_, this)), - signin_view_controller_(signin_view_controller) { - content_view->GetWebContents()->SetDelegate(this); - modal_signin_widget_ = constrained_window::ShowWebModalDialogViews( - this, browser->tab_strip_model()->GetActiveWebContents()); - } - - void CloseModalSignin() { - ResetSigninViewControllerDelegate(); - modal_signin_widget_->Close(); - } - - void ResetSigninViewControllerDelegate() { - if (signin_view_controller_) { - signin_view_controller_->ResetModalSigninDelegate(); - signin_view_controller_ = nullptr; - } - } - - // views::DialogDelegateView: - views::View* GetContentsView() override { - return host_view_; - } - - views::Widget* GetWidget() override { - return host_view_->GetWidget(); - } - - const views::Widget* GetWidget() const override { - return host_view_->GetWidget(); - } - - void DeleteDelegate() override { - ResetSigninViewControllerDelegate(); - delete this; - } - - ui::ModalType GetModalType() const override { - return ui::MODAL_TYPE_CHILD; - } - - bool ShouldShowCloseButton() const override { - return false; - } - - int GetDialogButtons() const override { - return ui::DIALOG_BUTTON_NONE; - } - - // views::ButtonListener: - void ButtonPressed(views::Button* sender, const ui::Event& event) override { - auto auth_web_contents = signin::GetAuthFrameWebContents( - content_view_->GetWebContents(), "signin-frame"); - if (auth_web_contents) { - if (auth_web_contents->GetController().CanGoBack()) - auth_web_contents->GetController().GoBack(); - else - CloseModalSignin(); - } - } - - private: - // content::WebContentsDelegate - void LoadingStateChanged( - content::WebContents* source, bool to_different_document) override { - auto auth_web_contents = signin::GetAuthFrameWebContents( - content_view_->GetWebContents(), "signin-frame"); - if (auth_web_contents) { - if (auth_web_contents->GetController().CanGoBack()) - host_view_->ShowBackArrow(); - else - host_view_->ShowCloseButton(); - } - } - - views::WebView* content_view_; - HostView* host_view_; - SigninViewController* signin_view_controller_; // Not owned. - views::Widget* modal_signin_widget_; // Not owned. - - DISALLOW_COPY_AND_ASSIGN(ModalSigninDelegate); -}; - -SigninViewController::SigninViewController() - : modal_signin_delegate_(nullptr) {} - -SigninViewController::~SigninViewController() { - CloseModalSignin(); -} - -// static -views::WebView* SigninViewController::CreateGaiaWebView( - content::WebContentsDelegate* delegate, - profiles::BubbleViewMode mode, - Profile* profile, - signin_metrics::AccessPoint access_point) { - GURL url = - signin::GetSigninURLFromBubbleViewMode(profile, mode, access_point); - - // Adds Gaia signin webview. - const gfx::Size pref_size = switches::UsePasswordSeparatedSigninFlow() - ? gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight) - : gfx::Size(kPasswordCombinedFixedGaiaViewWidth, - kPasswordCombinedFixedGaiaViewHeight); - views::WebView* web_view = new views::WebView(profile); - web_view->LoadInitialURL(url); - - if (delegate) - web_view->GetWebContents()->SetDelegate(delegate); - - web_view->SetPreferredSize(pref_size); - content::RenderWidgetHostView* rwhv = - web_view->GetWebContents()->GetRenderWidgetHostView(); - if (rwhv) - rwhv->SetBackgroundColor(profiles::kAvatarBubbleGaiaBackgroundColor); - - return web_view; -} - -views::WebView* SigninViewController::CreateSyncConfirmationWebView( - Profile* profile) { - views::WebView* web_view = new views::WebView(profile); - web_view->LoadInitialURL(GURL(chrome::kChromeUISyncConfirmationURL)); - web_view->SetPreferredSize( - gfx::Size(kSyncConfirmationDialogWidth, kSyncConfirmationDialogHeight)); - - return web_view; -} - -void SigninViewController::ShowModalSignin( - profiles::BubbleViewMode mode, - Browser* browser, - signin_metrics::AccessPoint access_point) { - CloseModalSignin(); - // The delegate will delete itself on request of the views code when the - // widget is closed. - modal_signin_delegate_ = new ModalSigninDelegate( - this, CreateGaiaWebView(nullptr, mode, browser->profile(), access_point), - browser); -} - -void SigninViewController::CloseModalSignin() { - if (modal_signin_delegate_) - modal_signin_delegate_->CloseModalSignin(); - DCHECK(!modal_signin_delegate_); -} - -void SigninViewController::ResetModalSigninDelegate() { - modal_signin_delegate_ = nullptr; -} - -void SigninViewController::ShowModalSyncConfirmationDialog(Browser* browser) { - CloseModalSignin(); - modal_signin_delegate_ = new ModalSigninDelegate( - this, CreateSyncConfirmationWebView(browser->profile()), browser); -} - -// static -bool SigninViewController::ShouldShowModalSigninForMode( - profiles::BubbleViewMode mode) { - return switches::UsePasswordSeparatedSigninFlow() && - (mode == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN || - mode == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT || - mode == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH); -} diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc new file mode 100644 index 0000000..4d6dff5 --- /dev/null +++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc @@ -0,0 +1,221 @@ +// Copyright 2016 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/ui/views/profiles/signin_view_controller_delegate_views.h" + +#include "base/macros.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_avatar_icon_util.h" +#include "chrome/browser/signin/signin_promo.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/url_constants.h" +#include "components/constrained_window/constrained_window_views.h" +#include "components/signin/core/common/profile_management_switches.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icons.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +const int kPasswordCombinedFixedGaiaViewHeight = 440; +const int kPasswordCombinedFixedGaiaViewWidth = 360; +const int kFixedGaiaViewHeight = 512; +const int kFixedGaiaViewWidth = 448; +const int kNavigationButtonSize = 16; +const int kNavigationButtonOffset = 16; +const int kSyncConfirmationDialogWidth = 448; +const int kSyncConfirmationDialogHeight = 351; + +// View that contains the signin web contents and the back/close overlay button. +class HostView : public views::View { + public: + HostView(views::View* contents, views::ButtonListener* button_listener) + : contents_(contents), + back_button_(new views::ImageButton(button_listener)) { + back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, + views::ImageButton::ALIGN_MIDDLE); + back_button_->SetFocusable(true); + ShowCloseButton(); + AddChildView(contents_); + SetLayoutManager(new views::FillLayout); + } + + gfx::Size GetPreferredSize() const override { + return contents_->GetPreferredSize(); + } + + void ShowCloseButton() { + gfx::ImageSkia image = gfx::CreateVectorIcon( + gfx::VectorIconId::NAVIGATE_STOP, kNavigationButtonSize, SK_ColorWHITE); + back_button_->SetImage(views::Button::STATE_NORMAL, &image); + back_button_->SchedulePaint(); + } + + void ShowBackArrow() { + gfx::ImageSkia image = gfx::CreateVectorIcon( + gfx::VectorIconId::NAVIGATE_BACK, kNavigationButtonSize, SK_ColorWHITE); + back_button_->SetImage(views::Button::STATE_NORMAL, &image); + back_button_->SchedulePaint(); + } + + private: + // views::View: + void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) override { + if (back_button_widget_ || !GetWidget()) + return; + + views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); + params.parent = GetWidget()->GetNativeView(); + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + + back_button_widget_.reset(new views::Widget); + back_button_widget_->Init(params); + back_button_widget_->SetContentsView(back_button_); + + gfx::Rect bounds(back_button_->GetPreferredSize()); + back_button_->SetBoundsRect(bounds); + bounds.Offset(kNavigationButtonOffset, kNavigationButtonOffset); + back_button_widget_->SetBounds(bounds); + } + + views::View* contents_; + views::ImageButton* back_button_; + scoped_ptr<views::Widget> back_button_widget_; + DISALLOW_COPY_AND_ASSIGN(HostView); +}; + +SigninViewControllerDelegateViews::SigninViewControllerDelegateViews( + SigninViewController* signin_view_controller, + views::WebView* content_view, + Browser* browser) + : SigninViewControllerDelegate(signin_view_controller, + content_view->GetWebContents()), + content_view_(content_view), + host_view_(new HostView(content_view_, this)), + modal_signin_widget_(nullptr) { + modal_signin_widget_ = constrained_window::ShowWebModalDialogViews( + this, browser->tab_strip_model()->GetActiveWebContents()); +} + +SigninViewControllerDelegateViews::~SigninViewControllerDelegateViews() {} + +// views::DialogDelegateView: +views::View* SigninViewControllerDelegateViews::GetContentsView() { + return host_view_; +} + +views::Widget* SigninViewControllerDelegateViews::GetWidget() { + return host_view_->GetWidget(); +} + +const views::Widget* SigninViewControllerDelegateViews::GetWidget() const { + return host_view_->GetWidget(); +} + +void SigninViewControllerDelegateViews::DeleteDelegate() { + ResetSigninViewControllerDelegate(); + delete this; +} + +ui::ModalType SigninViewControllerDelegateViews::GetModalType() const { + return ui::MODAL_TYPE_CHILD; +} + +bool SigninViewControllerDelegateViews::ShouldShowCloseButton() const { + return false; +} + +int SigninViewControllerDelegateViews::GetDialogButtons() const { + return ui::DIALOG_BUTTON_NONE; +} + +// views::ButtonListener: +void SigninViewControllerDelegateViews::ButtonPressed(views::Button* sender, + const ui::Event& event) { + NavigationButtonClicked(content_view_->GetWebContents()); +} + +void SigninViewControllerDelegateViews::ShowBackArrow() { + host_view_->ShowBackArrow(); +} + +void SigninViewControllerDelegateViews::ShowCloseButton() { + host_view_->ShowCloseButton(); +} + +void SigninViewControllerDelegateViews::PerformClose() { + modal_signin_widget_->Close(); +} + +// static +views::WebView* SigninViewControllerDelegateViews::CreateGaiaWebView( + content::WebContentsDelegate* delegate, + profiles::BubbleViewMode mode, + Profile* profile, + signin_metrics::AccessPoint access_point) { + GURL url = + signin::GetSigninURLFromBubbleViewMode(profile, mode, access_point); + + // Adds Gaia signin webview. + const gfx::Size pref_size = + switches::UsePasswordSeparatedSigninFlow() + ? gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight) + : gfx::Size(kPasswordCombinedFixedGaiaViewWidth, + kPasswordCombinedFixedGaiaViewHeight); + views::WebView* web_view = new views::WebView(profile); + web_view->LoadInitialURL(url); + + if (delegate) + web_view->GetWebContents()->SetDelegate(delegate); + + web_view->SetPreferredSize(pref_size); + content::RenderWidgetHostView* rwhv = + web_view->GetWebContents()->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetBackgroundColor(profiles::kAvatarBubbleGaiaBackgroundColor); + + return web_view; +} + +views::WebView* +SigninViewControllerDelegateViews::CreateSyncConfirmationWebView( + Profile* profile) { + views::WebView* web_view = new views::WebView(profile); + web_view->LoadInitialURL(GURL(chrome::kChromeUISyncConfirmationURL)); + web_view->SetPreferredSize( + gfx::Size(kSyncConfirmationDialogWidth, kSyncConfirmationDialogHeight)); + + return web_view; +} + +SigninViewControllerDelegate* +SigninViewControllerDelegate::CreateModalSigninDelegate( + SigninViewController* signin_view_controller, + profiles::BubbleViewMode mode, + Browser* browser, + signin_metrics::AccessPoint access_point) { + return new SigninViewControllerDelegateViews( + signin_view_controller, + SigninViewControllerDelegateViews::CreateGaiaWebView( + nullptr, mode, browser->profile(), access_point), + browser); +} + +SigninViewControllerDelegate* +SigninViewControllerDelegate::CreateSyncConfirmationDelegate( + SigninViewController* signin_view_controller, + Browser* browser) { + return new SigninViewControllerDelegateViews( + signin_view_controller, + SigninViewControllerDelegateViews::CreateSyncConfirmationWebView( + browser->profile()), + browser); +} diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h new file mode 100644 index 0000000..06a2e47 --- /dev/null +++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h @@ -0,0 +1,80 @@ +// Copyright 2016 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. + +#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_VIEWS_H_ +#define CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_VIEWS_H_ + +#include "base/macros.h" +#include "chrome/browser/ui/profile_chooser_constants.h" +#include "chrome/browser/ui/signin_view_controller_delegate.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/window/dialog_delegate.h" + +class Browser; +class HostView; +class Profile; + +namespace content { +class WebContentsDelegate; +} + +namespace signin_metrics { +enum class AccessPoint; +} + +namespace views { +class WebView; +} + +// Views implementation of SigninViewControllerDelegate. It's responsible for +// managing the Signin and Sync Confirmation tab-modal dialogs. +// Instances of this class delete themselves when the window they're managing +// closes (in the DeleteDelegate callback). +class SigninViewControllerDelegateViews : public views::DialogDelegateView, + public views::ButtonListener, + public SigninViewControllerDelegate { + public: + SigninViewControllerDelegateViews( + SigninViewController* signin_view_controller, + views::WebView* content_view, + Browser* browser); + + // Creates the web view that contains the signin flow in |mode| using + // |profile| as the web content's profile, then sets |delegate| as the created + // web content's delegate. + static views::WebView* CreateGaiaWebView( + content::WebContentsDelegate* delegate, + profiles::BubbleViewMode mode, + Profile* profile, + signin_metrics::AccessPoint access_point); + + static views::WebView* CreateSyncConfirmationWebView(Profile* profile); + + // views::DialogDelegateView: + views::View* GetContentsView() override; + views::Widget* GetWidget() override; + const views::Widget* GetWidget() const override; + void DeleteDelegate() override; + ui::ModalType GetModalType() const override; + bool ShouldShowCloseButton() const override; + int GetDialogButtons() const override; + + // views::ButtonListener: + void ButtonPressed(views::Button* sender, const ui::Event& event) override; + + private: + void ShowBackArrow() override; + void ShowCloseButton() override; + void PerformClose() override; + + ~SigninViewControllerDelegateViews() override; + + views::WebView* content_view_; + HostView* host_view_; + views::Widget* modal_signin_widget_; // Not owned. + + DISALLOW_COPY_AND_ASSIGN(SigninViewControllerDelegateViews); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_PROFILES_SIGNIN_VIEW_CONTROLLER_DELEGATE_VIEWS_H_ diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc index c97de48..21cedd1 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc @@ -736,12 +736,6 @@ void InlineLoginHandlerImpl::FinishCompleteLogin( const FinishCompleteLoginParams& params, Profile* profile, Profile::CreateStatus status) { - if (params.handler && switches::UsePasswordSeparatedSigninFlow()) { - Browser* browser = params.handler->GetDesktopBrowser(); - if (browser) - browser->window()->CloseModalSigninWindow(); - } - // When doing a SAML sign in, this email check may result in a false // positive. This happens when the user types one email address in the // gaia sign in page, but signs in to a different account in the SAML sign in @@ -842,6 +836,12 @@ void InlineLoginHandlerImpl::FinishCompleteLogin( if (params.handler) params.handler-> web_ui()->CallJavascriptFunction("inline.login.closeDialog"); + + if (params.handler && switches::UsePasswordSeparatedSigninFlow()) { + Browser* browser = params.handler->GetDesktopBrowser(); + if (browser) + browser->CloseModalSigninWindow(); + } } void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg) { diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc index b5194fe..b02ca9e 100644 --- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc +++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc @@ -54,7 +54,7 @@ void SyncConfirmationHandler::HandleUndo(const base::ListValue* args) { SigninManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))->SignOut( signin_metrics::ABORT_SIGNIN, signin_metrics::SignoutDelete::IGNORE_METRIC); - browser->window()->CloseModalSigninWindow(); + browser->CloseModalSigninWindow(); } void SyncConfirmationHandler::HandleInitialized(const base::ListValue* args) { @@ -107,5 +107,5 @@ void SyncConfirmationHandler::CloseModalSigninWindow( Browser* browser = GetDesktopBrowser(); LoginUIServiceFactory::GetForProfile(browser->profile())-> SyncConfirmationUIClosed(results); - browser->window()->CloseModalSigninWindow(); + browser->CloseModalSigninWindow(); } diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 441e064..eb32403 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -1195,6 +1195,8 @@ 'browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm', 'browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.h', 'browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm', + 'browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.h', + 'browser/ui/cocoa/profiles/signin_view_controller_delegate_mac.mm', 'browser/ui/cocoa/profiles/user_manager_mac.h', 'browser/ui/cocoa/profiles/user_manager_mac.mm', 'browser/ui/cocoa/rect_path_utils.h', @@ -1701,6 +1703,10 @@ 'browser/ui/settings_window_manager.cc', 'browser/ui/settings_window_manager.h', 'browser/ui/settings_window_manager_observer.h', + 'browser/ui/signin_view_controller.cc', + 'browser/ui/signin_view_controller.h', + 'browser/ui/signin_view_controller_delegate.cc', + 'browser/ui/signin_view_controller_delegate.h', 'browser/ui/singleton_tabs.cc', 'browser/ui/singleton_tabs.h', 'browser/ui/startup/bad_flags_prompt.cc', @@ -2392,8 +2398,8 @@ 'browser/ui/views/profiles/avatar_menu_button.h', 'browser/ui/views/profiles/profile_chooser_view.cc', 'browser/ui/views/profiles/profile_chooser_view.h', - 'browser/ui/views/profiles/signin_view_controller.cc', - 'browser/ui/views/profiles/signin_view_controller.h', + 'browser/ui/views/profiles/signin_view_controller_delegate_views.cc', + 'browser/ui/views/profiles/signin_view_controller_delegate_views.h', 'browser/ui/views/profiles/user_manager_view.cc', 'browser/ui/views/profiles/user_manager_view.h', 'browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc', diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h index 869dc1e..c4261b2 100644 --- a/chrome/test/base/test_browser_window.h +++ b/chrome/test/base/test_browser_window.h @@ -139,11 +139,6 @@ class TestBrowserWindow : public BrowserWindow { AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) override {} - void ShowModalSigninWindow( - AvatarBubbleMode mode, - signin_metrics::AccessPoint access_point) override {} - void CloseModalSigninWindow() override {} - void ShowModalSyncConfirmationWindow() override {} int GetRenderViewHeightInsetWithDetachedBookmarkBar() override; void ExecuteExtensionCommand(const extensions::Extension* extension, const extensions::Command& command) override; |
