1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/one_click_signin_view_controller.h"
7 #include "base/callback_helpers.h"
8 #include "base/logging.h"
9 #include "base/mac/bundle_locations.h"
10 #import "chrome/browser/ui/chrome_style.h"
11 #include "chrome/common/url_constants.h"
12 #include "chrome/grit/chromium_strings.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/signin/core/browser/signin_metrics.h"
15 #include "content/public/browser/web_contents.h"
16 #include "grit/components_strings.h"
17 #include "skia/ext/skia_utils_mac.h"
18 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
19 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
20 #include "ui/base/l10n/l10n_util_mac.h"
24 // The margin between the top edge of the border and the error message text in
25 // the sign-in bubble, in the case of an error.
26 const CGFloat kTopErrorMessageMargin = 12;
28 // Shift the origin of |view|'s frame by the given amount in the
29 // positive y direction (up).
30 void ShiftOriginY(NSView* view, CGFloat amount) {
31 NSPoint origin = [view frame].origin;
33 [view setFrameOrigin:origin];
38 @interface OneClickSigninViewController ()
39 - (CGFloat)initializeInformativeTextView;
43 @implementation OneClickSigninViewController
46 - (id)initWithNibName:(NSString*)nibName
47 webContents:(content::WebContents*)webContents
48 syncCallback:(const BrowserWindow::StartSyncCallback&)syncCallback
49 closeCallback:(const base::Closure&)closeCallback
50 isSyncDialog:(BOOL)isSyncDialog
51 email:(const base::string16&)email
52 errorMessage:(NSString*)errorMessage {
53 if ((self = [super initWithNibName:nibName
54 bundle:base::mac::FrameworkBundle()])) {
55 webContents_ = webContents;
56 startSyncCallback_ = syncCallback;
57 closeCallback_ = closeCallback;
58 isSyncDialog_ = isSyncDialog;
59 clickedLearnMore_ = NO;
61 errorMessage_.reset([errorMessage retain]);
63 DCHECK(!startSyncCallback_.is_null());
68 - (void)viewWillClose {
69 // This is usually called after a click handler has initiated sync
70 // and has reset the callback. However, in the case that we are closing
71 // the window and nothing else has initiated the sync, we must do so here
72 if (isSyncDialog_ && !startSyncCallback_.is_null()) {
73 base::ResetAndReturn(&startSyncCallback_).Run(
74 OneClickSigninSyncStarter::UNDO_SYNC);
78 - (IBAction)ok:(id)sender {
80 signin_metrics::LogSigninConfirmHistogramValue(
82 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_OK :
83 signin_metrics::HISTOGRAM_CONFIRM_OK);
85 base::ResetAndReturn(&startSyncCallback_).Run(
86 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
91 - (IBAction)onClickUndo:(id)sender {
93 signin_metrics::LogSigninConfirmHistogramValue(
95 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO :
96 signin_metrics::HISTOGRAM_CONFIRM_UNDO);
98 base::ResetAndReturn(&startSyncCallback_).Run(
99 OneClickSigninSyncStarter::UNDO_SYNC);
104 - (IBAction)onClickAdvancedLink:(id)sender {
106 signin_metrics::LogSigninConfirmHistogramValue(
108 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED :
109 signin_metrics::HISTOGRAM_CONFIRM_ADVANCED);
111 base::ResetAndReturn(&startSyncCallback_).Run(
112 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
115 content::OpenURLParams params(GURL(chrome::kChromeUISettingsURL),
116 content::Referrer(), CURRENT_TAB,
117 ui::PAGE_TRANSITION_LINK, false);
118 webContents_->OpenURL(params);
123 - (IBAction)onClickClose:(id)sender {
125 signin_metrics::LogSigninConfirmHistogramValue(
127 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE :
128 signin_metrics::HISTOGRAM_CONFIRM_CLOSE);
130 base::ResetAndReturn(&startSyncCallback_).Run(
131 OneClickSigninSyncStarter::UNDO_SYNC);
136 - (void)awakeFromNib {
137 // Lay out the text controls from the bottom up.
138 CGFloat totalYOffset = 0.0;
140 if ([errorMessage_ length] == 0) {
142 [GTMUILocalizerAndLayoutTweaker sizeToFitView:advancedLink_].height;
143 [[advancedLink_ cell] setTextColor:
144 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())];
146 // Don't display the advanced link for the error bubble.
147 // To align the Learn More link with the OK button, we need to offset by
148 // the height of the Advanced link, plus the padding between it and the
149 // Learn More link above.
150 float advancedLinkHeightPlusPadding =
151 [informativePlaceholderTextField_ frame].origin.y -
152 [advancedLink_ frame].origin.y;
154 totalYOffset -= advancedLinkHeightPlusPadding;
155 [advancedLink_ removeFromSuperview];
158 if (informativePlaceholderTextField_) {
159 if (!isSyncDialog_ && ([errorMessage_ length] != 0)) {
160 // Move up the "Learn more" origin in error case to account for the
162 NSRect frame = [informativePlaceholderTextField_ frame];
163 frame = NSOffsetRect(frame, 0, NSHeight([titleTextField_ frame]));
164 [informativePlaceholderTextField_ setFrame:frame];
167 ShiftOriginY(informativePlaceholderTextField_, totalYOffset);
168 totalYOffset += [self initializeInformativeTextView];
171 ShiftOriginY(messageTextField_, totalYOffset);
173 [GTMUILocalizerAndLayoutTweaker
174 sizeToFitFixedWidthTextField:messageTextField_];
176 ShiftOriginY(titleTextField_, totalYOffset);
178 [GTMUILocalizerAndLayoutTweaker
179 sizeToFitFixedWidthTextField:titleTextField_];
181 NSSize delta = NSMakeSize(0.0, totalYOffset);
184 [messageTextField_ setStringValue:l10n_util::GetNSStringWithFixup(
185 IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW)];
186 } else if ([errorMessage_ length] != 0) {
187 [titleTextField_ setHidden:YES];
188 [messageTextField_ setStringValue:errorMessage_];
190 // Make the bubble less tall, as the title text will be hidden.
191 NSSize size = [[self view] frame].size;
192 size.height = size.height - NSHeight([titleTextField_ frame]);
193 [[self view] setFrameSize:size];
195 // Shift the message text up to where the title text used to be.
196 NSPoint origin = [titleTextField_ frame].origin;
197 [messageTextField_ setFrameOrigin:origin];
198 ShiftOriginY(messageTextField_, -kTopErrorMessageMargin);
200 // Use "OK" instead of "OK, got it" in the error case, and size the button
202 [closeButton_ setTitle:l10n_util::GetNSStringWithFixup(
204 [GTMUILocalizerAndLayoutTweaker sizeToFitView:[closeButton_ superview]];
207 // Resize bubble and window to hold the controls.
208 [GTMUILocalizerAndLayoutTweaker
209 resizeViewWithoutAutoResizingSubViews:[self view]
213 signin_metrics::LogSigninConfirmHistogramValue(
214 signin_metrics::HISTOGRAM_CONFIRM_SHOWN);
218 - (CGFloat)initializeInformativeTextView {
219 NSRect oldFrame = [informativePlaceholderTextField_ frame];
221 // Replace the placeholder NSTextField with the real label NSTextView. The
222 // former doesn't show links in a nice way, but the latter can't be added in
223 // a xib without a containing scroll view, so create the NSTextView
225 informativeTextView_.reset(
226 [[HyperlinkTextView alloc] initWithFrame:oldFrame]);
227 [informativeTextView_.get() setAutoresizingMask:
228 [informativePlaceholderTextField_ autoresizingMask]];
229 [informativeTextView_.get() setDelegate:self];
232 NSString* learnMoreText = l10n_util::GetNSStringWithFixup(IDS_LEARN_MORE);
233 NSString* messageText;
234 NSUInteger learnMoreOffset = 0;
236 ui::ResourceBundle::FontStyle fontStyle = isSyncDialog_ ?
237 chrome_style::kTextFontStyle : ui::ResourceBundle::SmallFont;
238 NSFont* font = ui::ResourceBundle::GetSharedInstance().GetFont(
239 fontStyle).GetNativeFont();
241 // The non-modal bubble already has a text content and only needs the
242 // Learn More link (in a smaller font).
244 messageText = l10n_util::GetNSStringFWithFixup(
245 IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW, email_);
246 learnMoreOffset = [messageText length];
247 messageText = [messageText stringByAppendingFormat:@" %@", learnMoreText];
249 messageText = learnMoreText;
253 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
254 [informativeTextView_ setMessage:messageText
256 messageColor:[NSColor blackColor]];
257 [informativeTextView_ addLinkRange:NSMakeRange(learnMoreOffset,
258 [learnMoreText length])
260 linkColor:linkColor];
262 // Make the "Advanced" link font as large as the "Learn More" link.
263 [[advancedLink_ cell] setFont:font];
264 [advancedLink_ sizeToFit];
267 [[informativePlaceholderTextField_ cell] setAttributedStringValue:
268 [informativeTextView_ attributedString]];
269 [GTMUILocalizerAndLayoutTweaker
270 sizeToFitFixedWidthTextField:informativePlaceholderTextField_];
271 NSRect newFrame = [informativePlaceholderTextField_ frame];
272 [informativeTextView_ setFrame:newFrame];
275 [[informativePlaceholderTextField_ superview]
276 replaceSubview:informativePlaceholderTextField_
277 with:informativeTextView_.get()];
278 informativePlaceholderTextField_ = nil; // Now released.
280 return NSHeight(newFrame) - NSHeight(oldFrame);
283 - (BOOL)textView:(NSTextView*)textView
284 clickedOnLink:(id)link
285 atIndex:(NSUInteger)charIndex {
286 if (isSyncDialog_ && !clickedLearnMore_) {
287 clickedLearnMore_ = YES;
289 signin_metrics::LogSigninConfirmHistogramValue(
290 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE);
292 WindowOpenDisposition location = isSyncDialog_ ?
293 NEW_WINDOW : NEW_FOREGROUND_TAB;
294 content::OpenURLParams params(GURL(chrome::kChromeSyncLearnMoreURL),
295 content::Referrer(), location,
296 ui::PAGE_TRANSITION_LINK, false);
297 webContents_->OpenURL(params);
302 base::ResetAndReturn(&closeCallback_).Run();