| Index: chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
|
| index a13a2d94bc3400e29ee3687f714763b4f6a4b92a..b3509b1d05cc794f785bc96ab158de4ca6658d1a 100644
|
| --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
|
| +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
|
| @@ -4,6 +4,7 @@
|
|
|
| #import "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h"
|
|
|
| +#include "base/auto_reset.h"
|
| #include "base/i18n/rtl.h"
|
| #include "base/mac/bundle_locations.h"
|
| #include "base/mac/mac_util.h"
|
| @@ -29,6 +30,12 @@ using extensions::BundleInstaller;
|
| - (BOOL)isBundleInstall;
|
| - (BOOL)isInlineInstall;
|
| - (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage;
|
| +- (void)onOutlineViewRowCountDidChange;
|
| +- (NSDictionary*)buildItemWithTitle:(NSString*)title
|
| + isGroupItem:(BOOL)isGroupItem
|
| + children:(NSArray*)children;
|
| +- (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue;
|
| +- (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt;
|
| @end
|
|
|
| namespace {
|
| @@ -41,6 +48,11 @@ const CGFloat kWarningsSeparatorPadding = 14;
|
| // contents.
|
| const CGFloat kMaxControlHeight = 400;
|
|
|
| +NSString* const kTitleKey = @"title";
|
| +NSString* const kIsGroupItemKey = @"isGroupItem";
|
| +NSString* const kChildrenKey = @"children";
|
| +NSString* const kCanExpandKey = @"canExpand";
|
| +
|
| // Adjust the |control|'s height so that its content is not clipped.
|
| // This also adds the change in height to the |totalOffset| and shifts the
|
| // control down by that amount.
|
| @@ -51,10 +63,10 @@ void OffsetControlVerticallyToFitContent(NSControl* control,
|
| NSRect fitRect = currentRect;
|
| fitRect.size.height = kMaxControlHeight;
|
| CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height;
|
| - CGFloat offset = desiredHeight - currentRect.size.height;
|
| + CGFloat offset = desiredHeight - NSHeight(currentRect);
|
|
|
| - [control setFrameSize:NSMakeSize(currentRect.size.width,
|
| - currentRect.size.height + offset)];
|
| + [control setFrameSize:NSMakeSize(NSWidth(currentRect),
|
| + NSHeight(currentRect) + offset)];
|
|
|
| *totalOffset += offset;
|
|
|
| @@ -64,12 +76,48 @@ void OffsetControlVerticallyToFitContent(NSControl* control,
|
| [control setFrameOrigin:origin];
|
| }
|
|
|
| +// Gets the desired height of |outlineView|. Simply using the view's frame
|
| +// doesn't work if an animation is pending.
|
| +CGFloat GetDesiredOutlineViewHeight(NSOutlineView* outlineView) {
|
| + CGFloat height = 0;
|
| + for (NSInteger i = 0; i < [outlineView numberOfRows]; ++i)
|
| + height += NSHeight([outlineView rectOfRow:i]);
|
| + return height;
|
| +}
|
| +
|
| +void OffsetOutlineViewVerticallyToFitContent(NSOutlineView* outlineView,
|
| + CGFloat* totalOffset) {
|
| + NSScrollView* scrollView = [outlineView enclosingScrollView];
|
| + NSRect frame = [scrollView frame];
|
| + CGFloat desiredHeight = GetDesiredOutlineViewHeight(outlineView);
|
| + CGFloat offset = desiredHeight - NSHeight(frame);
|
| + frame.size.height += offset;
|
| +
|
| + *totalOffset += offset;
|
| +
|
| + // Move the control vertically by the new total offset.
|
| + frame.origin.y -= *totalOffset;
|
| + [scrollView setFrame:frame];
|
| +}
|
| +
|
| void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| ExtensionInstallDialogController* controller =
|
| static_cast<ExtensionInstallDialogController*>(data);
|
| [controller appendRatingStar:skiaImage];
|
| }
|
|
|
| +void DrawBulletInFrame(NSRect frame) {
|
| + NSRect rect;
|
| + rect.size.width = std::min(NSWidth(frame), NSHeight(frame)) * 0.38;
|
| + rect.size.height = NSWidth(rect);
|
| + rect.origin.x = frame.origin.x + (NSWidth(frame) - NSWidth(rect)) / 2.0;
|
| + rect.origin.y = frame.origin.y + (NSHeight(frame) - NSHeight(rect)) / 2.0;
|
| + rect = NSIntegralRect(rect);
|
| +
|
| + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.42] set];
|
| + [[NSBezierPath bezierPathWithOvalInRect:rect] fill];
|
| +}
|
| +
|
| }
|
|
|
| @implementation ExtensionInstallDialogController
|
| @@ -77,10 +125,9 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| @synthesize iconView = iconView_;
|
| @synthesize titleField = titleField_;
|
| @synthesize itemsField = itemsField_;
|
| -@synthesize subtitleField = subtitleField_;
|
| -@synthesize warningsField = warningsField_;
|
| @synthesize cancelButton = cancelButton_;
|
| @synthesize okButton = okButton_;
|
| +@synthesize outlineView = outlineView_;
|
| @synthesize warningsSeparator = warningsSeparator_;
|
| @synthesize ratingStars = ratingStars_;
|
| @synthesize ratingCountField = ratingCountField_;
|
| @@ -91,6 +138,7 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| delegate:(ExtensionInstallPrompt::Delegate*)delegate
|
| prompt:(const ExtensionInstallPrompt::Prompt&)prompt {
|
| NSString* nibpath = nil;
|
| + warnings_.reset([[self buildWarnings:prompt] retain]);
|
|
|
| // We use a different XIB in the case of bundle installs, inline installs or
|
| // no permission warnings. These are laid out nicely for the data they
|
| @@ -103,7 +151,8 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| nibpath = [base::mac::FrameworkBundle()
|
| pathForResource:@"ExtensionInstallPromptInline"
|
| ofType:@"nib"];
|
| - } else if (prompt.GetPermissionCount() == 0) {
|
| + } else if (prompt.GetPermissionCount() == 0 &&
|
| + prompt.GetOAuthIssueCount() == 0) {
|
| nibpath = [base::mac::FrameworkBundle()
|
| pathForResource:@"ExtensionInstallPromptNoWarnings"
|
| ofType:@"nib"];
|
| @@ -200,9 +249,6 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| }
|
|
|
| if ([self isBundleInstall]) {
|
| - [subtitleField_ setStringValue:base::SysUTF16ToNSString(
|
| - prompt_->GetPermissionsHeading())];
|
| -
|
| // We display the list of extension names as a simple text string, seperated
|
| // by newlines.
|
| BundleInstaller::ItemList items = prompt_->bundle()->GetItemsWithState(
|
| @@ -221,49 +267,29 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| OffsetControlVerticallyToFitContent(itemsField_, &totalOffset);
|
| }
|
|
|
| - // If there are any warnings, then we have to do some special layout.
|
| - if (prompt_->GetPermissionCount() > 0) {
|
| - [subtitleField_ setStringValue:base::SysUTF16ToNSString(
|
| - prompt_->GetPermissionsHeading())];
|
| -
|
| - // We display the permission warnings as a simple text string, separated by
|
| - // newlines.
|
| - NSMutableString* joinedWarnings = [NSMutableString string];
|
| - for (size_t i = 0; i < prompt_->GetPermissionCount(); ++i) {
|
| - if (i > 0)
|
| - [joinedWarnings appendString:@"\n"];
|
| - [joinedWarnings appendString:base::SysUTF16ToNSString(
|
| - prompt_->GetPermission(i))];
|
| + // If there are any warnings or OAuth issues, then we have to do some special
|
| + // layout.
|
| + if (prompt_->GetPermissionCount() > 0 || prompt_->GetOAuthIssueCount() > 0) {
|
| + NSSize spacing = [outlineView_ intercellSpacing];
|
| + spacing.width += 2;
|
| + spacing.height += 2;
|
| + [outlineView_ setIntercellSpacing:spacing];
|
| + [[[[outlineView_ tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
|
| + for (id item in warnings_.get()) {
|
| + if ([[item objectForKey:kIsGroupItemKey] boolValue])
|
| + [outlineView_ expandItem:item expandChildren:NO];
|
| }
|
| - [warningsField_ setStringValue:joinedWarnings];
|
| -
|
| - // In the store version of the dialog the icon extends past the one-line
|
| - // version of the permission field. Therefore when increasing the window
|
| - // size for multi-line permissions we don't have to add the full offset,
|
| - // only the part that extends past the icon.
|
| - CGFloat warningsGrowthSlack = 0;
|
| - if (![self isBundleInstall] &&
|
| - [warningsField_ frame].origin.y > [iconView_ frame].origin.y) {
|
| - warningsGrowthSlack =
|
| - [warningsField_ frame].origin.y - [iconView_ frame].origin.y;
|
| - }
|
| -
|
| - // Adjust the controls to fit the permission warnings.
|
| - OffsetControlVerticallyToFitContent(subtitleField_, &totalOffset);
|
| - OffsetControlVerticallyToFitContent(warningsField_, &totalOffset);
|
| -
|
| - totalOffset = MAX(totalOffset - warningsGrowthSlack, 0);
|
| + // Adjust the outline view to fit the warnings.
|
| + OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset);
|
| } else if ([self isInlineInstall] || [self isBundleInstall]) {
|
| // Inline and bundle installs that don't have a permissions section need to
|
| // hide controls related to that and shrink the window by the space they
|
| // take up.
|
| NSRect hiddenRect = NSUnionRect([warningsSeparator_ frame],
|
| - [subtitleField_ frame]);
|
| - hiddenRect = NSUnionRect(hiddenRect, [warningsField_ frame]);
|
| + [[outlineView_ enclosingScrollView] frame]);
|
| [warningsSeparator_ setHidden:YES];
|
| - [subtitleField_ setHidden:YES];
|
| - [warningsField_ setHidden:YES];
|
| - totalOffset -= hiddenRect.size.height + kWarningsSeparatorPadding;
|
| + [[outlineView_ enclosingScrollView] setHidden:YES];
|
| + totalOffset -= NSHeight(hiddenRect) + kWarningsSeparatorPadding;
|
| }
|
|
|
| // If necessary, adjust the window size.
|
| @@ -271,8 +297,8 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| NSRect currentRect = [[self window] frame];
|
| [[self window] setFrame:NSMakeRect(currentRect.origin.x,
|
| currentRect.origin.y - totalOffset,
|
| - currentRect.size.width,
|
| - currentRect.size.height + totalOffset)
|
| + NSWidth(currentRect),
|
| + NSHeight(currentRect) + totalOffset)
|
| display:NO];
|
| }
|
| }
|
| @@ -313,6 +339,198 @@ void AppendRatingStarsShim(const gfx::ImageSkia* skiaImage, void* data) {
|
| [ratingStars_ addSubview:view];
|
| }
|
|
|
| +- (void)onOutlineViewRowCountDidChange {
|
| + // Force the outline view to update.
|
| + [outlineView_ reloadData];
|
| +
|
| + CGFloat totalOffset = 0.0;
|
| + OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset);
|
| + if (totalOffset) {
|
| + NSRect currentRect = [[self window] frame];
|
| + currentRect.origin.y -= totalOffset;
|
| + currentRect.size.height += totalOffset;
|
| + [[self window] setFrame:currentRect
|
| + display:YES];
|
| + }
|
| +}
|
| +
|
| +- (id)outlineView:(NSOutlineView*)outlineView
|
| + child:(NSInteger)index
|
| + ofItem:(id)item {
|
| + if (!item)
|
| + return [warnings_ objectAtIndex:index];
|
| + if ([item isKindOfClass:[NSDictionary class]])
|
| + return [[item objectForKey:kChildrenKey] objectAtIndex:index];
|
| + NOTREACHED();
|
| + return nil;
|
| +}
|
| +
|
| +- (BOOL)outlineView:(NSOutlineView*)outlineView
|
| + isItemExpandable:(id)item {
|
| + return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0;
|
| +}
|
| +
|
| +- (NSInteger)outlineView:(NSOutlineView*)outlineView
|
| + numberOfChildrenOfItem:(id)item {
|
| + if (!item)
|
| + return [warnings_ count];
|
| + if ([item isKindOfClass:[NSDictionary class]])
|
| + return [[item objectForKey:kChildrenKey] count];
|
| + NOTREACHED();
|
| + return 0;
|
| +}
|
| +
|
| +- (id)outlineView:(NSOutlineView*)outlineView
|
| + objectValueForTableColumn:(NSTableColumn *)tableColumn
|
| + byItem:(id)item {
|
| + return [item objectForKey:kTitleKey];
|
| +}
|
| +
|
| +- (BOOL)outlineView:(NSOutlineView *)outlineView
|
| + shouldExpandItem:(id)item {
|
| + return [[item objectForKey:kCanExpandKey] boolValue];
|
| +}
|
| +
|
| +- (void)outlineViewItemDidExpand:sender {
|
| + // Call via run loop to avoid animation glitches.
|
| + [self performSelector:@selector(onOutlineViewRowCountDidChange)
|
| + withObject:nil
|
| + afterDelay:0];
|
| +}
|
| +
|
| +- (void)outlineViewItemDidCollapse:sender {
|
| + // Call via run loop to avoid animation glitches.
|
| + [self performSelector:@selector(onOutlineViewRowCountDidChange)
|
| + withObject:nil
|
| + afterDelay:0];
|
| +}
|
| +
|
| +- (CGFloat)outlineView:(NSOutlineView *)outlineView
|
| + heightOfRowByItem:(id)item {
|
| + // Prevent reentrancy due to the frameOfCellAtColumn:row: call below.
|
| + if (isComputingRowHeight_)
|
| + return 1;
|
| + AutoReset<BOOL> reset(&isComputingRowHeight_, YES);
|
| +
|
| + NSCell* cell = [[[outlineView_ tableColumns] objectAtIndex:0] dataCell];
|
| + [cell setStringValue:[item objectForKey:kTitleKey]];
|
| + NSRect bounds = NSZeroRect;
|
| + NSInteger row = [outlineView_ rowForItem:item];
|
| + bounds.size.width = NSWidth([outlineView_ frameOfCellAtColumn:0 row:row]);
|
| + bounds.size.height = kMaxControlHeight;
|
| +
|
| + return [cell cellSizeForBounds:bounds].height;
|
| +}
|
| +
|
| +- (BOOL)outlineView:(NSOutlineView*)outlineView
|
| + shouldShowOutlineCellForItem:(id)item {
|
| + // The top most group header items are always expanded so hide their
|
| + // disclosure trianggles.
|
| + return ![[item objectForKey:kIsGroupItemKey] boolValue];
|
| +}
|
| +
|
| +- (void)outlineView:(NSOutlineView*)outlineView
|
| + willDisplayCell:(id)cell
|
| + forTableColumn:(NSTableColumn *)tableColumn
|
| + item:(id)item {
|
| + if ([[item objectForKey:kIsGroupItemKey] boolValue])
|
| + [cell setFont:[NSFont boldSystemFontOfSize:11.0]];
|
| + else
|
| + [cell setFont:[NSFont systemFontOfSize:11.0]];
|
| +}
|
| +
|
| +- (void)outlineView:(NSOutlineView *)outlineView
|
| + willDisplayOutlineCell:(id)cell
|
| + forTableColumn:(NSTableColumn *)tableColumn
|
| + item:(id)item {
|
| + // Replace disclosure triangles with bullet lists for leaf nodes.
|
| + if (![[item objectForKey:kCanExpandKey] boolValue]) {
|
| + [cell setImagePosition:NSNoImage];
|
| + DrawBulletInFrame([outlineView_ frameOfOutlineCellAtRow:
|
| + [outlineView_ rowForItem:item]]);
|
| + } else {
|
| + // Reset image to default value.
|
| + [cell setImagePosition:NSImageOverlaps];
|
| + }
|
| +}
|
| +
|
| +- (BOOL)outlineView:(NSOutlineView *)outlineView
|
| + shouldSelectItem:(id)item {
|
| + return false;
|
| +}
|
| +
|
| +- (NSDictionary*)buildItemWithTitle:(NSString*)title
|
| + isGroupItem:(BOOL)isGroupItem
|
| + children:(NSArray*)children {
|
| + BOOL canExpand = YES;
|
| + if (!children) {
|
| + // Add a dummy child even though this is a leaf node. This will cause
|
| + // the outline view to show a disclosure triangle for this item.
|
| + // This is later overriden in willDisplayOutlineCell: to draw a bullet
|
| + // instead. (The bullet could be placed in the title instead but then
|
| + // the bullet wouldn't line up with disclosure triangles of sibling nodes.)
|
| + children = [NSArray arrayWithObject:[NSDictionary dictionary]];
|
| + canExpand = NO;
|
| + }
|
| +
|
| + return [NSDictionary dictionaryWithObjectsAndKeys:
|
| + title, kTitleKey,
|
| + [NSNumber numberWithBool:isGroupItem], kIsGroupItemKey,
|
| + children, kChildrenKey,
|
| + [NSNumber numberWithBool:canExpand], kCanExpandKey,
|
| + nil];
|
| +}
|
| +
|
| +- (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue {
|
| + if (issue.details.empty()) {
|
| + return [self buildItemWithTitle:SysUTF16ToNSString(issue.description)
|
| + isGroupItem:NO
|
| + children:nil];
|
| + }
|
| +
|
| + NSMutableArray* details = [NSMutableArray array];
|
| + for (size_t j = 0; j < issue.details.size(); ++j) {
|
| + [details addObject:
|
| + [self buildItemWithTitle:SysUTF16ToNSString(issue.details[j])
|
| + isGroupItem:NO
|
| + children:nil]];
|
| + }
|
| + return [self buildItemWithTitle:SysUTF16ToNSString(issue.description)
|
| + isGroupItem:NO
|
| + children:details];
|
| +}
|
| +
|
| +- (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt {
|
| + NSMutableArray* warnings = [NSMutableArray array];
|
| +
|
| + if (prompt.GetPermissionCount() > 0) {
|
| + NSMutableArray* children = [NSMutableArray array];
|
| + for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) {
|
| + [children addObject:
|
| + [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermission(i))
|
| + isGroupItem:NO
|
| + children:nil]];
|
| + }
|
| + [warnings addObject:[self
|
| + buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermissionsHeading())
|
| + isGroupItem:YES
|
| + children:children]];
|
| + }
|
| +
|
| + if (prompt.GetOAuthIssueCount() > 0) {
|
| + NSMutableArray* children = [NSMutableArray array];
|
| + for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i)
|
| + [children addObject:[self buildIssue:prompt.GetOAuthIssue(i)]];
|
| + [warnings addObject:
|
| + [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetOAuthHeading())
|
| + isGroupItem:YES
|
| + children:children]];
|
| + }
|
| +
|
| + return warnings;
|
| +}
|
| +
|
| +
|
| @end // ExtensionInstallDialogController
|
|
|
| void ShowExtensionInstallDialogImpl(
|
|
|