| Index: chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm b/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
|
| index 0b6f6f03f8b6c240922da6e39e02ff2e961e8f46..f283bdfea76b128dc7439697d5945397e5ed4547 100644
|
| --- a/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
|
| +++ b/chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.mm
|
| @@ -6,6 +6,7 @@
|
|
|
| #include <algorithm>
|
|
|
| +#include "base/mac/bind_objc_block.h"
|
| #include "base/mac/foundation_util.h"
|
| #include "base/mac/mac_util.h"
|
| #include "base/strings/sys_string_conversions.h"
|
| @@ -20,12 +21,15 @@
|
| #import "chrome/browser/ui/cocoa/info_bubble_view.h"
|
| #import "chrome/browser/ui/cocoa/info_bubble_window.h"
|
| #include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
|
| +#include "chrome/browser/ui/cocoa/website_settings/permission_selector_button.h"
|
| #include "chrome/browser/ui/cocoa/website_settings/split_block_button.h"
|
| #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
|
| #include "chrome/browser/ui/website_settings/permission_bubble_view.h"
|
| +#include "chrome/browser/ui/website_settings/permission_menu_model.h"
|
| #include "content/public/browser/user_metrics.h"
|
| #include "grit/generated_resources.h"
|
| #include "skia/ext/skia_utils_mac.h"
|
| +#import "ui/base/cocoa/menu_controller.h"
|
| #include "ui/base/cocoa/window_size_constants.h"
|
| #import "ui/base/cocoa/menu_controller.h"
|
| #include "ui/base/l10n/l10n_util_mac.h"
|
| @@ -39,9 +43,10 @@ const CGFloat kHorizontalPadding = 20.0f;
|
| const CGFloat kVerticalPadding = 20.0f;
|
| const CGFloat kButtonPadding = 10.0f;
|
| const CGFloat kTitlePaddingX = 50.0f;
|
| -const CGFloat kCheckboxYAdjustment = 2.0f;
|
| +const CGFloat kTitleFontSize = 15.0f;
|
| +const CGFloat kPermissionFontSize = 12.0f;
|
| +const CGFloat kPermissionButtonTitleRightPadding = 4.0f;
|
|
|
| -const CGFloat kFontSize = 15.0f;
|
| class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| public:
|
| explicit MenuDelegate(PermissionBubbleController* bubble)
|
| @@ -50,10 +55,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| return false;
|
| }
|
| virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
|
| - // TODO(leng): Change this to true once setting the bubble to be
|
| - // customizable works properly. Ideally, the bubble will alter its
|
| - // contents, rather than reshowing completely.
|
| - return false;
|
| + return true;
|
| }
|
| virtual bool GetAcceleratorForCommandId(
|
| int command_id,
|
| @@ -70,6 +72,65 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
|
|
| } // namespace
|
|
|
| +// NSPopUpButton with a menu containing two items: allow and block.
|
| +// One AllowBlockMenuButton is used for each requested permission, but only when
|
| +// the permission bubble is in 'customize' mode.
|
| +@interface AllowBlockMenuButton : NSPopUpButton {
|
| + @private
|
| + scoped_ptr<PermissionMenuModel> menuModel_;
|
| + base::scoped_nsobject<MenuController> menuController_;
|
| +}
|
| +
|
| +- (id)initForURL:(const GURL&)url
|
| + allowed:(BOOL)allow
|
| + index:(int)index
|
| + delegate:(PermissionBubbleView::Delegate*)delegate;
|
| +@end
|
| +
|
| +@implementation AllowBlockMenuButton
|
| +
|
| +- (id)initForURL:(const GURL&)url
|
| + allowed:(BOOL)allow
|
| + index:(int)index
|
| + delegate:(PermissionBubbleView::Delegate*)delegate {
|
| + if (self = [super initWithFrame:NSZeroRect pullsDown:NO]) {
|
| + ContentSetting setting =
|
| + allow ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
|
| + [self setFont:[NSFont systemFontOfSize:kPermissionFontSize]];
|
| + [self setBordered:NO];
|
| +
|
| + __block PermissionBubbleView::Delegate* blockDelegate = delegate;
|
| + PermissionMenuModel::ChangeCallback changeCallback =
|
| + base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) {
|
| + blockDelegate->ToggleAccept(
|
| + index, permission.setting == CONTENT_SETTING_ALLOW);
|
| + });
|
| +
|
| + menuModel_.reset(new PermissionMenuModel(url, setting, changeCallback));
|
| + menuController_.reset([[MenuController alloc] initWithModel:menuModel_.get()
|
| + useWithPopUpButtonCell:NO]);
|
| + [self setMenu:[menuController_ menu]];
|
| + [self selectItemAtIndex:menuModel_->GetIndexOfCommandId(setting)];
|
| + [self sizeToFit];
|
| + // Adjust the size to fit the current title. Using only -sizeToFit leaves
|
| + // an ugly amount of whitespace between the title and the arrows because it
|
| + // will fit to the largest element in the menu, not just the selected item.
|
| + // TODO(leng): This was copied from PermissionSelectorButton. Move to a
|
| + // shared location, so that the code is not duplicated.
|
| + NSDictionary* textAttributes =
|
| + [[self attributedTitle] attributesAtIndex:0 effectiveRange:NULL];
|
| + NSSize titleSize = [[self title] sizeWithAttributes:textAttributes];
|
| + NSRect frame = [self frame];
|
| + NSRect titleRect = [[self cell] titleRectForBounds:frame];
|
| + CGFloat width = titleSize.width + NSWidth(frame) - NSWidth(titleRect);
|
| + [self setFrameSize:NSMakeSize(width + kPermissionButtonTitleRightPadding,
|
| + NSHeight(frame))];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +@end
|
| +
|
| @interface PermissionBubbleController ()
|
|
|
| // Returns an autoreleased NSView displaying the icon and label for |request|.
|
| @@ -79,10 +140,11 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| // requesting settings for |host|.
|
| - (NSView*)titleWithHostname:(const std::string&)host;
|
|
|
| -// Returns an autoreleased NSView displaying a checkbox for |request|. The
|
| -// checkbox will be initialized as checked if |checked| is YES.
|
| -- (NSView*)checkboxForRequest:(PermissionBubbleRequest*)request
|
| - checked:(BOOL)checked;
|
| +// Returns an autoreleased NSView displaying a menu for |request|. The
|
| +// menu will be initialized as 'allow' if |allow| is YES.
|
| +- (NSView*)menuForRequest:(PermissionBubbleRequest*)request
|
| + atIndex:(int)index
|
| + allow:(BOOL)allow;
|
|
|
| // Returns an autoreleased NSView of a button with |title| and |action|.
|
| - (NSView*)buttonWithTitle:(NSString*)title
|
| @@ -98,10 +160,6 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| // Returns an autoreleased NSView displaying the close 'x' button.
|
| - (NSView*)closeButton;
|
|
|
| -// Sets the width of both |viewA| and |viewB| to be the larger of the
|
| -// two views' widths. Does not change either view's origin or height.
|
| -- (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB;
|
| -
|
| // Called when the 'ok' button is pressed.
|
| - (void)ok:(id)sender;
|
|
|
| @@ -117,8 +175,13 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| // Called when the 'customize' button is pressed.
|
| - (void)onCustomize:(id)sender;
|
|
|
| -// Called when a checkbox changes from checked to unchecked, or vice versa.
|
| -- (void)onCheckboxChanged:(id)sender;
|
| +// Sets the width of both |viewA| and |viewB| to be the larger of the
|
| +// two views' widths. Does not change either view's origin or height.
|
| ++ (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB;
|
| +
|
| +// Sets the offset of |viewA| so that its vertical center is aligned with the
|
| +// vertical center of |viewB|.
|
| ++ (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB;
|
|
|
| @end
|
|
|
| @@ -176,25 +239,32 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]);
|
| BOOL singlePermission = requests.size() == 1;
|
|
|
| - checkboxes_.reset(customizationMode ? [[NSMutableArray alloc] init] : nil);
|
| + base::scoped_nsobject<NSMutableArray> permissionMenus;
|
| + if (customizationMode)
|
| + permissionMenus.reset([[NSMutableArray alloc] init]);
|
| +
|
| for (auto it = requests.begin(); it != requests.end(); it++) {
|
| - base::scoped_nsobject<NSView> permissionView;
|
| - if (customizationMode) {
|
| - int index = it - requests.begin();
|
| - permissionView.reset(
|
| - [[self checkboxForRequest:(*it)
|
| - checked:acceptStates[index] ? YES : NO] retain]);
|
| - [base::mac::ObjCCastStrict<NSButton>(permissionView) setTag:index];
|
| - [checkboxes_ addObject:permissionView];
|
| - } else {
|
| - permissionView.reset([[self labelForRequest:(*it)] retain]);
|
| - }
|
| + base::scoped_nsobject<NSView> permissionView(
|
| + [[self labelForRequest:(*it)] retain]);
|
| NSPoint origin = [permissionView frame].origin;
|
| origin.x += kHorizontalPadding;
|
| origin.y += yOffset;
|
| [permissionView setFrameOrigin:origin];
|
| [contentView addSubview:permissionView];
|
|
|
| + if (customizationMode) {
|
| + int index = it - requests.begin();
|
| + base::scoped_nsobject<NSView> menu(
|
| + [[self menuForRequest:(*it)
|
| + atIndex:index
|
| + allow:acceptStates[index] ? YES : NO] retain]);
|
| + // Align vertically. Horizontal alignment will be adjusted once the
|
| + // widest permission is know.
|
| + [PermissionBubbleController alignCenterOf:menu
|
| + verticallyToCenterOf:permissionView];
|
| + [permissionMenus addObject:menu];
|
| + [contentView addSubview:menu];
|
| + }
|
| yOffset += NSHeight([permissionView frame]);
|
| }
|
|
|
| @@ -207,6 +277,18 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| bubbleFrame, NSInsetRect([view frame], -kHorizontalPadding, 0));
|
| }
|
|
|
| + if (customizationMode) {
|
| + // Adjust the horizontal origin for each menu.
|
| + CGFloat xOffset = NSWidth(bubbleFrame) - kHorizontalPadding;
|
| + CGFloat maxMenuWidth = 0;
|
| + for (NSView* view in permissionMenus.get()) {
|
| + [view setFrameOrigin:NSMakePoint(xOffset, NSMinY([view frame]))];
|
| + maxMenuWidth = std::max(maxMenuWidth, NSWidth([view frame]));
|
| + }
|
| + // And add the menu width to the bubble's width.
|
| + bubbleFrame.size.width += maxMenuWidth;
|
| + }
|
| +
|
| base::scoped_nsobject<NSView> titleView(
|
| [[self titleWithHostname:requests[0]->GetRequestingHostname().host()]
|
| retain]);
|
| @@ -242,7 +324,8 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| blockButton.reset([[self blockButton] retain]);
|
| else
|
| blockButton.reset([[self blockButtonWithCustomizeMenu] retain]);
|
| - CGFloat width = [self matchWidthsOf:blockButton andOf:allowOrOkButton];
|
| + CGFloat width = [PermissionBubbleController matchWidthsOf:blockButton
|
| + andOf:allowOrOkButton];
|
| // Ensure the allow/ok button is still in the correct position.
|
| xOrigin = NSWidth(bubbleFrame) - width - kHorizontalPadding;
|
| [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)];
|
| @@ -290,6 +373,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| [permissionLabel setBezeled:NO];
|
| [permissionLabel setEditable:NO];
|
| [permissionLabel setSelectable:NO];
|
| + [permissionLabel setFont:[NSFont systemFontOfSize:kPermissionFontSize]];
|
| [permissionLabel setStringValue:base::SysUTF16ToNSString(label)];
|
| [permissionLabel sizeToFit];
|
| [permissionLabel setFrameOrigin:
|
| @@ -326,7 +410,7 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| [titleView setStringValue:
|
| l10n_util::GetNSStringF(IDS_PERMISSIONS_BUBBLE_PROMPT,
|
| base::UTF8ToUTF16(host))];
|
| - [titleView setFont:[NSFont systemFontOfSize:kFontSize]];
|
| + [titleView setFont:[NSFont systemFontOfSize:kTitleFontSize]];
|
| [titleView sizeToFit];
|
| NSRect titleFrame = [titleView frame];
|
| [titleView setFrameSize:NSMakeSize(NSWidth(titleFrame) + kTitlePaddingX,
|
| @@ -334,20 +418,17 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| return titleView.autorelease();
|
| }
|
|
|
| -- (NSView*)checkboxForRequest:(PermissionBubbleRequest*)request
|
| - checked:(BOOL)checked {
|
| +- (NSView*)menuForRequest:(PermissionBubbleRequest*)request
|
| + atIndex:(int)index
|
| + allow:(BOOL)allow {
|
| DCHECK(request);
|
| - base::scoped_nsobject<NSButton> checkbox(
|
| - [[NSButton alloc] initWithFrame:NSZeroRect]);
|
| - [checkbox setButtonType:NSSwitchButton];
|
| - base::string16 permission = request->GetMessageTextFragment();
|
| - [checkbox setTitle:base::SysUTF16ToNSString(permission)];
|
| - [checkbox setState:(checked ? NSOnState : NSOffState)];
|
| - [checkbox setTarget:self];
|
| - [checkbox setAction:@selector(onCheckboxChanged:)];
|
| - [checkbox sizeToFit];
|
| - [checkbox setFrameOrigin:NSMakePoint(0, kCheckboxYAdjustment)];
|
| - return checkbox.autorelease();
|
| + DCHECK(delegate_);
|
| + base::scoped_nsobject<AllowBlockMenuButton> button(
|
| + [[AllowBlockMenuButton alloc] initForURL:request->GetRequestingHostname()
|
| + allowed:allow
|
| + index:index
|
| + delegate:delegate_]);
|
| + return button.autorelease();
|
| }
|
|
|
| - (NSView*)buttonWithTitle:(NSString*)title
|
| @@ -389,15 +470,6 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| return button.autorelease();
|
| }
|
|
|
| -- (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB {
|
| - NSRect frameA = [viewA frame];
|
| - NSRect frameB = [viewB frame];
|
| - CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB));
|
| - [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))];
|
| - [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))];
|
| - return width;
|
| -}
|
| -
|
| - (void)ok:(id)sender {
|
| DCHECK(delegate_);
|
| delegate_->Closing();
|
| @@ -423,15 +495,26 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate {
|
| delegate_->SetCustomizationMode();
|
| }
|
|
|
| -- (void)onCheckboxChanged:(id)sender {
|
| - DCHECK(delegate_);
|
| - NSButton* checkbox = base::mac::ObjCCastStrict<NSButton>(sender);
|
| - delegate_->ToggleAccept([checkbox tag], [checkbox state] == NSOnState);
|
| -}
|
| -
|
| - (void)onMenuItemClicked:(int)commandId {
|
| DCHECK(commandId == 0);
|
| [self onCustomize:nil];
|
| }
|
|
|
| ++ (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB {
|
| + NSRect frameA = [viewA frame];
|
| + NSRect frameB = [viewB frame];
|
| + CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB));
|
| + [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))];
|
| + [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))];
|
| + return width;
|
| +}
|
| +
|
| ++ (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB {
|
| + NSRect frameA = [viewA frame];
|
| + NSRect frameB = [viewB frame];
|
| + frameA.origin.y =
|
| + NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2);
|
| + [viewA setFrameOrigin:frameA.origin];
|
| +}
|
| +
|
| @end // implementation PermissionBubbleController
|
|
|