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

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, 9 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 // Adjust the |control|'s height so that its content is not clipped.
43 // amount the control's height had to be adjusted. 47 // This also adds the change in height to the |totalOffset| and shifts the
44 CGFloat AdjustControlHeightToFitContent(NSControl* control) { 48 // control down by that amount.
49 void OffsetControlVerticallyToFitContent(NSControl* control,
50 CGFloat* totalOffset) {
51 // Adjust the control's height so that its content is not clipped.
45 NSRect currentRect = [control frame]; 52 NSRect currentRect = [control frame];
46 NSRect fitRect = currentRect; 53 NSRect fitRect = currentRect;
47 fitRect.size.height = kMaxControlHeight; 54 fitRect.size.height = kMaxControlHeight;
48 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height; 55 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height;
49 CGFloat offset = desiredHeight - currentRect.size.height; 56 CGFloat offset = desiredHeight - currentRect.size.height;
50 57
51 [control setFrameSize:NSMakeSize(currentRect.size.width, 58 [control setFrameSize:NSMakeSize(currentRect.size.width,
52 currentRect.size.height + offset)]; 59 currentRect.size.height + offset)];
53 return offset;
54 }
55 60
56 // Moves the control vertically by the specified amount. 61 *totalOffset += offset;
57 void OffsetControlVertically(NSControl* control, CGFloat amount) { 62
63 // Move the control vertically by the new total offset.
58 NSPoint origin = [control frame].origin; 64 NSPoint origin = [control frame].origin;
59 origin.y += amount; 65 origin.y -= *totalOffset;
60 [control setFrameOrigin:origin]; 66 [control setFrameOrigin:origin];
61 } 67 }
62 68
63 void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) { 69 void AppendRatingStarsShim(const SkBitmap* skiaImage, void* data) {
64 ExtensionInstallDialogController* controller = 70 ExtensionInstallDialogController* controller =
65 static_cast<ExtensionInstallDialogController*>(data); 71 static_cast<ExtensionInstallDialogController*>(data);
66 [controller appendRatingStar:skiaImage]; 72 [controller appendRatingStar:skiaImage];
67 } 73 }
68 74
69 } 75 }
70 76
71 @implementation ExtensionInstallDialogController 77 @implementation ExtensionInstallDialogController
72 78
73 @synthesize iconView = iconView_; 79 @synthesize iconView = iconView_;
74 @synthesize titleField = titleField_; 80 @synthesize titleField = titleField_;
81 @synthesize itemsField = itemsField_;
75 @synthesize subtitleField = subtitleField_; 82 @synthesize subtitleField = subtitleField_;
76 @synthesize warningsField = warningsField_; 83 @synthesize warningsField = warningsField_;
77 @synthesize cancelButton = cancelButton_; 84 @synthesize cancelButton = cancelButton_;
78 @synthesize okButton = okButton_; 85 @synthesize okButton = okButton_;
79 @synthesize warningsSeparator = warningsSeparator_; 86 @synthesize warningsSeparator = warningsSeparator_;
80 @synthesize ratingStars = ratingStars_; 87 @synthesize ratingStars = ratingStars_;
81 @synthesize ratingCountField = ratingCountField_; 88 @synthesize ratingCountField = ratingCountField_;
82 @synthesize userCountField = userCountField_; 89 @synthesize userCountField = userCountField_;
83 90
84 - (id)initWithParentWindow:(NSWindow*)window 91 - (id)initWithParentWindow:(NSWindow*)window
85 profile:(Profile*)profile 92 profile:(Profile*)profile
86 delegate:(ExtensionInstallUI::Delegate*)delegate 93 delegate:(ExtensionInstallUI::Delegate*)delegate
87 prompt:(const ExtensionInstallUI::Prompt&)prompt { 94 prompt:(const ExtensionInstallUI::Prompt&)prompt {
88 NSString* nibpath = nil; 95 NSString* nibpath = nil;
89 96
90 // We use a different XIB in the case of inline installs or no permission 97 // We use a different XIB in the case of bundle installs, inline installs or
91 // warnings, that respectively show webstore ratings data and are a more 98 // no permission warnings. These are laid out nicely for the data they
92 // nicely laid out. 99 // display.
93 if (prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT) { 100 if (prompt.type() == ExtensionInstallUI::BUNDLE_INSTALL_PROMPT) {
101 nibpath = [base::mac::FrameworkBundle()
102 pathForResource:@"ExtensionInstallPromptBundle"
103 ofType:@"nib"];
104 } else if (prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT) {
94 nibpath = [base::mac::FrameworkBundle() 105 nibpath = [base::mac::FrameworkBundle()
95 pathForResource:@"ExtensionInstallPromptInline" 106 pathForResource:@"ExtensionInstallPromptInline"
96 ofType:@"nib"]; 107 ofType:@"nib"];
97 } else if (prompt.GetPermissionCount() == 0) { 108 } else if (prompt.GetPermissionCount() == 0) {
98 nibpath = [base::mac::FrameworkBundle() 109 nibpath = [base::mac::FrameworkBundle()
99 pathForResource:@"ExtensionInstallPromptNoWarnings" 110 pathForResource:@"ExtensionInstallPromptNoWarnings"
100 ofType:@"nib"]; 111 ofType:@"nib"];
101 } else { 112 } else {
102 nibpath = [base::mac::FrameworkBundle() 113 nibpath = [base::mac::FrameworkBundle()
103 pathForResource:@"ExtensionInstallPrompt" 114 pathForResource:@"ExtensionInstallPrompt"
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 base::SysUTF16ToNSString(prompt_->GetAbortButtonLabel()) : 166 base::SysUTF16ToNSString(prompt_->GetAbortButtonLabel()) :
156 l10n_util::GetNSString(IDS_CANCEL)]; 167 l10n_util::GetNSString(IDS_CANCEL)];
157 if ([self isInlineInstall]) { 168 if ([self isInlineInstall]) {
158 prompt_->AppendRatingStars(AppendRatingStarsShim, self); 169 prompt_->AppendRatingStars(AppendRatingStarsShim, self);
159 [ratingCountField_ setStringValue:base::SysUTF16ToNSString( 170 [ratingCountField_ setStringValue:base::SysUTF16ToNSString(
160 prompt_->GetRatingCount())]; 171 prompt_->GetRatingCount())];
161 [userCountField_ setStringValue:base::SysUTF16ToNSString( 172 [userCountField_ setStringValue:base::SysUTF16ToNSString(
162 prompt_->GetUserCount())]; 173 prompt_->GetUserCount())];
163 } 174 }
164 175
165 [iconView_ setImage:prompt_->icon().ToNSImage()]; 176 // The bundle install dialog has no icon.
177 if (![self isBundleInstall])
178 [iconView_ setImage:prompt_->icon().ToNSImage()];
166 179
167 // Resize |titleField_| to fit the title. 180 // Resize |titleField_| to fit the title.
168 CGFloat originalTitleWidth = [titleField_ frame].size.width; 181 CGFloat originalTitleWidth = [titleField_ frame].size.width;
169 [titleField_ sizeToFit]; 182 [titleField_ sizeToFit];
170 CGFloat newTitleWidth = [titleField_ frame].size.width; 183 CGFloat newTitleWidth = [titleField_ frame].size.width;
171 if (newTitleWidth > originalTitleWidth) { 184 if (newTitleWidth > originalTitleWidth) {
172 NSRect frame = [[self window] frame]; 185 NSRect frame = [[self window] frame];
173 frame.size.width += newTitleWidth - originalTitleWidth; 186 frame.size.width += newTitleWidth - originalTitleWidth;
174 [[self window] setFrame:frame display:NO]; 187 [[self window] setFrame:frame display:NO];
175 } 188 }
176 189
177 // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep 190 // Resize |okButton_| and |cancelButton_| to fit the button labels, but keep
178 // them right-aligned. 191 // them right-aligned.
179 NSSize buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_]; 192 NSSize buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_];
180 if (buttonDelta.width) { 193 if (buttonDelta.width) {
181 [okButton_ setFrame:NSOffsetRect([okButton_ frame], -buttonDelta.width, 0)]; 194 [okButton_ setFrame:NSOffsetRect([okButton_ frame], -buttonDelta.width, 0)];
182 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], 195 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
183 -buttonDelta.width, 0)]; 196 -buttonDelta.width, 0)];
184 } 197 }
185 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_]; 198 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_];
186 if (buttonDelta.width) { 199 if (buttonDelta.width) {
187 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], 200 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame],
188 -buttonDelta.width, 0)]; 201 -buttonDelta.width, 0)];
189 } 202 }
190 203
204 // The dialog is laid out in the NIB exactly how we want it assuming that
205 // each label fits on one line. However, for each label, we want to allow
206 // wrapping onto multiple lines. So we accumulate an offset by measuring how
207 // big each label wants to be, and comparing it to how big it actually is.
208 // Then we shift each label down and resize by the appropriate amount, then
209 // finally resize the window.
191 CGFloat totalOffset = 0.0; 210 CGFloat totalOffset = 0.0;
211
212 if ([self isBundleInstall]) {
213 [subtitleField_ setStringValue:base::SysUTF16ToNSString(
214 prompt_->GetPermissionsHeading())];
215
216 // We display the list of extension names as a simple text string, seperated
217 // by newlines.
218 BundleInstaller::ItemList items = prompt_->bundle()->GetItemsWithState(
219 BundleInstaller::Item::STATE_PENDING);
220
221 NSMutableString* joinedItems = [NSMutableString string];
222 for (size_t i = 0; i < items.size(); ++i) {
223 if (i > 0)
224 [joinedItems appendString:@"\n"];
225 [joinedItems appendString:base::SysUTF16ToNSString(
226 items[i].GetNameForDisplay())];
227 }
228 [itemsField_ setStringValue:joinedItems];
229
230 // Adjust the controls to fit the list of extensions.
231 OffsetControlVerticallyToFitContent(titleField_, &totalOffset);
232 OffsetControlVerticallyToFitContent(itemsField_, &totalOffset);
233 }
234
192 // If there are any warnings, then we have to do some special layout. 235 // If there are any warnings, then we have to do some special layout.
193 if (prompt_->GetPermissionCount() > 0) { 236 if (prompt_->GetPermissionCount() > 0) {
194 [subtitleField_ setStringValue:base::SysUTF16ToNSString( 237 [subtitleField_ setStringValue:base::SysUTF16ToNSString(
195 prompt_->GetPermissionsHeading())]; 238 prompt_->GetPermissionsHeading())];
196 239
197 // We display the permission warnings as a simple text string, separated by 240 // We display the permission warnings as a simple text string, separated by
198 // newlines. 241 // newlines.
199 NSMutableString* joinedWarnings = [NSMutableString string]; 242 NSMutableString* joinedWarnings = [NSMutableString string];
200 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) { 243 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) {
201 if (i > 0) 244 if (i > 0)
202 [joinedWarnings appendString:@"\n"]; 245 [joinedWarnings appendString:@"\n"];
203 [joinedWarnings appendString:base::SysUTF16ToNSString( 246 [joinedWarnings appendString:base::SysUTF16ToNSString(
204 prompt_->GetPermission(i))]; 247 prompt_->GetPermission(i))];
205 } 248 }
206 [warningsField_ setStringValue:joinedWarnings]; 249 [warningsField_ setStringValue:joinedWarnings];
207 250
208 // The dialog is laid out in the NIB exactly how we want it assuming that 251 // 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 252 // version of the permission field. Therefore when increasing the window
210 // wrapping onto multiple lines. So we accumulate an offset by measuring how 253 // 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. 254 // 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; 255 CGFloat warningsGrowthSlack = 0;
220 if ([warningsField_ frame].origin.y > [iconView_ frame].origin.y) { 256 if (![self isBundleInstall] &&
257 [warningsField_ frame].origin.y > [iconView_ frame].origin.y) {
221 warningsGrowthSlack = 258 warningsGrowthSlack =
222 [warningsField_ frame].origin.y - [iconView_ frame].origin.y; 259 [warningsField_ frame].origin.y - [iconView_ frame].origin.y;
223 } 260 }
224 261
225 totalOffset += AdjustControlHeightToFitContent(subtitleField_); 262 // Adjust the controls to fit the permission warnings.
226 OffsetControlVertically(subtitleField_, -totalOffset); 263 OffsetControlVerticallyToFitContent(subtitleField_, &totalOffset);
264 OffsetControlVerticallyToFitContent(warningsField_, &totalOffset);
227 265
228 totalOffset += AdjustControlHeightToFitContent(warningsField_);
229 OffsetControlVertically(warningsField_, -totalOffset);
230 totalOffset = MAX(totalOffset - warningsGrowthSlack, 0); 266 totalOffset = MAX(totalOffset - warningsGrowthSlack, 0);
231 } else if ([self isInlineInstall]) { 267 } else if ([self isInlineInstall] || [self isBundleInstall]) {
232 // Inline installs that don't have a permissions section need to hide 268 // Inline and bundle installs that don't have a permissions section need to
233 // controls related to that and shrink the window by the space they take 269 // hide controls related to that and shrink the window by the space they
234 // up. 270 // take up.
235 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame], 271 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame],
236 [subtitleField_ frame]); 272 [subtitleField_ frame]);
237 hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]); 273 hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]);
238 [warningsSeparator_ setHidden:YES]; 274 [warningsSeparator_ setHidden:YES];
239 [subtitleField_ setHidden:YES]; 275 [subtitleField_ setHidden:YES];
240 [warningsField_ setHidden:YES]; 276 [warningsField_ setHidden:YES];
241 totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding; 277 totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding;
242 } 278 }
243 279
244 // If necessary, adjust the window size. 280 // If necessary, adjust the window size.
245 if (totalOffset) { 281 if (totalOffset) {
246 NSRect currentRect = [[self window] frame]; 282 NSRect currentRect = [[self window] frame];
247 [[self window] setFrame:NSMakeRect(currentRect.origin.x, 283 [[self window] setFrame:NSMakeRect(currentRect.origin.x,
248 currentRect.origin.y - totalOffset, 284 currentRect.origin.y - totalOffset,
249 currentRect.size.width, 285 currentRect.size.width,
250 currentRect.size.height + totalOffset) 286 currentRect.size.height + totalOffset)
251 display:NO]; 287 display:NO];
252 } 288 }
253 } 289 }
254 290
255 - (void)didEndSheet:(NSWindow*)sheet 291 - (void)didEndSheet:(NSWindow*)sheet
256 returnCode:(int)returnCode 292 returnCode:(int)returnCode
257 contextInfo:(void*)contextInfo { 293 contextInfo:(void*)contextInfo {
258 [sheet close]; 294 [sheet close];
259 } 295 }
260 296
261 - (void)windowWillClose:(NSNotification*)notification { 297 - (void)windowWillClose:(NSNotification*)notification {
262 [self autorelease]; 298 [self autorelease];
263 } 299 }
264 300
265 - (bool)isInlineInstall { 301 - (BOOL)isBundleInstall {
302 return prompt_->type() == ExtensionInstallUI::BUNDLE_INSTALL_PROMPT;
303 }
304
305 - (BOOL)isInlineInstall {
266 return prompt_->type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT; 306 return prompt_->type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT;
267 } 307 }
268 308
269 - (void)appendRatingStar:(const SkBitmap*)skiaImage { 309 - (void)appendRatingStar:(const SkBitmap*)skiaImage {
270 NSImage* image = gfx::SkBitmapToNSImageWithColorSpace( 310 NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
271 *skiaImage, base::mac::GetSystemColorSpace()); 311 *skiaImage, base::mac::GetSystemColorSpace());
272 NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height()); 312 NSRect frame = NSMakeRect(0, 0, skiaImage->width(), skiaImage->height());
273 scoped_nsobject<NSImageView> view([[NSImageView alloc] initWithFrame:frame]); 313 scoped_nsobject<NSImageView> view([[NSImageView alloc] initWithFrame:frame]);
274 [view setImage:image]; 314 [view setImage:image];
275 315
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 ExtensionInstallDialogController* controller = 347 ExtensionInstallDialogController* controller =
308 [[ExtensionInstallDialogController alloc] 348 [[ExtensionInstallDialogController alloc]
309 initWithParentWindow:native_window 349 initWithParentWindow:native_window
310 profile:profile 350 profile:profile
311 delegate:delegate 351 delegate:delegate
312 prompt:prompt]; 352 prompt:prompt];
313 353
314 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455) 354 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455)
315 [controller runAsModalSheet]; 355 [controller runAsModalSheet];
316 } 356 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698