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

Side by Side Diff: chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm

Issue 9460045: Add Mac interface for installing bundles of extensions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 10 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller. h" 5 #import "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller. h"
6 6
7 #include "base/i18n/rtl.h"
7 #include "base/mac/bundle_locations.h" 8 #include "base/mac/bundle_locations.h"
8 #include "base/mac/mac_util.h" 9 #include "base/mac/mac_util.h"
9 #include "base/memory/scoped_nsobject.h" 10 #include "base/memory/scoped_nsobject.h"
10 #include "base/string_util.h" 11 #include "base/string_util.h"
11 #include "base/sys_string_conversions.h" 12 #include "base/sys_string_conversions.h"
12 #include "base/utf_string_conversions.h" 13 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/extensions/bundle_installer.h"
13 #include "chrome/browser/extensions/extension_install_dialog.h" 15 #include "chrome/browser/extensions/extension_install_dialog.h"
14 #include "chrome/browser/ui/browser.h" 16 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_list.h" 17 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/common/extensions/extension.h" 19 #include "chrome/common/extensions/extension.h"
18 #include "grit/generated_resources.h" 20 #include "grit/generated_resources.h"
19 #include "skia/ext/skia_utils_mac.h" 21 #include "skia/ext/skia_utils_mac.h"
20 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" 22 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
21 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/l10n/l10n_util_mac.h" 24 #include "ui/base/l10n/l10n_util_mac.h"
23 25
24 using content::OpenURLParams; 26 using content::OpenURLParams;
25 using content::Referrer; 27 using content::Referrer;
28 using extensions::BundleInstaller;
26 29
27 @interface ExtensionInstallDialogController () 30 @interface ExtensionInstallDialogController ()
28 - (bool)isInlineInstall; 31 - (BOOL)isBundleInstall;
32 - (BOOL)isInlineInstall;
29 - (void)appendRatingStar:(const SkBitmap*)skiaImage; 33 - (void)appendRatingStar:(const SkBitmap*)skiaImage;
30 @end 34 @end
31 35
32 namespace { 36 namespace {
33 37
34 // Padding above the warnings separator, we must also subtract this when hiding 38 // Padding above the warnings separator, we must also subtract this when hiding
35 // it. 39 // it.
36 const CGFloat kWarningsSeparatorPadding = 14; 40 const CGFloat kWarningsSeparatorPadding = 14;
37 41
38 // Maximum height we will adjust controls to when trying to accomodate their 42 // Maximum height we will adjust controls to when trying to accomodate their
39 // contents. 43 // contents.
40 const CGFloat kMaxControlHeight = 400; 44 const CGFloat kMaxControlHeight = 400;
41 45
42 // Adjust a control's height so that its content its not clipped. Returns the 46 void OffsetControlVerticallyToFitContent(NSControl* control,
43 // amount the control's height had to be adjusted. 47 CGFloat* totalOffset) {
Robert Sesek 2012/02/27 18:59:20 Why remove this comment?
jstritar 2012/03/05 18:05:08 Updated the comment.
44 CGFloat AdjustControlHeightToFitContent(NSControl* control) { 48 // Adjust the control's height so that its content is not clipped.
45 NSRect currentRect = [control frame]; 49 NSRect currentRect = [control frame];
46 NSRect fitRect = currentRect; 50 NSRect fitRect = currentRect;
47 fitRect.size.height = kMaxControlHeight; 51 fitRect.size.height = kMaxControlHeight;
48 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height; 52 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height;
49 CGFloat offset = desiredHeight - currentRect.size.height; 53 CGFloat offset = desiredHeight - currentRect.size.height;
50 54
51 [control setFrameSize:NSMakeSize(currentRect.size.width, 55 [control setFrameSize:NSMakeSize(currentRect.size.width,
52 currentRect.size.height + offset)]; 56 currentRect.size.height + offset)];
53 return offset;
54 }
55 57
56 // Moves the control vertically by the specified amount. 58 *totalOffset += offset;
57 void OffsetControlVertically(NSControl* control, CGFloat amount) { 59
60 // Move the control vertically by the new total offset.
58 NSPoint origin = [control frame].origin; 61 NSPoint origin = [control frame].origin;
59 origin.y += amount; 62 origin.y -= *totalOffset;
60 [control setFrameOrigin:origin]; 63 [control setFrameOrigin:origin];
61 } 64 }
62 65
63 void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) { 66 void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) {
64 ExtensionInstallDialogController* controller = 67 ExtensionInstallDialogController* controller =
65 static_cast<ExtensionInstallDialogController*>(data); 68 static_cast<ExtensionInstallDialogController*>(data);
66 [controller appendRatingStar:skiaImage]; 69 [controller appendRatingStar:skiaImage];
67 } 70 }
68 71
69 } 72 }
70 73
71 @implementation ExtensionInstallDialogController 74 @implementation ExtensionInstallDialogController
72 75
73 @synthesize iconView = iconView_; 76 @synthesize iconView = iconView_;
74 @synthesize titleField = titleField_; 77 @synthesize titleField = titleField_;
78 @synthesize itemsField = itemsField_;
75 @synthesize subtitleField = subtitleField_; 79 @synthesize subtitleField = subtitleField_;
76 @synthesize warningsField = warningsField_; 80 @synthesize warningsField = warningsField_;
77 @synthesize cancelButton = cancelButton_; 81 @synthesize cancelButton = cancelButton_;
78 @synthesize okButton = okButton_; 82 @synthesize okButton = okButton_;
79 @synthesize warningsSeparator = warningsSeparator_; 83 @synthesize warningsSeparator = warningsSeparator_;
80 @synthesize ratingStars = ratingStars_; 84 @synthesize ratingStars = ratingStars_;
81 @synthesize ratingCountField = ratingCountField_; 85 @synthesize ratingCountField = ratingCountField_;
82 @synthesize userCountField = userCountField_; 86 @synthesize userCountField = userCountField_;
83 87
84 - (id)initWithParentWindow:(NSWindow*)window 88 - (id)initWithParentWindow:(NSWindow*)window
85 profile:(Profile*)profile 89 profile:(Profile*)profile
86 delegate:(ExtensionInstallUI::Delegate*)delegate 90 delegate:(ExtensionInstallUI::Delegate*)delegate
87 prompt:(const ExtensionInstallUI::Prompt&)prompt { 91 prompt:(const ExtensionInstallUI::Prompt&)prompt {
88 NSString* nibpath = nil; 92 NSString* nibpath = nil;
89 93
90 // We use a different XIB in the case of inline installs or no permission 94 // We use a different XIB in the case of inline installs or no permission
91 // warnings, that respectively show webstore ratings data and are a more 95 // warnings, that respectively show webstore ratings data and are a more
92 // nicely laid out. 96 // nicely laid out.
93 if (prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT) { 97 if (prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT) {
94 nibpath = [base::mac::FrameworkBundle() 98 nibpath = [base::mac::FrameworkBundle()
95 pathForResource:@"ExtensionInstallPromptInline" 99 pathForResource:@"ExtensionInstallPromptInline"
96 ofType:@"nib"]; 100 ofType:@"nib"];
97 } else if (prompt.GetPermissionCount() == 0) { 101 } else if (prompt.GetPermissionCount() == 0) {
98 nibpath = [base::mac::FrameworkBundle() 102 nibpath = [base::mac::FrameworkBundle()
99 pathForResource:@"ExtensionInstallPromptNoWarnings" 103 pathForResource:@"ExtensionInstallPromptNoWarnings"
100 ofType:@"nib"]; 104 ofType:@"nib"];
105 } else if (prompt.type() == ExtensionInstallUI::BUNDLE_INSTALL_PROMPT) {
106 nibpath = [base::mac::FrameworkBundle()
107 pathForResource:@"ExtensionInstallPromptBundle"
108 ofType:@"nib"];
101 } else { 109 } else {
102 nibpath = [base::mac::FrameworkBundle() 110 nibpath = [base::mac::FrameworkBundle()
103 pathForResource:@"ExtensionInstallPrompt" 111 pathForResource:@"ExtensionInstallPrompt"
104 ofType:@"nib"]; 112 ofType:@"nib"];
105 } 113 }
106 114
107 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { 115 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
108 parentWindow_ = window; 116 parentWindow_ = window;
109 profile_ = profile; 117 profile_ = profile;
110 delegate_ = delegate; 118 delegate_ = delegate;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 base::SysUTF16ToNSString(prompt_->GetAbortButtonLabel()) : 163 base::SysUTF16ToNSString(prompt_->GetAbortButtonLabel()) :
156 l10n_util::GetNSString(IDS_CANCEL)]; 164 l10n_util::GetNSString(IDS_CANCEL)];
157 if ([self isInlineInstall]) { 165 if ([self isInlineInstall]) {
158 prompt_->AppendRatingStars(AppendRatingStarsShim, self); 166 prompt_->AppendRatingStars(AppendRatingStarsShim, self);
159 [ratingCountField_ setStringValue:base::SysUTF16ToNSString( 167 [ratingCountField_ setStringValue:base::SysUTF16ToNSString(
160 prompt_->GetRatingCount())]; 168 prompt_->GetRatingCount())];
161 [userCountField_ setStringValue:base::SysUTF16ToNSString( 169 [userCountField_ setStringValue:base::SysUTF16ToNSString(
162 prompt_->GetUserCount())]; 170 prompt_->GetUserCount())];
163 } 171 }
164 172
165 [iconView_ setImage:prompt_->icon().ToNSImage()]; 173 // The bundle install dialog has no icon.
174 if (![self isBundleInstall])
175 [iconView_ setImage:prompt_->icon().ToNSImage()];
166 176
167 // Resize |titleField_| to fit the title. 177 // Resize |titleField_| to fit the title.
168 CGFloat originalTitleWidth = [titleField_ frame].size.width; 178 CGFloat originalTitleWidth = [titleField_ frame].size.width;
169 [titleField_ sizeToFit]; 179 [titleField_ sizeToFit];
170 CGFloat newTitleWidth = [titleField_ frame].size.width; 180 CGFloat newTitleWidth = [titleField_ frame].size.width;
171 if (newTitleWidth > originalTitleWidth) { 181 if (newTitleWidth > originalTitleWidth) {
172 NSRect frame = [[self window] frame]; 182 NSRect frame = [[self window] frame];
173 frame.size.width += newTitleWidth - originalTitleWidth; 183 frame.size.width += newTitleWidth - originalTitleWidth;
174 [[self window] setFrame:frame display:NO]; 184 [[self window] setFrame:frame display:NO];
175 } 185 }
176 186
177 // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep 187 // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep
178 // them right-aligned. 188 // them right-aligned.
179 NSSize buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_]; 189 NSSize buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_];
180 if (buttonDelta.width) { 190 if (buttonDelta.width) {
181 [okButton_ setFrame:NSOffsetRect([okButton_ frame], -buttonDelta.width, 0)]; 191 [okButton_ setFrame:NSOffsetRect([okButton_ frame], -buttonDelta.width, 0)];
182 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], 192 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
183 -buttonDelta.width, 0)]; 193 -buttonDelta.width, 0)];
184 } 194 }
185 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_]; 195 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_];
186 if (buttonDelta.width) { 196 if (buttonDelta.width) {
187 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], 197 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
188 -buttonDelta.width, 0)]; 198 -buttonDelta.width, 0)];
189 } 199 }
190 200
201 // The dialog is laid out in the NIB exactly how we want it assuming that
202 // each label fits on one line. However, for each label, we want to allow
203 // wrapping onto multiple lines. So we accumulate an offset by measuring how
204 // big each label wants to be, and comparing it to how big it actually is.
205 // Then we shift each label down and resize by the appropriate amount, then
206 // finally resize the window.
191 CGFloat totalOffset = 0.0; 207 CGFloat totalOffset = 0.0;
208
209 if ([self isBundleInstall]) {
210 [subtitleField_ setStringValue:base::SysUTF16ToNSString(
211 prompt_->GetPermissionsHeading())];
212
213 // We display the list of extension names as a simple text string, seperated
214 // by newlines.
215 BundleInstaller::ItemList items = prompt_->bundle()->GetItemsWithState(
216 BundleInstaller::Item::STATE_PENDING);
217
218 NSMutableString* joinedItems = [NSMutableString string];
219 for (size_t i = 0; i < items.size(); ++i) {
220 if (i > 0)
221 [joinedItems appendString:@"\n"];
222 [joinedItems appendString:base::SysUTF16ToNSString(
223 items[i].GetNameForDisplay())];
224 }
225 [itemsField_ setStringValue:joinedItems];
226
227 // Adjust the controls to fit the list of extensions.
228 OffsetControlVerticallyToFitContent(titleField_, &totalOffset);
229 OffsetControlVerticallyToFitContent(itemsField_, &totalOffset);
230 }
231
192 // If there are any warnings, then we have to do some special layout. 232 // If there are any warnings, then we have to do some special layout.
193 if (prompt_->GetPermissionCount() > 0) { 233 if (prompt_->GetPermissionCount() > 0) {
194 [subtitleField_ setStringValue:base::SysUTF16ToNSString( 234 [subtitleField_ setStringValue:base::SysUTF16ToNSString(
195 prompt_->GetPermissionsHeading())]; 235 prompt_->GetPermissionsHeading())];
196 236
197 // We display the permission warnings as a simple text string, separated by 237 // We display the permission warnings as a simple text string, separated by
198 // newlines. 238 // newlines.
199 NSMutableString* joinedWarnings = [NSMutableString string]; 239 NSMutableString* joinedWarnings = [NSMutableString string];
200 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) { 240 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) {
201 if (i > 0) 241 if (i > 0)
202 [joinedWarnings appendString:@"\n"]; 242 [joinedWarnings appendString:@"\n"];
203 [joinedWarnings appendString:base::SysUTF16ToNSString( 243 [joinedWarnings appendString:base::SysUTF16ToNSString(
204 prompt_->GetPermission(i))]; 244 prompt_->GetPermission(i))];
205 } 245 }
206 [warningsField_ setStringValue:joinedWarnings]; 246 [warningsField_ setStringValue:joinedWarnings];
207 247
208 // The dialog is laid out in the NIB exactly how we want it assuming that 248 // In the store version of the dialog the icon extends past the one-line
209 // each label fits on one line. However, for each label, we want to allow 249 // version of the permission field. Therefore when increasing the window
210 // wrapping onto multiple lines. So we accumulate an offset by measuring how 250 // size for multi-line permissions we don't have to add the full offset,
211 // big each label wants to be, and comparing it to how big it actually is. 251 // only the part that extends past the icon.
212 // Then we shift each label down and resize by the appropriate amount, then
213 // finally resize the window.
214
215 // Additionally, in the store version of the dialog the icon extends past
216 // the one-line version of the permission field. Therefore when increasing
217 // the window size for multi-line permissions we don't have to add the full
218 // offset, only the part that extends past the icon.
219 CGFloat warningsGrowthSlack = 0; 252 CGFloat warningsGrowthSlack = 0;
220 if ([warningsField_ frame].origin.y > [iconView_ frame].origin.y) { 253 if (![self isBundleInstall] &&
254 [warningsField_ frame].origin.y > [iconView_ frame].origin.y) {
221 warningsGrowthSlack = 255 warningsGrowthSlack =
222 [warningsField_ frame].origin.y - [iconView_ frame].origin.y; 256 [warningsField_ frame].origin.y - [iconView_ frame].origin.y;
223 } 257 }
224 258
225 totalOffset += AdjustControlHeightToFitContent(subtitleField_); 259 // Adjust the controls to fit the permission warnings.
226 OffsetControlVertically(subtitleField_, -totalOffset); 260 OffsetControlVerticallyToFitContent(subtitleField_, &totalOffset);
261 OffsetControlVerticallyToFitContent(warningsField_, &totalOffset);
227 262
228 totalOffset += AdjustControlHeightToFitContent(warningsField_);
229 OffsetControlVertically(warningsField_, -totalOffset);
230 totalOffset = MAX(totalOffset - warningsGrowthSlack, 0); 263 totalOffset = MAX(totalOffset - warningsGrowthSlack, 0);
264
Robert Sesek 2012/02/27 18:59:20 nit: remove
jstritar 2012/03/05 18:05:08 Done.
231 } else if ([self isInlineInstall]) { 265 } else if ([self isInlineInstall]) {
232 // Inline installs that don't have a permissions section need to hide 266 // Inline installs that don't have a permissions section need to hide
233 // controls related to that and shrink the window by the space they take 267 // controls related to that and shrink the window by the space they take
234 // up. 268 // up.
235 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame], 269 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame],
236 [subtitleField_ frame]); 270 [subtitleField_ frame]);
237 hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]); 271 hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]);
238 [warningsSeparator_ setHidden:YES]; 272 [warningsSeparator_ setHidden:YES];
239 [subtitleField_ setHidden:YES]; 273 [subtitleField_ setHidden:YES];
240 [warningsField_ setHidden:YES]; 274 [warningsField_ setHidden:YES];
241 totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding; 275 totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding;
242 } 276 }
243 277
244 // If necessary, adjust the window size. 278 // If necessary, adjust the window size.
245 if (totalOffset) { 279 if (totalOffset) {
246 NSRect currentRect = [[self window] frame]; 280 NSRect currentRect = [[self window] frame];
247 [[self window] setFrame:NSMakeRect(currentRect.origin.x, 281 [[self window] setFrame:NSMakeRect(currentRect.origin.x,
248 currentRect.origin.y - totalOffset, 282 currentRect.origin.y - totalOffset,
249 currentRect.size.width, 283 currentRect.size.width,
250 currentRect.size.height + totalOffset) 284 currentRect.size.height + totalOffset)
251 display:NO]; 285 display:NO];
252 } 286 }
253 } 287 }
254 288
255 - (void)didEndSheet:(NSWindow*)sheet 289 - (void)didEndSheet:(NSWindow*)sheet
256 returnCode:(int)returnCode 290 returnCode:(int)returnCode
257 contextInfo:(void*)contextInfo { 291 contextInfo:(void*)contextInfo {
258 [sheet close]; 292 [sheet close];
259 } 293 }
260 294
261 - (void)windowWillClose:(NSNotification*)notification { 295 - (void)windowWillClose:(NSNotification*)notification {
262 [self autorelease]; 296 [self autorelease];
263 } 297 }
264 298
265 - (bool)isInlineInstall { 299 - (BOOL)isBundleInstall {
300 return prompt_->type() == ExtensionInstallUI::BUNDLE_INSTALL_PROMPT;
301 }
302
303 - (BOOL)isInlineInstall {
266 return prompt_->type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT; 304 return prompt_->type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT;
267 } 305 }
268 306
269 - (void)appendRatingStar:(const SkBitmap*)skiaImage { 307 - (void)appendRatingStar:(const SkBitmap*)skiaImage {
270 NSImage* image = gfx::SkBitmapToNSImageWithColorSpace( 308 NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
271 *skiaImage, base::mac::GetSystemColorSpace()); 309 *skiaImage, base::mac::GetSystemColorSpace());
272 NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height()); 310 NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height());
273 scoped_nsobject<NSImageView> view([[NSImageView alloc] initWithFrame:frame]); 311 scoped_nsobject<NSImageView> view([[NSImageView alloc] initWithFrame:frame]);
274 [view setImage:image]; 312 [view setImage:image];
275 313
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 ExtensionInstallDialogController* controller = 345 ExtensionInstallDialogController* controller =
308 [[ExtensionInstallDialogController alloc] 346 [[ExtensionInstallDialogController alloc]
309 initWithParentWindow:native_window 347 initWithParentWindow:native_window
310 profile:profile 348 profile:profile
311 delegate:delegate 349 delegate:delegate
312 prompt:prompt]; 350 prompt:prompt];
313 351
314 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455) 352 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455)
315 [controller runAsModalSheet]; 353 [controller runAsModalSheet];
316 } 354 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698