OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_controller.h
" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/mac/bind_objc_block.h" | |
10 #include "base/macros.h" | |
11 #include "base/strings/sys_string_conversions.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "chrome/browser/permissions/permission_request.h" | |
14 #include "chrome/browser/ui/browser.h" | |
15 #include "chrome/browser/ui/browser_finder.h" | |
16 #include "chrome/browser/ui/browser_window.h" | |
17 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | |
18 #import "chrome/browser/ui/cocoa/browser_window_utils.h" | |
19 #import "chrome/browser/ui/cocoa/chrome_style.h" | |
20 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" | |
21 #import "chrome/browser/ui/cocoa/hover_close_button.h" | |
22 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | |
23 #import "chrome/browser/ui/cocoa/info_bubble_window.h" | |
24 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | |
25 #include "chrome/browser/ui/cocoa/page_info/permission_selector_button.h" | |
26 #include "chrome/browser/ui/cocoa/page_info/split_block_button.h" | |
27 #include "chrome/browser/ui/cocoa/page_info/website_settings_utils_cocoa.h" | |
28 #include "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h" | |
29 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" | |
30 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" | |
31 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" | |
32 #include "chrome/browser/ui/page_info/permission_menu_model.h" | |
33 #include "chrome/browser/ui/website_settings/permission_prompt.h" | |
34 #include "chrome/common/pref_names.h" | |
35 #include "chrome/grit/generated_resources.h" | |
36 #include "components/prefs/pref_service.h" | |
37 #include "components/strings/grit/components_strings.h" | |
38 #include "components/url_formatter/elide_url.h" | |
39 #include "content/public/browser/native_web_keyboard_event.h" | |
40 #include "content/public/browser/user_metrics.h" | |
41 #include "skia/ext/skia_utils_mac.h" | |
42 #include "ui/base/cocoa/cocoa_base_utils.h" | |
43 #import "ui/base/cocoa/controls/hyperlink_text_view.h" | |
44 #import "ui/base/cocoa/menu_controller.h" | |
45 #include "ui/base/cocoa/window_size_constants.h" | |
46 #include "ui/base/l10n/l10n_util_mac.h" | |
47 #include "ui/base/models/simple_menu_model.h" | |
48 #include "ui/gfx/color_palette.h" | |
49 #include "ui/gfx/image/image_skia_util_mac.h" | |
50 #include "ui/gfx/paint_vector_icon.h" | |
51 #include "url/gurl.h" | |
52 | |
53 using base::UserMetricsAction; | |
54 | |
55 namespace { | |
56 | |
57 // Distance between permission icon and permission label. | |
58 const CGFloat kHorizontalIconPadding = 8.0f; | |
59 | |
60 // Distance between two permission labels. | |
61 const CGFloat kVerticalPermissionPadding = 2.0f; | |
62 | |
63 const CGFloat kHorizontalPadding = 13.0f; | |
64 const CGFloat kVerticalPadding = 15.0f; | |
65 const CGFloat kBetweenButtonsPadding = 10.0f; | |
66 const CGFloat kButtonRightEdgePadding = 17.0f; | |
67 const CGFloat kTitlePaddingX = 50.0f; | |
68 const CGFloat kBubbleMinWidth = 315.0f; | |
69 const NSSize kPermissionIconSize = {18, 18}; | |
70 | |
71 const NSInteger kFullscreenLeftOffset = 40; | |
72 | |
73 } // namespace | |
74 | |
75 // NSPopUpButton with a menu containing two items: allow and block. | |
76 // One AllowBlockMenuButton is used for each requested permission when there are | |
77 // multiple permissions in the bubble. | |
78 @interface AllowBlockMenuButton : NSPopUpButton { | |
79 @private | |
80 std::unique_ptr<PermissionMenuModel> menuModel_; | |
81 base::scoped_nsobject<MenuController> menuController_; | |
82 } | |
83 | |
84 - (id)initForURL:(const GURL&)url | |
85 allowed:(BOOL)allow | |
86 index:(int)index | |
87 delegate:(PermissionPrompt::Delegate*)delegate | |
88 profile:(Profile*)profile; | |
89 | |
90 // Returns the maximum width of its possible titles. | |
91 - (CGFloat)maximumTitleWidth; | |
92 @end | |
93 | |
94 @implementation AllowBlockMenuButton | |
95 | |
96 - (id)initForURL:(const GURL&)url | |
97 allowed:(BOOL)allow | |
98 index:(int)index | |
99 delegate:(PermissionPrompt::Delegate*)delegate | |
100 profile:(Profile*)profile { | |
101 if (self = [super initWithFrame:NSZeroRect pullsDown:NO]) { | |
102 ContentSetting setting = | |
103 allow ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; | |
104 [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; | |
105 [self setBordered:NO]; | |
106 | |
107 __block PermissionPrompt::Delegate* blockDelegate = delegate; | |
108 __block AllowBlockMenuButton* blockSelf = self; | |
109 PermissionMenuModel::ChangeCallback changeCallback = | |
110 base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) { | |
111 blockDelegate->ToggleAccept( | |
112 index, permission.setting == CONTENT_SETTING_ALLOW); | |
113 [blockSelf setFrameSize: | |
114 SizeForWebsiteSettingsButtonTitle(blockSelf, | |
115 [blockSelf title])]; | |
116 }); | |
117 | |
118 menuModel_.reset( | |
119 new PermissionMenuModel(profile, url, setting, changeCallback)); | |
120 menuController_.reset([[MenuController alloc] initWithModel:menuModel_.get() | |
121 useWithPopUpButtonCell:NO]); | |
122 [self setMenu:[menuController_ menu]]; | |
123 [self selectItemAtIndex:menuModel_->GetIndexOfCommandId(setting)]; | |
124 // Although the frame is reset, below, this sizes the cell properly. | |
125 [self sizeToFit]; | |
126 // Adjust the size to fit the current title. Using only -sizeToFit leaves | |
127 // an ugly amount of whitespace between the title and the arrows because it | |
128 // will fit to the largest element in the menu, not just the selected item. | |
129 [self setFrameSize:SizeForWebsiteSettingsButtonTitle(self, [self title])]; | |
130 } | |
131 return self; | |
132 } | |
133 | |
134 - (CGFloat)maximumTitleWidth { | |
135 CGFloat maxTitleWidth = 0; | |
136 for (NSMenuItem* item in [self itemArray]) { | |
137 NSSize size = SizeForWebsiteSettingsButtonTitle(self, [item title]); | |
138 maxTitleWidth = std::max(maxTitleWidth, size.width); | |
139 } | |
140 return maxTitleWidth; | |
141 } | |
142 | |
143 @end | |
144 | |
145 @interface PermissionBubbleController () | |
146 | |
147 // Determines if the bubble has an anchor in a corner or no anchor at all. | |
148 - (info_bubble::BubbleArrowLocation)getExpectedArrowLocation; | |
149 | |
150 // Returns the expected parent for this bubble. | |
151 - (NSWindow*)getExpectedParentWindow; | |
152 | |
153 // Returns an autoreleased NSView displaying the icon and label for |request|. | |
154 - (NSView*)labelForRequest:(PermissionRequest*)request; | |
155 | |
156 // Returns an autoreleased NSView displaying the title for the bubble | |
157 // requesting settings for |host|. | |
158 - (NSView*)titleWithOrigin:(const GURL&)origin; | |
159 | |
160 // Returns an autoreleased NSView displaying a menu for |request|. The | |
161 // menu will be initialized as 'allow' if |allow| is YES. | |
162 - (NSView*)menuForRequest:(PermissionRequest*)request | |
163 atIndex:(int)index | |
164 allow:(BOOL)allow; | |
165 | |
166 // Returns an autoreleased NSView of a button with |title| and |action|. | |
167 - (NSView*)buttonWithTitle:(NSString*)title | |
168 action:(SEL)action; | |
169 | |
170 // Returns an autoreleased NSView displaying a block button. | |
171 - (NSView*)blockButton; | |
172 | |
173 // Returns an autoreleased NSView displaying the close 'x' button. | |
174 - (NSView*)closeButton; | |
175 | |
176 // Called when the 'ok' button is pressed. | |
177 - (void)ok:(id)sender; | |
178 | |
179 // Called when the 'allow' button is pressed. | |
180 - (void)onAllow:(id)sender; | |
181 | |
182 // Called when the 'block' button is pressed. | |
183 - (void)onBlock:(id)sender; | |
184 | |
185 // Called when the 'close' button is pressed. | |
186 - (void)onClose:(id)sender; | |
187 | |
188 // Returns the constant offset from the left to use for fullscreen permission | |
189 // bubbles. Only used in tests. | |
190 + (NSInteger)getFullscreenLeftOffset; | |
191 | |
192 // Sets the width of both |viewA| and |viewB| to be the larger of the | |
193 // two views' widths. Does not change either view's origin or height. | |
194 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB; | |
195 | |
196 // Sets the offset of |viewA| so that its vertical center is aligned with the | |
197 // vertical center of |viewB|. | |
198 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB; | |
199 | |
200 // BaseBubbleController override. | |
201 - (IBAction)cancel:(id)sender; | |
202 | |
203 @end | |
204 | |
205 @implementation PermissionBubbleController | |
206 | |
207 - (id)initWithBrowser:(Browser*)browser bridge:(PermissionBubbleCocoa*)bridge { | |
208 DCHECK(browser); | |
209 DCHECK(bridge); | |
210 browser_ = browser; | |
211 base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc] | |
212 initWithContentRect:ui::kWindowSizeDeterminedLater | |
213 styleMask:NSBorderlessWindowMask | |
214 backing:NSBackingStoreBuffered | |
215 defer:NO]); | |
216 | |
217 [window setAllowedAnimations:info_bubble::kAnimateNone]; | |
218 [window setReleasedWhenClosed:NO]; | |
219 if ((self = [super initWithWindow:window | |
220 parentWindow:[self getExpectedParentWindow] | |
221 anchoredAt:NSZeroPoint])) { | |
222 [self setShouldCloseOnResignKey:NO]; | |
223 [self setShouldOpenAsKeyWindow:YES]; | |
224 [[self bubble] setArrowLocation:[self getExpectedArrowLocation]]; | |
225 bridge_ = bridge; | |
226 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | |
227 [center addObserver:self | |
228 selector:@selector(parentWindowDidMove:) | |
229 name:NSWindowDidMoveNotification | |
230 object:[self getExpectedParentWindow]]; | |
231 } | |
232 return self; | |
233 } | |
234 | |
235 - (LocationBarDecoration*)decorationForBubble { | |
236 if (![self hasVisibleLocationBar]) | |
237 return nullptr; | |
238 | |
239 LocationBarViewMac* location_bar = | |
240 [[self.parentWindow windowController] locationBarBridge]; | |
241 return location_bar->GetPageInfoDecoration(); | |
242 } | |
243 | |
244 + (NSPoint)getAnchorPointForBrowser:(Browser*)browser { | |
245 NSPoint anchor; | |
246 NSWindow* parentWindow = browser->window()->GetNativeWindow(); | |
247 if ([PermissionBubbleController hasVisibleLocationBarForBrowser:browser]) { | |
248 LocationBarViewMac* location_bar = | |
249 [[parentWindow windowController] locationBarBridge]; | |
250 anchor = location_bar->GetPageInfoBubblePoint(); | |
251 } else { | |
252 // Position the bubble on the left of the screen if there is no page info | |
253 // button to point at. | |
254 NSRect contentFrame = [[parentWindow contentView] frame]; | |
255 anchor = NSMakePoint(NSMinX(contentFrame) + kFullscreenLeftOffset, | |
256 NSMaxY(contentFrame)); | |
257 } | |
258 | |
259 return ui::ConvertPointFromWindowToScreen(parentWindow, anchor); | |
260 } | |
261 | |
262 + (bool)hasVisibleLocationBarForBrowser:(Browser*)browser { | |
263 if (!browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) | |
264 return false; | |
265 | |
266 if (!browser->exclusive_access_manager()->context()->IsFullscreen()) | |
267 return true; | |
268 | |
269 // If the browser is in browser-initiated full screen, a preference can cause | |
270 // the toolbar to be hidden. | |
271 if (browser->exclusive_access_manager() | |
272 ->fullscreen_controller() | |
273 ->IsFullscreenForBrowser()) { | |
274 PrefService* prefs = browser->profile()->GetPrefs(); | |
275 bool show_toolbar = prefs->GetBoolean(prefs::kShowFullscreenToolbar); | |
276 return show_toolbar; | |
277 } | |
278 | |
279 // Otherwise this is fullscreen without a toolbar, so there is no visible | |
280 // location bar. | |
281 return false; | |
282 } | |
283 | |
284 - (void)windowWillClose:(NSNotification*)notification { | |
285 [[NSNotificationCenter defaultCenter] | |
286 removeObserver:self | |
287 name:NSWindowDidMoveNotification | |
288 object:nil]; | |
289 bridge_->OnBubbleClosing(); | |
290 [super windowWillClose:notification]; | |
291 } | |
292 | |
293 - (void)parentWindowWillToggleFullScreen:(NSNotification*)notification { | |
294 // Override the base class implementation, which would have closed the bubble. | |
295 } | |
296 | |
297 - (void)parentWindowDidResize:(NSNotification*)notification { | |
298 // Override the base class implementation, which sets the anchor point. But | |
299 // it's not necessary since BrowserWindowController will notify the | |
300 // PermissionRequestManager to update the anchor position on a resize. | |
301 } | |
302 | |
303 - (void)parentWindowDidMove:(NSNotification*)notification { | |
304 DCHECK(bridge_); | |
305 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
306 } | |
307 | |
308 - (void)showWithDelegate:(PermissionPrompt::Delegate*)delegate | |
309 forRequests:(const std::vector<PermissionRequest*>&)requests | |
310 acceptStates:(const std::vector<bool>&)acceptStates { | |
311 DCHECK(!requests.empty()); | |
312 DCHECK(delegate); | |
313 delegate_ = delegate; | |
314 | |
315 NSView* contentView = [[self window] contentView]; | |
316 [contentView setSubviews:@[]]; | |
317 | |
318 BOOL singlePermission = requests.size() == 1; | |
319 | |
320 // Create one button to use as a guide for the permissions' y-offsets. | |
321 base::scoped_nsobject<NSView> allowOrOkButton; | |
322 if (singlePermission) { | |
323 NSString* allowTitle = l10n_util::GetNSString(IDS_PERMISSION_ALLOW); | |
324 allowOrOkButton.reset([[self buttonWithTitle:allowTitle | |
325 action:@selector(onAllow:)] retain]); | |
326 } else { | |
327 NSString* okTitle = l10n_util::GetNSString(IDS_OK); | |
328 allowOrOkButton.reset([[self buttonWithTitle:okTitle | |
329 action:@selector(ok:)] retain]); | |
330 } | |
331 CGFloat yOffset = 2 * kVerticalPadding + NSMaxY([allowOrOkButton frame]); | |
332 | |
333 base::scoped_nsobject<NSMutableArray> permissionMenus; | |
334 if (!singlePermission) | |
335 permissionMenus.reset([[NSMutableArray alloc] init]); | |
336 | |
337 CGFloat maxPermissionLineWidth = 0; | |
338 CGFloat verticalPadding = 0.0f; | |
339 for (auto it = requests.begin(); it != requests.end(); it++) { | |
340 base::scoped_nsobject<NSView> permissionView( | |
341 [[self labelForRequest:(*it)] retain]); | |
342 NSPoint origin = [permissionView frame].origin; | |
343 origin.x += kHorizontalPadding; | |
344 origin.y += yOffset + verticalPadding; | |
345 [permissionView setFrameOrigin:origin]; | |
346 [contentView addSubview:permissionView]; | |
347 | |
348 if (!singlePermission) { | |
349 int index = it - requests.begin(); | |
350 base::scoped_nsobject<NSView> menu( | |
351 [[self menuForRequest:(*it) | |
352 atIndex:index | |
353 allow:acceptStates[index] ? YES : NO] retain]); | |
354 // Align vertically. Horizontal alignment will be adjusted once the | |
355 // widest permission is know. | |
356 [PermissionBubbleController alignCenterOf:menu | |
357 verticallyToCenterOf:permissionView]; | |
358 [permissionMenus addObject:menu]; | |
359 [contentView addSubview:menu]; | |
360 } | |
361 maxPermissionLineWidth = std::max( | |
362 maxPermissionLineWidth, NSMaxX([permissionView frame])); | |
363 yOffset += NSHeight([permissionView frame]); | |
364 | |
365 // Add extra padding for all but first permission. | |
366 verticalPadding = kVerticalPermissionPadding; | |
367 } | |
368 | |
369 base::scoped_nsobject<NSView> titleView( | |
370 [[self titleWithOrigin:requests[0]->GetOrigin()] retain]); | |
371 [contentView addSubview:titleView]; | |
372 [titleView setFrameOrigin:NSMakePoint(kHorizontalPadding, | |
373 kVerticalPadding + yOffset)]; | |
374 | |
375 // 'x' button in the upper-right-hand corner. | |
376 base::scoped_nsobject<NSView> closeButton([[self closeButton] retain]); | |
377 | |
378 // Determine the dimensions of the bubble. | |
379 // Once the height and width are set, the buttons and permission menus can | |
380 // be laid out correctly. | |
381 NSRect bubbleFrame = NSMakeRect(0, 0, kBubbleMinWidth, 0); | |
382 | |
383 // Fix the height of the bubble relative to the title. | |
384 bubbleFrame.size.height = NSMaxY([titleView frame]) + kVerticalPadding + | |
385 info_bubble::kBubbleArrowHeight; | |
386 | |
387 if (!singlePermission) { | |
388 // Add the maximum menu width to the bubble width. | |
389 CGFloat maxMenuWidth = 0; | |
390 for (AllowBlockMenuButton* button in permissionMenus.get()) { | |
391 maxMenuWidth = std::max(maxMenuWidth, [button maximumTitleWidth]); | |
392 } | |
393 maxPermissionLineWidth += maxMenuWidth; | |
394 } | |
395 | |
396 // The title and 'x' button row must fit within the bubble. | |
397 CGFloat titleRowWidth = NSMaxX([titleView frame]) + | |
398 NSWidth([closeButton frame]) + | |
399 chrome_style::kCloseButtonPadding; | |
400 | |
401 bubbleFrame.size.width = std::max( | |
402 NSWidth(bubbleFrame), std::max(titleRowWidth, maxPermissionLineWidth)); | |
403 | |
404 // Now that the bubble's dimensions have been set, lay out the buttons and | |
405 // menus. | |
406 | |
407 // Place the close button at the upper-right-hand corner of the bubble. | |
408 NSPoint closeButtonOrigin = | |
409 NSMakePoint(NSWidth(bubbleFrame) - NSWidth([closeButton frame]) - | |
410 chrome_style::kCloseButtonPadding, | |
411 NSHeight(bubbleFrame) - NSWidth([closeButton frame]) - | |
412 chrome_style::kCloseButtonPadding); | |
413 // Account for the bubble's arrow. | |
414 closeButtonOrigin.y -= info_bubble::kBubbleArrowHeight; | |
415 | |
416 [closeButton setFrameOrigin:closeButtonOrigin]; | |
417 [contentView addSubview:closeButton]; | |
418 | |
419 // Position the allow/ok button. | |
420 CGFloat xOrigin = NSWidth(bubbleFrame) - NSWidth([allowOrOkButton frame]) - | |
421 kButtonRightEdgePadding; | |
422 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | |
423 [contentView addSubview:allowOrOkButton]; | |
424 | |
425 if (singlePermission) { | |
426 base::scoped_nsobject<NSView> blockButton; | |
427 blockButton.reset([[self blockButton] retain]); | |
428 CGFloat width = [PermissionBubbleController matchWidthsOf:blockButton | |
429 andOf:allowOrOkButton]; | |
430 // Ensure the allow/ok button is still in the correct position. | |
431 xOrigin = NSWidth(bubbleFrame) - width - kHorizontalPadding; | |
432 [allowOrOkButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | |
433 // Line up the block button. | |
434 xOrigin = NSMinX([allowOrOkButton frame]) - width - kBetweenButtonsPadding; | |
435 [blockButton setFrameOrigin:NSMakePoint(xOrigin, kVerticalPadding)]; | |
436 [contentView addSubview:blockButton]; | |
437 } else { | |
438 // Adjust the horizontal origin for each menu so that its right edge | |
439 // lines up with the right edge of the ok button. | |
440 CGFloat rightEdge = NSMaxX([allowOrOkButton frame]); | |
441 for (NSView* view in permissionMenus.get()) { | |
442 [view setFrameOrigin:NSMakePoint(rightEdge - NSWidth([view frame]), | |
443 NSMinY([view frame]))]; | |
444 } | |
445 } | |
446 | |
447 bubbleFrame = [[self window] frameRectForContentRect:bubbleFrame]; | |
448 if ([[self window] isVisible]) { | |
449 // Unfortunately, calling -setFrame followed by -setFrameOrigin (called | |
450 // within -setAnchorPoint) causes flickering. Avoid the flickering by | |
451 // manually adjusting the new frame's origin so that the top left stays the | |
452 // same, and only calling -setFrame. | |
453 NSRect currentWindowFrame = [[self window] frame]; | |
454 bubbleFrame.origin = currentWindowFrame.origin; | |
455 bubbleFrame.origin.y = bubbleFrame.origin.y + | |
456 currentWindowFrame.size.height - bubbleFrame.size.height; | |
457 [[self window] setFrame:bubbleFrame display:YES]; | |
458 } else { | |
459 [[self window] setFrame:bubbleFrame display:NO]; | |
460 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
461 [self showWindow:nil]; | |
462 [[self window] makeFirstResponder:nil]; | |
463 [[self window] setInitialFirstResponder:allowOrOkButton.get()]; | |
464 } | |
465 } | |
466 | |
467 - (void)updateAnchorPosition { | |
468 [self setParentWindow:[self getExpectedParentWindow]]; | |
469 [self setAnchorPoint:[self getExpectedAnchorPoint]]; | |
470 [[self bubble] setArrowLocation:[self getExpectedArrowLocation]]; | |
471 } | |
472 | |
473 - (NSPoint)getExpectedAnchorPoint { | |
474 return [PermissionBubbleController getAnchorPointForBrowser:browser_]; | |
475 } | |
476 | |
477 - (bool)hasVisibleLocationBar { | |
478 return [PermissionBubbleController hasVisibleLocationBarForBrowser:browser_]; | |
479 } | |
480 | |
481 - (info_bubble::BubbleArrowLocation)getExpectedArrowLocation { | |
482 return info_bubble::kTopLeading; | |
483 } | |
484 | |
485 - (NSWindow*)getExpectedParentWindow { | |
486 DCHECK(browser_->window()); | |
487 return browser_->window()->GetNativeWindow(); | |
488 } | |
489 | |
490 - (NSView*)labelForRequest:(PermissionRequest*)request { | |
491 DCHECK(request); | |
492 base::scoped_nsobject<NSView> permissionView( | |
493 [[NSView alloc] initWithFrame:NSZeroRect]); | |
494 base::scoped_nsobject<NSImageView> permissionIcon( | |
495 [[NSImageView alloc] initWithFrame:NSZeroRect]); | |
496 [permissionIcon | |
497 setImage:NSImageFromImageSkia(gfx::CreateVectorIcon( | |
498 request->GetIconId(), 18, gfx::kChromeIconGrey))]; | |
499 [permissionIcon setFrameSize:kPermissionIconSize]; | |
500 [permissionView addSubview:permissionIcon]; | |
501 | |
502 base::scoped_nsobject<NSTextField> permissionLabel( | |
503 [[NSTextField alloc] initWithFrame:NSZeroRect]); | |
504 base::string16 label = request->GetMessageTextFragment(); | |
505 [permissionLabel setDrawsBackground:NO]; | |
506 [permissionLabel setBezeled:NO]; | |
507 [permissionLabel setEditable:NO]; | |
508 [permissionLabel setSelectable:NO]; | |
509 [permissionLabel | |
510 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; | |
511 [permissionLabel setStringValue:base::SysUTF16ToNSString(label)]; | |
512 [permissionLabel sizeToFit]; | |
513 [permissionLabel setFrameOrigin: | |
514 NSMakePoint(NSWidth([permissionIcon frame]) + kHorizontalIconPadding, 0)]; | |
515 [permissionView addSubview:permissionLabel]; | |
516 | |
517 // Match the horizontal centers of the two subviews. Note that the label's | |
518 // center is rounded down, and the icon's center, up. It looks better that | |
519 // way - with the text's center slightly lower than the icon's center - if the | |
520 // height delta is not evenly split. | |
521 NSRect iconFrame = [permissionIcon frame]; | |
522 NSRect labelFrame = [permissionLabel frame]; | |
523 NSRect unionFrame = NSUnionRect(iconFrame, labelFrame); | |
524 | |
525 iconFrame.origin.y = | |
526 std::ceil((NSHeight(unionFrame) - NSHeight(iconFrame)) / 2); | |
527 labelFrame.origin.y = | |
528 std::floor((NSHeight(unionFrame) - NSHeight(labelFrame)) / 2); | |
529 | |
530 [permissionLabel setFrame:labelFrame]; | |
531 [permissionIcon setFrame:iconFrame]; | |
532 [permissionView setFrame:unionFrame]; | |
533 | |
534 return permissionView.autorelease(); | |
535 } | |
536 | |
537 - (NSView*)titleWithOrigin:(const GURL&)origin { | |
538 base::scoped_nsobject<NSTextField> titleView( | |
539 [[NSTextField alloc] initWithFrame:NSZeroRect]); | |
540 [titleView setDrawsBackground:NO]; | |
541 [titleView setBezeled:NO]; | |
542 [titleView setEditable:NO]; | |
543 [titleView setSelectable:NO]; | |
544 [titleView setStringValue:l10n_util::GetNSStringF( | |
545 IDS_PERMISSIONS_BUBBLE_PROMPT, | |
546 url_formatter::FormatUrlForSecurityDisplay( | |
547 origin, url_formatter::SchemeDisplay:: | |
548 OMIT_CRYPTOGRAPHIC))]; | |
549 [titleView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; | |
550 [titleView sizeToFit]; | |
551 NSRect titleFrame = [titleView frame]; | |
552 [titleView setFrameSize:NSMakeSize(NSWidth(titleFrame) + kTitlePaddingX, | |
553 NSHeight(titleFrame))]; | |
554 return titleView.autorelease(); | |
555 } | |
556 | |
557 - (NSView*)menuForRequest:(PermissionRequest*)request | |
558 atIndex:(int)index | |
559 allow:(BOOL)allow { | |
560 DCHECK(request); | |
561 DCHECK(delegate_); | |
562 base::scoped_nsobject<AllowBlockMenuButton> button( | |
563 [[AllowBlockMenuButton alloc] initForURL:request->GetOrigin() | |
564 allowed:allow | |
565 index:index | |
566 delegate:delegate_ | |
567 profile:browser_->profile()]); | |
568 return button.autorelease(); | |
569 } | |
570 | |
571 - (NSView*)buttonWithTitle:(NSString*)title | |
572 action:(SEL)action { | |
573 base::scoped_nsobject<NSButton> button( | |
574 [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); | |
575 [button setButtonType:NSMomentaryPushInButton]; | |
576 [button setTitle:title]; | |
577 [button setTarget:self]; | |
578 [button setAction:action]; | |
579 [button sizeToFit]; | |
580 return button.autorelease(); | |
581 } | |
582 | |
583 - (NSView*)blockButton { | |
584 NSString* blockTitle = l10n_util::GetNSString(IDS_PERMISSION_DENY); | |
585 return [self buttonWithTitle:blockTitle | |
586 action:@selector(onBlock:)]; | |
587 } | |
588 | |
589 - (NSView*)closeButton { | |
590 int dimension = chrome_style::GetCloseButtonSize(); | |
591 NSRect frame = NSMakeRect(0, 0, dimension, dimension); | |
592 base::scoped_nsobject<NSButton> button( | |
593 [[WebUIHoverCloseButton alloc] initWithFrame:frame]); | |
594 [button setAction:@selector(onClose:)]; | |
595 [button setTarget:self]; | |
596 return button.autorelease(); | |
597 } | |
598 | |
599 - (void)ok:(id)sender { | |
600 if (delegate_) | |
601 delegate_->Accept(); | |
602 } | |
603 | |
604 - (void)onAllow:(id)sender { | |
605 if (delegate_) | |
606 delegate_->Accept(); | |
607 } | |
608 | |
609 - (void)onBlock:(id)sender { | |
610 if (delegate_) | |
611 delegate_->Deny(); | |
612 } | |
613 | |
614 - (void)onClose:(id)sender { | |
615 if (delegate_) | |
616 delegate_->Closing(); | |
617 } | |
618 | |
619 + (NSInteger)getFullscreenLeftOffset { | |
620 return kFullscreenLeftOffset; | |
621 } | |
622 | |
623 - (void)activateTabWithContents:(content::WebContents*)newContents | |
624 previousContents:(content::WebContents*)oldContents | |
625 atIndex:(NSInteger)index | |
626 reason:(int)reason { | |
627 // The show/hide of this bubble is handled by the PermissionRequestManager. | |
628 // So bypass the base class, which would close the bubble here. | |
629 } | |
630 | |
631 + (CGFloat)matchWidthsOf:(NSView*)viewA andOf:(NSView*)viewB { | |
632 NSRect frameA = [viewA frame]; | |
633 NSRect frameB = [viewB frame]; | |
634 CGFloat width = std::max(NSWidth(frameA), NSWidth(frameB)); | |
635 [viewA setFrameSize:NSMakeSize(width, NSHeight(frameA))]; | |
636 [viewB setFrameSize:NSMakeSize(width, NSHeight(frameB))]; | |
637 return width; | |
638 } | |
639 | |
640 + (void)alignCenterOf:(NSView*)viewA verticallyToCenterOf:(NSView*)viewB { | |
641 NSRect frameA = [viewA frame]; | |
642 NSRect frameB = [viewB frame]; | |
643 frameA.origin.y = | |
644 NSMinY(frameB) + std::floor((NSHeight(frameB) - NSHeight(frameA)) / 2); | |
645 [viewA setFrameOrigin:frameA.origin]; | |
646 } | |
647 | |
648 - (IBAction)cancel:(id)sender { | |
649 // This is triggered by ESC when the bubble has focus. | |
650 if (delegate_) | |
651 delegate_->Closing(); | |
652 [super cancel:sender]; | |
653 } | |
654 | |
655 @end // implementation PermissionBubbleController | |
OLD | NEW |