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/browser/ui/sync/one_click_signin_helper.h"
12 #include "chrome/common/url_constants.h"
13 #include "chrome/grit/chromium_strings.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "components/signin/core/browser/signin_metrics.h"
16 #include "content/public/browser/web_contents.h"
17 #include "grit/components_strings.h"
18 #include "skia/ext/skia_utils_mac.h"
19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
20 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
21 #include "ui/base/l10n/l10n_util_mac.h"
25 // The margin between the top edge of the border and the error message text in
26 // the sign-in bubble, in the case of an error.
27 const CGFloat kTopErrorMessageMargin = 12;
29 // Shift the origin of |view|'s frame by the given amount in the
30 // positive y direction (up).
31 void ShiftOriginY(NSView* view, CGFloat amount) {
32 NSPoint origin = [view frame].origin;
34 [view setFrameOrigin:origin];
39 @interface OneClickSigninViewController ()
40 - (CGFloat)initializeInformativeTextView;
44 @implementation OneClickSigninViewController
47 - (id)initWithNibName:(NSString*)nibName
48 webContents:(content::WebContents*)webContents
49 syncCallback:(const BrowserWindow::StartSyncCallback&)syncCallback
50 closeCallback:(const base::Closure&)closeCallback
51 isSyncDialog:(BOOL)isSyncDialog
52 email:(const base::string16&)email
53 errorMessage:(NSString*)errorMessage {
54 if ((self = [super initWithNibName:nibName
55 bundle:base::mac::FrameworkBundle()])) {
56 webContents_ = webContents;
57 startSyncCallback_ = syncCallback;
58 closeCallback_ = closeCallback;
59 isSyncDialog_ = isSyncDialog;
60 clickedLearnMore_ = NO;
62 errorMessage_.reset([errorMessage retain]);
64 DCHECK(!startSyncCallback_.is_null());
69 - (void)viewWillClose {
70 // This is usually called after a click handler has initiated sync
71 // and has reset the callback. However, in the case that we are closing
72 // the window and nothing else has initiated the sync, we must do so here
73 if (isSyncDialog_ && !startSyncCallback_.is_null()) {
74 base::ResetAndReturn(&startSyncCallback_).Run(
75 OneClickSigninSyncStarter::UNDO_SYNC);
79 - (IBAction)ok:(id)sender {
81 OneClickSigninHelper::LogConfirmHistogramValue(
83 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_OK :
84 signin_metrics::HISTOGRAM_CONFIRM_OK);
86 base::ResetAndReturn(&startSyncCallback_).Run(
87 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
92 - (IBAction)onClickUndo:(id)sender {
94 OneClickSigninHelper::LogConfirmHistogramValue(
96 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO :
97 signin_metrics::HISTOGRAM_CONFIRM_UNDO);
99 base::ResetAndReturn(&startSyncCallback_).Run(
100 OneClickSigninSyncStarter::UNDO_SYNC);
105 - (IBAction)onClickAdvancedLink:(id)sender {
107 OneClickSigninHelper::LogConfirmHistogramValue(
109 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED :
110 signin_metrics::HISTOGRAM_CONFIRM_ADVANCED);
112 base::ResetAndReturn(&startSyncCallback_).Run(
113 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
116 content::OpenURLParams params(GURL(chrome::kChromeUISettingsURL),
117 content::Referrer(), CURRENT_TAB,
118 ui::PAGE_TRANSITION_LINK, false);
119 webContents_->OpenURL(params);
124 - (IBAction)onClickClose:(id)sender {
126 OneClickSigninHelper::LogConfirmHistogramValue(
128 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE :
129 signin_metrics::HISTOGRAM_CONFIRM_CLOSE);
131 base::ResetAndReturn(&startSyncCallback_).Run(
132 OneClickSigninSyncStarter::UNDO_SYNC);
137 - (void)awakeFromNib {
138 // Lay out the text controls from the bottom up.
139 CGFloat totalYOffset = 0.0;
141 if ([errorMessage_ length] == 0) {
143 [GTMUILocalizerAndLayoutTweaker sizeToFitView:advancedLink_].height;
144 [[advancedLink_ cell] setTextColor:
145 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())];
147 // Don't display the advanced link for the error bubble.
148 // To align the Learn More link with the OK button, we need to offset by
149 // the height of the Advanced link, plus the padding between it and the
150 // Learn More link above.
151 float advancedLinkHeightPlusPadding =
152 [informativePlaceholderTextField_ frame].origin.y -
153 [advancedLink_ frame].origin.y;
155 totalYOffset -= advancedLinkHeightPlusPadding;
156 [advancedLink_ removeFromSuperview];
159 if (informativePlaceholderTextField_) {
160 if (!isSyncDialog_ && ([errorMessage_ length] != 0)) {
161 // Move up the "Learn more" origin in error case to account for the
163 NSRect frame = [informativePlaceholderTextField_ frame];
164 frame = NSOffsetRect(frame, 0, NSHeight([titleTextField_ frame]));
165 [informativePlaceholderTextField_ setFrame:frame];
168 ShiftOriginY(informativePlaceholderTextField_, totalYOffset);
169 totalYOffset += [self initializeInformativeTextView];
172 ShiftOriginY(messageTextField_, totalYOffset);
174 [GTMUILocalizerAndLayoutTweaker
175 sizeToFitFixedWidthTextField:messageTextField_];
177 ShiftOriginY(titleTextField_, totalYOffset);
179 [GTMUILocalizerAndLayoutTweaker
180 sizeToFitFixedWidthTextField:titleTextField_];
182 NSSize delta = NSMakeSize(0.0, totalYOffset);
185 [messageTextField_ setStringValue:l10n_util::GetNSStringWithFixup(
186 IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW)];
187 } else if ([errorMessage_ length] != 0) {
188 [titleTextField_ setHidden:YES];
189 [messageTextField_ setStringValue:errorMessage_];
191 // Make the bubble less tall, as the title text will be hidden.
192 NSSize size = [[self view] frame].size;
193 size.height = size.height - NSHeight([titleTextField_ frame]);
194 [[self view] setFrameSize:size];
196 // Shift the message text up to where the title text used to be.
197 NSPoint origin = [titleTextField_ frame].origin;
198 [messageTextField_ setFrameOrigin:origin];
199 ShiftOriginY(messageTextField_, -kTopErrorMessageMargin);
201 // Use "OK" instead of "OK, got it" in the error case, and size the button
203 [closeButton_ setTitle:l10n_util::GetNSStringWithFixup(
205 [GTMUILocalizerAndLayoutTweaker sizeToFitView:[closeButton_ superview]];
208 // Resize bubble and window to hold the controls.
209 [GTMUILocalizerAndLayoutTweaker
210 resizeViewWithoutAutoResizingSubViews:[self view]
214 OneClickSigninHelper::LogConfirmHistogramValue(
215 signin_metrics::HISTOGRAM_CONFIRM_SHOWN);
219 - (CGFloat)initializeInformativeTextView {
220 NSRect oldFrame = [informativePlaceholderTextField_ frame];
222 // Replace the placeholder NSTextField with the real label NSTextView. The
223 // former doesn't show links in a nice way, but the latter can't be added in
224 // a xib without a containing scroll view, so create the NSTextView
226 informativeTextView_.reset(
227 [[HyperlinkTextView alloc] initWithFrame:oldFrame]);
228 [informativeTextView_.get() setAutoresizingMask:
229 [informativePlaceholderTextField_ autoresizingMask]];
230 [informativeTextView_.get() setDelegate:self];
233 NSString* learnMoreText = l10n_util::GetNSStringWithFixup(IDS_LEARN_MORE);
234 NSString* messageText;
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 messageText = [messageText stringByAppendingString:@" "];
252 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor());
253 [informativeTextView_ setMessageAndLink:messageText
254 withLink:learnMoreText
255 atOffset:[messageText length]
257 messageColor:[NSColor blackColor]
258 linkColor:linkColor];
261 // Make the "Advanced" link font as large as the "Learn More" link.
262 [[advancedLink_ cell] setFont:font];
263 [advancedLink_ sizeToFit];
266 [[informativePlaceholderTextField_ cell] setAttributedStringValue:
267 [informativeTextView_ attributedString]];
268 [GTMUILocalizerAndLayoutTweaker
269 sizeToFitFixedWidthTextField:informativePlaceholderTextField_];
270 NSRect newFrame = [informativePlaceholderTextField_ frame];
271 [informativeTextView_ setFrame:newFrame];
274 [[informativePlaceholderTextField_ superview]
275 replaceSubview:informativePlaceholderTextField_
276 with:informativeTextView_.get()];
277 informativePlaceholderTextField_ = nil; // Now released.
279 return NSHeight(newFrame) - NSHeight(oldFrame);
282 - (BOOL)textView:(NSTextView*)textView
283 clickedOnLink:(id)link
284 atIndex:(NSUInteger)charIndex {
285 if (isSyncDialog_ && !clickedLearnMore_) {
286 clickedLearnMore_ = YES;
288 OneClickSigninHelper::LogConfirmHistogramValue(
289 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE);
291 WindowOpenDisposition location = isSyncDialog_ ?
292 NEW_WINDOW : NEW_FOREGROUND_TAB;
293 content::OpenURLParams params(GURL(chrome::kChromeSyncLearnMoreURL),
294 content::Referrer(), location,
295 ui::PAGE_TRANSITION_LINK, false);
296 webContents_->OpenURL(params);
301 base::ResetAndReturn(&closeCallback_).Run();