OLD | NEW |
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/auto_reset.h" |
7 #include "base/i18n/rtl.h" | 8 #include "base/i18n/rtl.h" |
8 #include "base/mac/bundle_locations.h" | 9 #include "base/mac/bundle_locations.h" |
9 #include "base/mac/mac_util.h" | 10 #include "base/mac/mac_util.h" |
10 #include "base/memory/scoped_nsobject.h" | 11 #include "base/memory/scoped_nsobject.h" |
11 #include "base/string_util.h" | 12 #include "base/string_util.h" |
12 #include "base/sys_string_conversions.h" | 13 #include "base/sys_string_conversions.h" |
13 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
14 #include "chrome/browser/extensions/bundle_installer.h" | 15 #include "chrome/browser/extensions/bundle_installer.h" |
15 #include "chrome/browser/extensions/extension_install_dialog.h" | 16 #include "chrome/browser/extensions/extension_install_dialog.h" |
16 #include "chrome/common/extensions/extension.h" | 17 #include "chrome/common/extensions/extension.h" |
17 #include "content/public/browser/page_navigator.h" | 18 #include "content/public/browser/page_navigator.h" |
18 #include "grit/generated_resources.h" | 19 #include "grit/generated_resources.h" |
19 #include "skia/ext/skia_utils_mac.h" | 20 #include "skia/ext/skia_utils_mac.h" |
20 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | 21 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
21 #include "ui/base/l10n/l10n_util.h" | 22 #include "ui/base/l10n/l10n_util.h" |
22 #include "ui/base/l10n/l10n_util_mac.h" | 23 #include "ui/base/l10n/l10n_util_mac.h" |
23 | 24 |
24 using content::OpenURLParams; | 25 using content::OpenURLParams; |
25 using content::Referrer; | 26 using content::Referrer; |
26 using extensions::BundleInstaller; | 27 using extensions::BundleInstaller; |
27 | 28 |
28 @interface ExtensionInstallDialogController () | 29 @interface ExtensionInstallDialogController () |
29 - (BOOL)isBundleInstall; | 30 - (BOOL)isBundleInstall; |
30 - (BOOL)isInlineInstall; | 31 - (BOOL)isInlineInstall; |
31 - (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage; | 32 - (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage; |
| 33 - (void)onOutlineViewRowCountDidChange; |
| 34 - (NSDictionary*)buildItemWithTitle:(NSString*)title |
| 35 isGroupItem:(BOOL)isGroupItem |
| 36 children:(NSArray*)children; |
| 37 - (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue; |
| 38 - (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt; |
32 @end | 39 @end |
33 | 40 |
34 namespace { | 41 namespace { |
35 | 42 |
36 // Padding above the warnings separator, we must also subtract this when hiding | 43 // Padding above the warnings separator, we must also subtract this when hiding |
37 // it. | 44 // it. |
38 const CGFloat kWarningsSeparatorPadding = 14; | 45 const CGFloat kWarningsSeparatorPadding = 14; |
39 | 46 |
40 // Maximum height we will adjust controls to when trying to accomodate their | 47 // Maximum height we will adjust controls to when trying to accomodate their |
41 // contents. | 48 // contents. |
42 const CGFloat kMaxControlHeight = 400; | 49 const CGFloat kMaxControlHeight = 400; |
43 | 50 |
| 51 NSString* const kTitleKey = @"title"; |
| 52 NSString* const kIsGroupItemKey = @"isGroupItem"; |
| 53 NSString* const kChildrenKey = @"children"; |
| 54 NSString* const kCanExpandKey = @"canExpand"; |
| 55 |
44 // Adjust the |control|'s height so that its content is not clipped. | 56 // Adjust the |control|'s height so that its content is not clipped. |
45 // This also adds the change in height to the |totalOffset| and shifts the | 57 // This also adds the change in height to the |totalOffset| and shifts the |
46 // control down by that amount. | 58 // control down by that amount. |
47 void OffsetControlVerticallyToFitContent(NSControl* control, | 59 void OffsetControlVerticallyToFitContent(NSControl* control, |
48 CGFloat* totalOffset) { | 60 CGFloat* totalOffset) { |
49 // Adjust the control's height so that its content is not clipped. | 61 // Adjust the control's height so that its content is not clipped. |
50 NSRect currentRect = [control frame]; | 62 NSRect currentRect = [control frame]; |
51 NSRect fitRect = currentRect; | 63 NSRect fitRect = currentRect; |
52 fitRect.size.height = kMaxControlHeight; | 64 fitRect.size.height = kMaxControlHeight; |
53 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height; | 65 CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height; |
54 CGFloat offset = desiredHeight - currentRect.size.height; | 66 CGFloat offset = desiredHeight - NSHeight(currentRect); |
55 | 67 |
56 [control setFrameSize:NSMakeSize(currentRect.size.width, | 68 [control setFrameSize:NSMakeSize(NSWidth(currentRect), |
57 currentRect.size.height + offset)]; | 69 NSHeight(currentRect) + offset)]; |
58 | 70 |
59 *totalOffset += offset; | 71 *totalOffset += offset; |
60 | 72 |
61 // Move the control vertically by the new total offset. | 73 // Move the control vertically by the new total offset. |
62 NSPoint origin = [control frame].origin; | 74 NSPoint origin = [control frame].origin; |
63 origin.y -= *totalOffset; | 75 origin.y -= *totalOffset; |
64 [control setFrameOrigin:origin]; | 76 [control setFrameOrigin:origin]; |
65 } | 77 } |
66 | 78 |
| 79 // Gets the desired height of |outlineView|. Simply using the view's frame |
| 80 // doesn't work if an animation is pending. |
| 81 CGFloat GetDesiredOutlineViewHeight(NSOutlineView* outlineView) { |
| 82 CGFloat height = 0; |
| 83 for (NSInteger i = 0; i < [outlineView numberOfRows]; ++i) |
| 84 height += NSHeight([outlineView rectOfRow:i]); |
| 85 return height; |
| 86 } |
| 87 |
| 88 void OffsetOutlineViewVerticallyToFitContent(NSOutlineView* outlineView, |
| 89 CGFloat* totalOffset) { |
| 90 NSScrollView* scrollView = [outlineView enclosingScrollView]; |
| 91 NSRect frame = [scrollView frame]; |
| 92 CGFloat desiredHeight = GetDesiredOutlineViewHeight(outlineView); |
| 93 CGFloat offset = desiredHeight - NSHeight(frame); |
| 94 frame.size.height += offset; |
| 95 |
| 96 *totalOffset += offset; |
| 97 |
| 98 // Move the control vertically by the new total offset. |
| 99 frame.origin.y -= *totalOffset; |
| 100 [scrollView setFrame:frame]; |
| 101 } |
| 102 |
67 void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) { | 103 void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) { |
68 ExtensionInstallDialogController* controller = | 104 ExtensionInstallDialogController* controller = |
69 static_cast<ExtensionInstallDialogController*>(data); | 105 static_cast<ExtensionInstallDialogController*>(data); |
70 [controller appendRatingStar:skiaImage]; | 106 [controller appendRatingStar:skiaImage]; |
71 } | 107 } |
72 | 108 |
| 109 void DrawBulletInFrame(NSRect frame) { |
| 110 NSRect rect; |
| 111 rect.size.width = std::min(NSWidth(frame), NSHeight(frame)) * 0.38; |
| 112 rect.size.height = NSWidth(rect); |
| 113 rect.origin.x = frame.origin.x + (NSWidth(frame) - NSWidth(rect)) / 2.0; |
| 114 rect.origin.y = frame.origin.y + (NSHeight(frame) - NSHeight(rect)) / 2.0; |
| 115 rect = NSIntegralRect(rect); |
| 116 |
| 117 [[NSColor colorWithCalibratedWhite:0.0 alpha:0.42] set]; |
| 118 [[NSBezierPath bezierPathWithOvalInRect:rect] fill]; |
| 119 } |
| 120 |
73 } | 121 } |
74 | 122 |
75 @implementation ExtensionInstallDialogController | 123 @implementation ExtensionInstallDialogController |
76 | 124 |
77 @synthesize iconView = iconView_; | 125 @synthesize iconView = iconView_; |
78 @synthesize titleField = titleField_; | 126 @synthesize titleField = titleField_; |
79 @synthesize itemsField = itemsField_; | 127 @synthesize itemsField = itemsField_; |
80 @synthesize subtitleField = subtitleField_; | |
81 @synthesize warningsField = warningsField_; | |
82 @synthesize cancelButton = cancelButton_; | 128 @synthesize cancelButton = cancelButton_; |
83 @synthesize okButton = okButton_; | 129 @synthesize okButton = okButton_; |
| 130 @synthesize outlineView = outlineView_; |
84 @synthesize warningsSeparator = warningsSeparator_; | 131 @synthesize warningsSeparator = warningsSeparator_; |
85 @synthesize ratingStars = ratingStars_; | 132 @synthesize ratingStars = ratingStars_; |
86 @synthesize ratingCountField = ratingCountField_; | 133 @synthesize ratingCountField = ratingCountField_; |
87 @synthesize userCountField = userCountField_; | 134 @synthesize userCountField = userCountField_; |
88 | 135 |
89 - (id)initWithParentWindow:(NSWindow*)window | 136 - (id)initWithParentWindow:(NSWindow*)window |
90 navigator:(content::PageNavigator*)navigator | 137 navigator:(content::PageNavigator*)navigator |
91 delegate:(ExtensionInstallPrompt::Delegate*)delegate | 138 delegate:(ExtensionInstallPrompt::Delegate*)delegate |
92 prompt:(const ExtensionInstallPrompt::Prompt&)prompt { | 139 prompt:(const ExtensionInstallPrompt::Prompt&)prompt { |
93 NSString* nibpath = nil; | 140 NSString* nibpath = nil; |
| 141 warnings_.reset([[self buildWarnings:prompt] retain]); |
94 | 142 |
95 // We use a different XIB in the case of bundle installs, inline installs or | 143 // We use a different XIB in the case of bundle installs, inline installs or |
96 // no permission warnings. These are laid out nicely for the data they | 144 // no permission warnings. These are laid out nicely for the data they |
97 // display. | 145 // display. |
98 if (prompt.type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT) { | 146 if (prompt.type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT) { |
99 nibpath = [base::mac::FrameworkBundle() | 147 nibpath = [base::mac::FrameworkBundle() |
100 pathForResource:@"ExtensionInstallPromptBundle" | 148 pathForResource:@"ExtensionInstallPromptBundle" |
101 ofType:@"nib"]; | 149 ofType:@"nib"]; |
102 } else if (prompt.type() == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT) { | 150 } else if (prompt.type() == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT) { |
103 nibpath = [base::mac::FrameworkBundle() | 151 nibpath = [base::mac::FrameworkBundle() |
104 pathForResource:@"ExtensionInstallPromptInline" | 152 pathForResource:@"ExtensionInstallPromptInline" |
105 ofType:@"nib"]; | 153 ofType:@"nib"]; |
106 } else if (prompt.GetPermissionCount() == 0) { | 154 } else if (prompt.GetPermissionCount() == 0 && |
| 155 prompt.GetOAuthIssueCount() == 0) { |
107 nibpath = [base::mac::FrameworkBundle() | 156 nibpath = [base::mac::FrameworkBundle() |
108 pathForResource:@"ExtensionInstallPromptNoWarnings" | 157 pathForResource:@"ExtensionInstallPromptNoWarnings" |
109 ofType:@"nib"]; | 158 ofType:@"nib"]; |
110 } else { | 159 } else { |
111 nibpath = [base::mac::FrameworkBundle() | 160 nibpath = [base::mac::FrameworkBundle() |
112 pathForResource:@"ExtensionInstallPrompt" | 161 pathForResource:@"ExtensionInstallPrompt" |
113 ofType:@"nib"]; | 162 ofType:@"nib"]; |
114 } | 163 } |
115 | 164 |
116 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { | 165 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], | 242 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], |
194 -buttonDelta.width, 0)]; | 243 -buttonDelta.width, 0)]; |
195 } | 244 } |
196 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_]; | 245 buttonDelta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_]; |
197 if (buttonDelta.width) { | 246 if (buttonDelta.width) { |
198 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], | 247 [cancelButton_ setFrame:NSOffsetRect([cancelButton_ frame], |
199 -buttonDelta.width, 0)]; | 248 -buttonDelta.width, 0)]; |
200 } | 249 } |
201 | 250 |
202 if ([self isBundleInstall]) { | 251 if ([self isBundleInstall]) { |
203 [subtitleField_ setStringValue:base::SysUTF16ToNSString( | |
204 prompt_->GetPermissionsHeading())]; | |
205 | |
206 // We display the list of extension names as a simple text string, seperated | 252 // We display the list of extension names as a simple text string, seperated |
207 // by newlines. | 253 // by newlines. |
208 BundleInstaller::ItemList items = prompt_->bundle()->GetItemsWithState( | 254 BundleInstaller::ItemList items = prompt_->bundle()->GetItemsWithState( |
209 BundleInstaller::Item::STATE_PENDING); | 255 BundleInstaller::Item::STATE_PENDING); |
210 | 256 |
211 NSMutableString* joinedItems = [NSMutableString string]; | 257 NSMutableString* joinedItems = [NSMutableString string]; |
212 for (size_t i = 0; i < items.size(); ++i) { | 258 for (size_t i = 0; i < items.size(); ++i) { |
213 if (i > 0) | 259 if (i > 0) |
214 [joinedItems appendString:@"\n"]; | 260 [joinedItems appendString:@"\n"]; |
215 [joinedItems appendString:base::SysUTF16ToNSString( | 261 [joinedItems appendString:base::SysUTF16ToNSString( |
216 items[i].GetNameForDisplay())]; | 262 items[i].GetNameForDisplay())]; |
217 } | 263 } |
218 [itemsField_ setStringValue:joinedItems]; | 264 [itemsField_ setStringValue:joinedItems]; |
219 | 265 |
220 // Adjust the controls to fit the list of extensions. | 266 // Adjust the controls to fit the list of extensions. |
221 OffsetControlVerticallyToFitContent(itemsField_, &totalOffset); | 267 OffsetControlVerticallyToFitContent(itemsField_, &totalOffset); |
222 } | 268 } |
223 | 269 |
224 // If there are any warnings, then we have to do some special layout. | 270 // If there are any warnings or OAuth issues, then we have to do some special |
225 if (prompt_->GetPermissionCount() > 0) { | 271 // layout. |
226 [subtitleField_ setStringValue:base::SysUTF16ToNSString( | 272 if (prompt_->GetPermissionCount() > 0 || prompt_->GetOAuthIssueCount() > 0) { |
227 prompt_->GetPermissionsHeading())]; | 273 NSSize spacing = [outlineView_ intercellSpacing]; |
228 | 274 spacing.width += 2; |
229 // We display the permission warnings as a simple text string, separated by | 275 spacing.height += 2; |
230 // newlines. | 276 [outlineView_ setIntercellSpacing:spacing]; |
231 NSMutableString* joinedWarnings = [NSMutableString string]; | 277 [[[[outlineView_ tableColumns] objectAtIndex:0] dataCell] setWraps:YES]; |
232 for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) { | 278 for (id item in warnings_.get()) { |
233 if (i > 0) | 279 if ([[item objectForKey:kIsGroupItemKey] boolValue]) |
234 [joinedWarnings appendString:@"\n"]; | 280 [outlineView_ expandItem:item expandChildren:NO]; |
235 [joinedWarnings appendString:base::SysUTF16ToNSString( | |
236 prompt_->GetPermission(i))]; | |
237 } | 281 } |
238 [warningsField_ setStringValue:joinedWarnings]; | 282 // Adjust the outline view to fit the warnings. |
239 | 283 OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset); |
240 // In the store version of the dialog the icon extends past the one-line | |
241 // version of the permission field. Therefore when increasing the window | |
242 // size for multi-line permissions we don't have to add the full offset, | |
243 // only the part that extends past the icon. | |
244 CGFloat warningsGrowthSlack = 0; | |
245 if (![self isBundleInstall] && | |
246 [warningsField_ frame].origin.y > [iconView_ frame].origin.y) { | |
247 warningsGrowthSlack = | |
248 [warningsField_ frame].origin.y - [iconView_ frame].origin.y; | |
249 } | |
250 | |
251 // Adjust the controls to fit the permission warnings. | |
252 OffsetControlVerticallyToFitContent(subtitleField_, &totalOffset); | |
253 OffsetControlVerticallyToFitContent(warningsField_, &totalOffset); | |
254 | |
255 totalOffset = MAX(totalOffset - warningsGrowthSlack, 0); | |
256 } else if ([self isInlineInstall] || [self isBundleInstall]) { | 284 } else if ([self isInlineInstall] || [self isBundleInstall]) { |
257 // Inline and bundle installs that don't have a permissions section need to | 285 // Inline and bundle installs that don't have a permissions section need to |
258 // hide controls related to that and shrink the window by the space they | 286 // hide controls related to that and shrink the window by the space they |
259 // take up. | 287 // take up. |
260 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame], | 288 NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame], |
261 [subtitleField_ frame]); | 289 [[outlineView_ enclosingScrollView] frame]); |
262 hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]); | |
263 [warningsSeparator_ setHidden:YES]; | 290 [warningsSeparator_ setHidden:YES]; |
264 [subtitleField_ setHidden:YES]; | 291 [[outlineView_ enclosingScrollView] setHidden:YES]; |
265 [warningsField_ setHidden:YES]; | 292 totalOffset -= NSHeight(hiddenRect) + kWarningsSeparatorPadding; |
266 totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding; | |
267 } | 293 } |
268 | 294 |
269 // If necessary, adjust the window size. | 295 // If necessary, adjust the window size. |
270 if (totalOffset) { | 296 if (totalOffset) { |
271 NSRect currentRect = [[self window] frame]; | 297 NSRect currentRect = [[self window] frame]; |
272 [[self window] setFrame:NSMakeRect(currentRect.origin.x, | 298 [[self window] setFrame:NSMakeRect(currentRect.origin.x, |
273 currentRect.origin.y - totalOffset, | 299 currentRect.origin.y - totalOffset, |
274 currentRect.size.width, | 300 NSWidth(currentRect), |
275 currentRect.size.height + totalOffset) | 301 NSHeight(currentRect) + totalOffset) |
276 display:NO]; | 302 display:NO]; |
277 } | 303 } |
278 } | 304 } |
279 | 305 |
280 - (void)didEndSheet:(NSWindow*)sheet | 306 - (void)didEndSheet:(NSWindow*)sheet |
281 returnCode:(int)returnCode | 307 returnCode:(int)returnCode |
282 contextInfo:(void*)contextInfo { | 308 contextInfo:(void*)contextInfo { |
283 [sheet close]; | 309 [sheet close]; |
284 } | 310 } |
285 | 311 |
(...skipping 20 matching lines...) Expand all Loading... |
306 CGFloat maxStarRight = 0; | 332 CGFloat maxStarRight = 0; |
307 if ([[ratingStars_ subviews] count]) { | 333 if ([[ratingStars_ subviews] count]) { |
308 maxStarRight = NSMaxX([[[ratingStars_ subviews] lastObject] frame]); | 334 maxStarRight = NSMaxX([[[ratingStars_ subviews] lastObject] frame]); |
309 } | 335 } |
310 NSRect starBounds = NSMakeRect(maxStarRight, 0, | 336 NSRect starBounds = NSMakeRect(maxStarRight, 0, |
311 skiaImage->width(), skiaImage->height()); | 337 skiaImage->width(), skiaImage->height()); |
312 [view setFrame:starBounds]; | 338 [view setFrame:starBounds]; |
313 [ratingStars_ addSubview:view]; | 339 [ratingStars_ addSubview:view]; |
314 } | 340 } |
315 | 341 |
| 342 - (void)onOutlineViewRowCountDidChange { |
| 343 // Force the outline view to update. |
| 344 [outlineView_ reloadData]; |
| 345 |
| 346 CGFloat totalOffset = 0.0; |
| 347 OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset); |
| 348 if (totalOffset) { |
| 349 NSRect currentRect = [[self window] frame]; |
| 350 currentRect.origin.y -= totalOffset; |
| 351 currentRect.size.height += totalOffset; |
| 352 [[self window] setFrame:currentRect |
| 353 display:YES]; |
| 354 } |
| 355 } |
| 356 |
| 357 - (id)outlineView:(NSOutlineView*)outlineView |
| 358 child:(NSInteger)index |
| 359 ofItem:(id)item { |
| 360 if (!item) |
| 361 return [warnings_ objectAtIndex:index]; |
| 362 if ([item isKindOfClass:[NSDictionary class]]) |
| 363 return [[item objectForKey:kChildrenKey] objectAtIndex:index]; |
| 364 NOTREACHED(); |
| 365 return nil; |
| 366 } |
| 367 |
| 368 - (BOOL)outlineView:(NSOutlineView*)outlineView |
| 369 isItemExpandable:(id)item { |
| 370 return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0; |
| 371 } |
| 372 |
| 373 - (NSInteger)outlineView:(NSOutlineView*)outlineView |
| 374 numberOfChildrenOfItem:(id)item { |
| 375 if (!item) |
| 376 return [warnings_ count]; |
| 377 if ([item isKindOfClass:[NSDictionary class]]) |
| 378 return [[item objectForKey:kChildrenKey] count]; |
| 379 NOTREACHED(); |
| 380 return 0; |
| 381 } |
| 382 |
| 383 - (id)outlineView:(NSOutlineView*)outlineView |
| 384 objectValueForTableColumn:(NSTableColumn *)tableColumn |
| 385 byItem:(id)item { |
| 386 return [item objectForKey:kTitleKey]; |
| 387 } |
| 388 |
| 389 - (BOOL)outlineView:(NSOutlineView *)outlineView |
| 390 shouldExpandItem:(id)item { |
| 391 return [[item objectForKey:kCanExpandKey] boolValue]; |
| 392 } |
| 393 |
| 394 - (void)outlineViewItemDidExpand:sender { |
| 395 // Call via run loop to avoid animation glitches. |
| 396 [self performSelector:@selector(onOutlineViewRowCountDidChange) |
| 397 withObject:nil |
| 398 afterDelay:0]; |
| 399 } |
| 400 |
| 401 - (void)outlineViewItemDidCollapse:sender { |
| 402 // Call via run loop to avoid animation glitches. |
| 403 [self performSelector:@selector(onOutlineViewRowCountDidChange) |
| 404 withObject:nil |
| 405 afterDelay:0]; |
| 406 } |
| 407 |
| 408 - (CGFloat)outlineView:(NSOutlineView *)outlineView |
| 409 heightOfRowByItem:(id)item { |
| 410 // Prevent reentrancy due to the frameOfCellAtColumn:row: call below. |
| 411 if (isComputingRowHeight_) |
| 412 return 1; |
| 413 AutoReset<BOOL> reset(&isComputingRowHeight_, YES); |
| 414 |
| 415 NSCell* cell = [[[outlineView_ tableColumns] objectAtIndex:0] dataCell]; |
| 416 [cell setStringValue:[item objectForKey:kTitleKey]]; |
| 417 NSRect bounds = NSZeroRect; |
| 418 NSInteger row = [outlineView_ rowForItem:item]; |
| 419 bounds.size.width = NSWidth([outlineView_ frameOfCellAtColumn:0 row:row]); |
| 420 bounds.size.height = kMaxControlHeight; |
| 421 |
| 422 return [cell cellSizeForBounds:bounds].height; |
| 423 } |
| 424 |
| 425 - (BOOL)outlineView:(NSOutlineView*)outlineView |
| 426 shouldShowOutlineCellForItem:(id)item { |
| 427 // The top most group header items are always expanded so hide their |
| 428 // disclosure trianggles. |
| 429 return ![[item objectForKey:kIsGroupItemKey] boolValue]; |
| 430 } |
| 431 |
| 432 - (void)outlineView:(NSOutlineView*)outlineView |
| 433 willDisplayCell:(id)cell |
| 434 forTableColumn:(NSTableColumn *)tableColumn |
| 435 item:(id)item { |
| 436 if ([[item objectForKey:kIsGroupItemKey] boolValue]) |
| 437 [cell setFont:[NSFont boldSystemFontOfSize:11.0]]; |
| 438 else |
| 439 [cell setFont:[NSFont systemFontOfSize:11.0]]; |
| 440 } |
| 441 |
| 442 - (void)outlineView:(NSOutlineView *)outlineView |
| 443 willDisplayOutlineCell:(id)cell |
| 444 forTableColumn:(NSTableColumn *)tableColumn |
| 445 item:(id)item { |
| 446 // Replace disclosure triangles with bullet lists for leaf nodes. |
| 447 if (![[item objectForKey:kCanExpandKey] boolValue]) { |
| 448 [cell setImagePosition:NSNoImage]; |
| 449 DrawBulletInFrame([outlineView_ frameOfOutlineCellAtRow: |
| 450 [outlineView_ rowForItem:item]]); |
| 451 } else { |
| 452 // Reset image to default value. |
| 453 [cell setImagePosition:NSImageOverlaps]; |
| 454 } |
| 455 } |
| 456 |
| 457 - (BOOL)outlineView:(NSOutlineView *)outlineView |
| 458 shouldSelectItem:(id)item { |
| 459 return false; |
| 460 } |
| 461 |
| 462 - (NSDictionary*)buildItemWithTitle:(NSString*)title |
| 463 isGroupItem:(BOOL)isGroupItem |
| 464 children:(NSArray*)children { |
| 465 BOOL canExpand = YES; |
| 466 if (!children) { |
| 467 // Add a dummy child even though this is a leaf node. This will cause |
| 468 // the outline view to show a disclosure triangle for this item. |
| 469 // This is later overriden in willDisplayOutlineCell: to draw a bullet |
| 470 // instead. (The bullet could be placed in the title instead but then |
| 471 // the bullet wouldn't line up with disclosure triangles of sibling nodes.) |
| 472 children = [NSArray arrayWithObject:[NSDictionary dictionary]]; |
| 473 canExpand = NO; |
| 474 } |
| 475 |
| 476 return [NSDictionary dictionaryWithObjectsAndKeys: |
| 477 title, kTitleKey, |
| 478 [NSNumber numberWithBool:isGroupItem], kIsGroupItemKey, |
| 479 children, kChildrenKey, |
| 480 [NSNumber numberWithBool:canExpand], kCanExpandKey, |
| 481 nil]; |
| 482 } |
| 483 |
| 484 - (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue { |
| 485 if (issue.details.empty()) { |
| 486 return [self buildItemWithTitle:SysUTF16ToNSString(issue.description) |
| 487 isGroupItem:NO |
| 488 children:nil]; |
| 489 } |
| 490 |
| 491 NSMutableArray* details = [NSMutableArray array]; |
| 492 for (size_t j = 0; j < issue.details.size(); ++j) { |
| 493 [details addObject: |
| 494 [self buildItemWithTitle:SysUTF16ToNSString(issue.details[j]) |
| 495 isGroupItem:NO |
| 496 children:nil]]; |
| 497 } |
| 498 return [self buildItemWithTitle:SysUTF16ToNSString(issue.description) |
| 499 isGroupItem:NO |
| 500 children:details]; |
| 501 } |
| 502 |
| 503 - (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt { |
| 504 NSMutableArray* warnings = [NSMutableArray array]; |
| 505 |
| 506 if (prompt.GetPermissionCount() > 0) { |
| 507 NSMutableArray* children = [NSMutableArray array]; |
| 508 for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) { |
| 509 [children addObject: |
| 510 [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermission(i)) |
| 511 isGroupItem:NO |
| 512 children:nil]]; |
| 513 } |
| 514 [warnings addObject:[self |
| 515 buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermissionsHeading()) |
| 516 isGroupItem:YES |
| 517 children:children]]; |
| 518 } |
| 519 |
| 520 if (prompt.GetOAuthIssueCount() > 0) { |
| 521 NSMutableArray* children = [NSMutableArray array]; |
| 522 for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i) |
| 523 [children addObject:[self buildIssue:prompt.GetOAuthIssue(i)]]; |
| 524 [warnings addObject: |
| 525 [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetOAuthHeading()) |
| 526 isGroupItem:YES |
| 527 children:children]]; |
| 528 } |
| 529 |
| 530 return warnings; |
| 531 } |
| 532 |
| 533 |
316 @end // ExtensionInstallDialogController | 534 @end // ExtensionInstallDialogController |
317 | 535 |
318 void ShowExtensionInstallDialogImpl( | 536 void ShowExtensionInstallDialogImpl( |
319 gfx::NativeWindow parent, | 537 gfx::NativeWindow parent, |
320 content::PageNavigator* navigator, | 538 content::PageNavigator* navigator, |
321 ExtensionInstallPrompt::Delegate* delegate, | 539 ExtensionInstallPrompt::Delegate* delegate, |
322 const ExtensionInstallPrompt::Prompt& prompt) { | 540 const ExtensionInstallPrompt::Prompt& prompt) { |
323 ExtensionInstallDialogController* controller = | 541 ExtensionInstallDialogController* controller = |
324 [[ExtensionInstallDialogController alloc] | 542 [[ExtensionInstallDialogController alloc] |
325 initWithParentWindow:parent | 543 initWithParentWindow:parent |
326 navigator:navigator | 544 navigator:navigator |
327 delegate:delegate | 545 delegate:delegate |
328 prompt:prompt]; | 546 prompt:prompt]; |
329 | 547 |
330 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455) | 548 // TODO(mihaip): Switch this to be tab-modal (http://crbug.com/95455) |
331 [controller runAsModalSheet]; | 549 [controller runAsModalSheet]; |
332 } | 550 } |
OLD | NEW |