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.
5 #import "chrome/browser/ui/cocoa/website_settings/website_settings_bubble_controller.h"
9 #import <AppKit/AppKit.h>
11 #include "base/i18n/rtl.h"
12 #include "base/mac/bind_objc_block.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/sys_string_conversions.h"
15 #import "chrome/browser/certificate_viewer.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/ui/browser_dialogs.h"
18 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
19 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
20 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
21 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
22 #import "chrome/browser/ui/cocoa/website_settings/permission_selector_button.h"
23 #import "chrome/browser/ui/tab_dialogs.h"
24 #include "chrome/browser/ui/website_settings/permission_menu_model.h"
25 #include "chrome/browser/ui/website_settings/website_settings_utils.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/grit/chromium_strings.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "content/public/browser/cert_store.h"
30 #include "content/public/browser/page_navigator.h"
31 #include "content/public/browser/ssl_host_state_delegate.h"
32 #include "content/public/browser/user_metrics.h"
33 #include "content/public/browser/web_contents.h"
34 #include "grit/theme_resources.h"
35 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
36 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
37 #import "ui/base/cocoa/flipped_view.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #import "ui/gfx/mac/coordinate_conversion.h"
41 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
45 // The default width of the window, in view coordinates. It may be larger to
47 const CGFloat kDefaultWindowWidth = 310;
49 // Spacing in between sections.
50 const CGFloat kVerticalSpacing = 10;
52 // Padding between the window frame and content.
53 const CGFloat kFramePadding = 20;
55 // Padding between the window frame and content for the internal page bubble.
56 const CGFloat kInternalPageFramePadding = 10;
58 // Spacing between the headlines and description text on the Connection tab.
59 const CGFloat kConnectionHeadlineSpacing = 2;
61 // Spacing between images on the Connection tab and the text.
62 const CGFloat kConnectionImageSpacing = 10;
64 // Spacing between the image and text for internal pages.
65 const CGFloat kInternalPageImageSpacing = 10;
67 // Square size of the images on the Connections tab.
68 const CGFloat kConnectionImageSize = 30;
70 // Square size of the image that is shown for internal pages.
71 const CGFloat kInternalPageImageSize = 26;
73 // Square size of the permission images.
74 const CGFloat kPermissionImageSize = 19;
76 // Vertical adjustment for the permission images. They have an extra pixel of
77 // padding on the bottom edge.
78 const CGFloat kPermissionImageYAdjust = 1;
80 // Spacing between a permission image and the text.
81 const CGFloat kPermissionImageSpacing = 3;
83 // The spacing between individual items in the Permissions tab.
84 const CGFloat kPermissionsTabSpacing = 12;
86 // Extra spacing after a headline on the Permissions tab.
87 const CGFloat kPermissionsHeadlineSpacing = 2;
89 // The amount of horizontal space between a permission label and the popup.
90 const CGFloat kPermissionPopUpXSpacing = 3;
92 // The extra space to the left of the first tab in the tab strip.
93 const CGFloat kTabStripXPadding = kFramePadding;
95 // The amount of space between the visual borders of adjacent tabs.
96 const CGFloat kTabSpacing = 4;
98 // The amount of space above the tab strip.
99 const CGFloat kTabStripTopSpacing = 14;
101 // The height of the clickable area of the tab.
102 const CGFloat kTabHeight = 28;
104 // The amount of space above tab labels.
105 const CGFloat kTabLabelTopPadding = 6;
107 // The amount of padding to leave on either side of the tab label.
108 const CGFloat kTabLabelXPadding = 12;
110 // The amount of padding to *remove* when placing
111 // |IDS_WEBSITE_SETTINGS_{FIRST,THIRD}_PARTY_SITE_DATA| next to each other.
112 const CGFloat kTextLabelXPadding = 5;
114 // Takes in the parent window, which should be a BrowserWindow, and gets the
115 // proper anchor point for the bubble. The returned point is in screen
117 NSPoint AnchorPointForWindow(NSWindow* parent) {
118 BrowserWindowController* controller = [parent windowController];
119 NSPoint origin = NSZeroPoint;
120 if ([controller isKindOfClass:[BrowserWindowController class]]) {
121 LocationBarViewMac* location_bar = [controller locationBarBridge];
123 NSPoint bubble_point = location_bar->GetPageInfoBubblePoint();
124 origin = [parent convertBaseToScreen:bubble_point];
132 @interface WebsiteSettingsTabSegmentedCell : NSSegmentedCell {
134 base::scoped_nsobject<NSImage> tabstripCenterImage_;
135 base::scoped_nsobject<NSImage> tabstripLeftImage_;
136 base::scoped_nsobject<NSImage> tabstripRightImage_;
138 base::scoped_nsobject<NSImage> tabCenterImage_;
139 base::scoped_nsobject<NSImage> tabLeftImage_;
140 base::scoped_nsobject<NSImage> tabRightImage_;
142 // Key track of the index of segment which has keyboard focus. This is not
143 // the same as the currently selected segment.
144 NSInteger keySegment_;
147 // The text attributes to use for the tab labels.
148 + (NSDictionary*)textAttributes;
152 @implementation WebsiteSettingsTabSegmentedCell
154 + (NSDictionary*)textAttributes {
155 NSFont* smallSystemFont =
156 [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
157 return @{ NSFontAttributeName : smallSystemFont };
161 if ((self = [super init])) {
162 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
163 tabstripCenterImage_.reset(rb.GetNativeImageNamed(
164 IDR_WEBSITE_SETTINGS_TABSTRIP_CENTER).CopyNSImage());
165 tabstripLeftImage_.reset(rb.GetNativeImageNamed(
166 IDR_WEBSITE_SETTINGS_TABSTRIP_LEFT).CopyNSImage());
167 tabstripRightImage_.reset(rb.GetNativeImageNamed(
168 IDR_WEBSITE_SETTINGS_TABSTRIP_RIGHT).CopyNSImage());
170 tabCenterImage_.reset(
171 rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_CENTER2).CopyNSImage());
173 rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_LEFT2).CopyNSImage());
174 tabRightImage_.reset(
175 rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_RIGHT2).CopyNSImage());
180 // Called when keyboard focus in the segmented control is moved forward.
181 - (void)makeNextSegmentKey {
182 [super makeNextSegmentKey];
183 keySegment_ = (keySegment_ + 1) % [self segmentCount];
186 // Called when keyboard focus in the segmented control is moved backwards.
187 - (void)makePreviousSegmentKey {
188 [super makePreviousSegmentKey];
189 if (--keySegment_ < 0)
190 keySegment_ += [self segmentCount];
193 - (void)setSelectedSegment:(NSInteger)selectedSegment {
194 keySegment_ = selectedSegment;
195 [super setSelectedSegment:selectedSegment];
198 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
199 CGFloat tabstripHeight = [tabCenterImage_ size].height;
201 // Draw the tab for the selected segment.
202 NSRect tabRect = [self hitRectForSegment:[self selectedSegment]];
203 tabRect.origin.y = 0;
204 tabRect.size.height = tabstripHeight;
206 NSDrawThreePartImage(tabRect,
211 NSCompositeSourceOver,
215 // Draw the background to the left of the selected tab.
216 NSRect backgroundRect = NSMakeRect(0, 0, NSMinX(tabRect), tabstripHeight);
217 NSDrawThreePartImage(backgroundRect,
219 tabstripCenterImage_,
222 NSCompositeSourceOver,
226 // Draw the background to the right of the selected tab.
227 backgroundRect.origin.x = NSMaxX(tabRect);
228 backgroundRect.size.width = NSMaxX(cellFrame) - NSMaxX(tabRect);
229 NSDrawThreePartImage(backgroundRect,
231 tabstripCenterImage_,
234 NSCompositeSourceOver,
238 // Call the superclass method to trigger drawing of the tab labels.
239 NSRect interiorFrame = cellFrame;
240 interiorFrame.size.width = 0;
241 for (NSInteger i = 0; i < [self segmentCount]; ++i)
242 interiorFrame.size.width += [self widthForSegment:i];
243 [self drawInteriorWithFrame:interiorFrame inView:controlView];
245 if ([[controlView window] firstResponder] == controlView)
246 [self drawFocusRect];
249 - (void)drawFocusRect {
250 gfx::ScopedNSGraphicsContextSaveGState scoped_state;
251 NSSetFocusRingStyle(NSFocusRingOnly);
252 [[NSColor keyboardFocusIndicatorColor] set];
253 NSFrameRect([self hitRectForSegment:keySegment_]);
256 // Returns the segment number for the left-most positioned segment.
257 // On Right-to-Left languages, segment 0 is on the right.
258 - (NSInteger)leftSegment {
259 BOOL isRTL = [self userInterfaceLayoutDirection] ==
260 NSUserInterfaceLayoutDirectionRightToLeft;
261 return isRTL ? [self segmentCount] - 1 : 0;
264 // Return the hit rect (i.e., the visual bounds of the tab) for
265 // the given segment.
266 - (NSRect)hitRectForSegment:(NSInteger)segment {
267 CGFloat tabstripHeight = [tabCenterImage_ size].height;
268 DCHECK_GT(tabstripHeight, kTabHeight);
269 DCHECK([self segmentCount] == 2); // Assume 2 segments to keep things simple.
270 NSInteger leftSegment = [self leftSegment];
271 CGFloat xOrigin = segment == leftSegment ? kTabStripXPadding
272 : [self widthForSegment:leftSegment];
273 NSRect rect = NSMakeRect(xOrigin, tabstripHeight - kTabHeight,
274 [self widthForSegment:segment] - kTabStripXPadding,
276 return NSInsetRect(rect, kTabSpacing / 2, 0);
279 - (void)drawSegment:(NSInteger)segment
280 inFrame:(NSRect)tabFrame
281 withView:(NSView*)controlView {
282 // Adjust the tab's frame so that the label appears centered in the tab.
283 if (segment == [self leftSegment])
284 tabFrame.origin.x += kTabStripXPadding;
285 tabFrame.size.width -= kTabStripXPadding;
286 tabFrame.origin.y += kTabLabelTopPadding;
287 tabFrame.size.height -= kTabLabelTopPadding;
289 // Center the label's frame in the tab's frame.
290 NSString* label = [self labelForSegment:segment];
291 NSDictionary* textAttributes =
292 [WebsiteSettingsTabSegmentedCell textAttributes];
293 NSSize textSize = [label sizeWithAttributes:textAttributes];
295 labelFrame.size = textSize;
296 labelFrame.origin.x =
297 tabFrame.origin.x + (NSWidth(tabFrame) - textSize.width) / 2.0;
298 labelFrame.origin.y =
299 tabFrame.origin.y + (NSHeight(tabFrame) - textSize.height) / 2.0;
301 [label drawInRect:labelFrame withAttributes:textAttributes];
304 // Overrides the default tracking behavior to only respond to clicks inside the
305 // visual borders of the tab.
306 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
307 NSInteger segmentCount = [self segmentCount];
308 for (NSInteger i = 0; i < segmentCount; ++i) {
309 if (NSPointInRect(startPoint, [self hitRectForSegment:i]))
315 // Overrides the default cell height to take up the full height of the
316 // segmented control. Otherwise, clicks on the lower part of a tab will be
318 - (NSSize)cellSizeForBounds:(NSRect)aRect {
319 return NSMakeSize([super cellSizeForBounds:aRect].width,
320 [tabstripCenterImage_ size].height);
323 // Returns the minimum size required to display this cell.
324 // It should always be exactly as tall as the tabstrip background image.
326 return NSMakeSize([super cellSize].width,
327 [tabstripCenterImage_ size].height);
331 @implementation WebsiteSettingsBubbleController
333 - (CGFloat)defaultWindowWidth {
334 return kDefaultWindowWidth;
337 - (id)initWithParentWindow:(NSWindow*)parentWindow
338 websiteSettingsUIBridge:(WebsiteSettingsUIBridge*)bridge
339 webContents:(content::WebContents*)webContents
340 isInternalPage:(BOOL)isInternalPage {
341 DCHECK(parentWindow);
343 webContents_ = webContents;
345 // Use an arbitrary height; it will be changed in performLayout.
346 NSRect contentRect = NSMakeRect(0, 0, [self defaultWindowWidth], 1);
347 // Create an empty window into which content is placed.
348 base::scoped_nsobject<InfoBubbleWindow> window(
349 [[InfoBubbleWindow alloc] initWithContentRect:contentRect
350 styleMask:NSBorderlessWindowMask
351 backing:NSBackingStoreBuffered
354 if ((self = [super initWithWindow:window.get()
355 parentWindow:parentWindow
356 anchoredAt:NSZeroPoint])) {
357 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
359 // Create the container view that uses flipped coordinates.
360 NSRect contentFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
362 [[FlippedView alloc] initWithFrame:contentFrame]);
364 // Replace the window's content.
365 [[[self window] contentView] setSubviews:
366 [NSArray arrayWithObject:contentView_.get()]];
369 [self initializeContentsForInternalPage];
371 [self initializeContents];
373 bridge_.reset(bridge);
374 bridge_->set_bubble_controller(self);
379 - (void)windowWillClose:(NSNotification*)notification {
380 if (presenter_.get())
381 presenter_->OnUIClosing();
383 [super windowWillClose:notification];
386 - (void)setPresenter:(WebsiteSettings*)presenter {
387 presenter_.reset(presenter);
390 // Create the subviews for the bubble for internal Chrome pages.
391 - (void)initializeContentsForInternalPage {
392 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
394 NSPoint controlOrigin = NSMakePoint(
395 kInternalPageFramePadding,
396 kInternalPageFramePadding + info_bubble::kBubbleArrowHeight);
397 NSSize imageSize = NSMakeSize(kInternalPageImageSize,
398 kInternalPageImageSize);
399 NSImageView* imageView = [self addImageWithSize:imageSize
401 atPoint:controlOrigin];
402 [imageView setImage:rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_26).ToNSImage()];
404 controlOrigin.x += NSWidth([imageView frame]) + kInternalPageImageSpacing;
405 base::string16 text = l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE);
406 NSTextField* textField = [self addText:text
407 withSize:[NSFont smallSystemFontSize]
410 atPoint:controlOrigin];
411 // Center the text vertically with the image.
412 NSRect textFrame = [textField frame];
413 textFrame.origin.y += (imageSize.height - NSHeight(textFrame)) / 2;
414 [textField setFrame:textFrame];
416 // Adjust the contentView to fit everything.
417 CGFloat maxY = std::max(NSMaxY([imageView frame]), NSMaxY(textFrame));
418 [contentView_ setFrame:NSMakeRect(
419 0, 0, [self defaultWindowWidth], maxY + kInternalPageFramePadding)];
421 [self sizeAndPositionWindow];
424 // Create the subviews for the website settings bubble.
425 - (void)initializeContents {
426 // Keeps track of the position that the next control should be drawn at.
427 NSPoint controlOrigin = NSMakePoint(
429 kFramePadding + info_bubble::kBubbleArrowHeight);
431 // Create a text field (empty for now) to show the site identity.
432 identityField_ = [self addText:base::string16()
433 withSize:[NSFont systemFontSize]
436 atPoint:controlOrigin];
438 NSHeight([identityField_ frame]) + kConnectionHeadlineSpacing;
440 // Create a text field to identity status (e.g. verified, not verified).
441 identityStatusField_ = [self addText:base::string16()
442 withSize:[NSFont smallSystemFontSize]
445 atPoint:controlOrigin];
447 // Create the tab view and its two tabs.
449 base::scoped_nsobject<WebsiteSettingsTabSegmentedCell> cell(
450 [[WebsiteSettingsTabSegmentedCell alloc] init]);
451 CGFloat tabstripHeight = [cell cellSize].height;
452 NSRect tabstripFrame = NSMakeRect(
453 0, 0, [self defaultWindowWidth], tabstripHeight);
454 segmentedControl_.reset(
455 [[NSSegmentedControl alloc] initWithFrame:tabstripFrame]);
456 [segmentedControl_ setCell:cell];
457 [segmentedControl_ setSegmentCount:WebsiteSettingsUI::NUM_TAB_IDS];
458 [segmentedControl_ setTarget:self];
459 [segmentedControl_ setAction:@selector(tabSelected:)];
460 [segmentedControl_ setAutoresizingMask:NSViewWidthSizable];
462 NSDictionary* textAttributes =
463 [WebsiteSettingsTabSegmentedCell textAttributes];
465 // Create the "Permissions" tab.
466 NSString* label = l10n_util::GetNSString(
467 IDS_WEBSITE_SETTINGS_TAB_LABEL_PERMISSIONS);
468 [segmentedControl_ setLabel:label
469 forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
470 NSSize textSize = [label sizeWithAttributes:textAttributes];
471 CGFloat tabWidth = textSize.width + 2 * kTabLabelXPadding;
473 // Create the "Connection" tab.
474 label = l10n_util::GetNSString(IDS_WEBSITE_SETTINGS_TAB_LABEL_CONNECTION);
475 textSize = [label sizeWithAttributes:textAttributes];
476 [segmentedControl_ setLabel:label
477 forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
479 DCHECK_EQ([segmentedControl_ segmentCount], WebsiteSettingsUI::NUM_TAB_IDS);
481 // Make both tabs the width of the widest. The first segment has some
482 // additional padding that is not part of the tab, which is used for drawing
483 // the background of the tab strip.
484 tabWidth = std::max(tabWidth,
485 textSize.width + 2 * kTabLabelXPadding);
486 [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
487 forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
488 [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
489 forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
491 [segmentedControl_ setFont:[textAttributes objectForKey:NSFontAttributeName]];
492 [contentView_ addSubview:segmentedControl_];
494 NSRect tabFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
495 tabView_.reset([[NSTabView alloc] initWithFrame:tabFrame]);
496 [tabView_ setTabViewType:NSNoTabsNoBorder];
497 [tabView_ setDrawsBackground:NO];
498 [tabView_ setControlSize:NSSmallControlSize];
499 [contentView_ addSubview:tabView_.get()];
501 permissionsTabContentView_ = [self addPermissionsTabToTabView:tabView_];
502 connectionTabContentView_ = [self addConnectionTabToTabView:tabView_];
503 [self setSelectedTab:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
505 [self performLayout];
508 // Create the contents of the Permissions tab and add it to the given tab view.
509 // Returns a weak reference to the tab view item's view.
510 - (NSView*)addPermissionsTabToTabView:(NSTabView*)tabView {
511 base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
512 [tabView_ insertTabViewItem:item.get()
513 atIndex:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
514 base::scoped_nsobject<NSView> contentView(
515 [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
516 [contentView setAutoresizingMask:NSViewWidthSizable];
517 [item setView:contentView.get()];
519 // Initialize the two containers that hold the controls. The initial frames
520 // are arbitrary, and will be adjusted after the controls are laid out.
521 cookiesView_ = [[[FlippedView alloc]
522 initWithFrame:[tabView_ contentRect]] autorelease];
523 [cookiesView_ setAutoresizingMask:NSViewWidthSizable];
525 permissionsView_ = [[[FlippedView alloc]
526 initWithFrame:[tabView_ contentRect]] autorelease];
528 [contentView addSubview:cookiesView_];
529 [contentView addSubview:permissionsView_];
531 // Create the link button to view site settings. Its position will be set in
533 NSString* siteSettingsButtonText =
534 l10n_util::GetNSString(IDS_PAGE_INFO_SITE_SETTINGS_LINK);
535 siteSettingsButton_ =
536 [self addLinkButtonWithText:siteSettingsButtonText toView:contentView];
537 [siteSettingsButton_ setTarget:self];
538 [siteSettingsButton_ setAction:@selector(showSiteSettingsData:)];
540 return contentView.get();
543 // Handler for the link button below the list of cookies.
544 - (void)showCookiesAndSiteData:(id)sender {
545 DCHECK(webContents_);
547 presenter_->RecordWebsiteSettingsAction(
548 WebsiteSettings::WEBSITE_SETTINGS_COOKIES_DIALOG_OPENED);
549 TabDialogs::FromWebContents(webContents_)->ShowCollectedCookies();
552 // Handler for the site settings button below the list of permissions.
553 - (void)showSiteSettingsData:(id)sender {
554 DCHECK(webContents_);
556 presenter_->RecordWebsiteSettingsAction(
557 WebsiteSettings::WEBSITE_SETTINGS_SITE_SETTINGS_OPENED);
558 webContents_->OpenURL(content::OpenURLParams(
559 GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
560 NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
563 // Handler for the link button to show certificate information.
564 - (void)showCertificateInfo:(id)sender {
565 DCHECK(certificateId_);
566 presenter_->RecordWebsiteSettingsAction(
567 WebsiteSettings::WEBSITE_SETTINGS_CERTIFICATE_DIALOG_OPENED);
568 ShowCertificateViewerByID(webContents_, [self parentWindow], certificateId_);
571 // Handler for the link button to revoke user certificate decisions.
572 - (void)resetCertificateDecisions:(id)sender {
573 DCHECK(resetDecisionsButton_);
574 presenter_->OnRevokeSSLErrorBypassButtonPressed();
578 // Handler for the link to show help information about the connection tab.
579 - (void)showHelpPage:(id)sender {
580 presenter_->RecordWebsiteSettingsAction(
581 WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_HELP_OPENED);
582 webContents_->OpenURL(content::OpenURLParams(
583 GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(),
584 NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
587 // Create the contents of the Connection tab and add it to the given tab view.
588 // Returns a weak reference to the tab view item's view.
589 - (NSView*)addConnectionTabToTabView:(NSTabView*)tabView {
590 base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
591 base::scoped_nsobject<NSView> contentView(
592 [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
593 [contentView setAutoresizingMask:NSViewWidthSizable];
595 // Place all the text and images at the same position. The positions will be
596 // adjusted in performLayout.
597 NSPoint textPosition = NSMakePoint(
598 kFramePadding + kConnectionImageSize + kConnectionImageSpacing,
600 NSPoint imagePosition = NSMakePoint(kFramePadding, kFramePadding);
601 NSSize imageSize = NSMakeSize(kConnectionImageSize, kConnectionImageSize);
603 identityStatusIcon_ = [self addImageWithSize:imageSize
605 atPoint:imagePosition];
606 identityStatusDescriptionField_ =
607 [self addText:base::string16()
608 withSize:[NSFont smallSystemFontSize]
610 toView:contentView.get()
611 atPoint:textPosition];
613 separatorAfterIdentity_ = [self addSeparatorToView:contentView];
614 [separatorAfterIdentity_ setAutoresizingMask:NSViewWidthSizable];
616 connectionStatusIcon_ = [self addImageWithSize:imageSize
618 atPoint:imagePosition];
619 connectionStatusDescriptionField_ =
620 [self addText:base::string16()
621 withSize:[NSFont smallSystemFontSize]
623 toView:contentView.get()
624 atPoint:textPosition];
625 certificateInfoButton_ = nil; // This will be created only if necessary.
626 resetDecisionsButton_ = nil; // This will be created only if necessary.
627 separatorAfterConnection_ = [self addSeparatorToView:contentView];
628 [separatorAfterConnection_ setAutoresizingMask:NSViewWidthSizable];
630 NSString* helpButtonText = l10n_util::GetNSString(
631 IDS_PAGE_INFO_HELP_CENTER_LINK);
632 helpButton_ = [self addLinkButtonWithText:helpButtonText
634 [helpButton_ setTarget:self];
635 [helpButton_ setAction:@selector(showHelpPage:)];
637 [item setView:contentView.get()];
638 [tabView_ insertTabViewItem:item.get()
639 atIndex:WebsiteSettingsUI::TAB_ID_CONNECTION];
641 return contentView.get();
644 // Set the Y position of |view| to the given position, and return the position
645 // of its bottom edge.
646 - (CGFloat)setYPositionOfView:(NSView*)view to:(CGFloat)position {
647 NSRect frame = [view frame];
648 frame.origin.y = position;
649 [view setFrame:frame];
650 return position + NSHeight(frame);
653 - (void)setWidthOfView:(NSView*)view to:(CGFloat)width {
654 [view setFrameSize:NSMakeSize(width, NSHeight([view frame]))];
657 // Layout all of the controls in the window. This should be called whenever
658 // the content has changed.
659 - (void)performLayout {
660 // Make the content at least as wide as the permissions view.
661 CGFloat contentWidth = std::max([self defaultWindowWidth],
662 NSWidth([permissionsView_ frame]));
664 // Set the width of the content view now, so that all the text fields will
665 // be sized to fit before their heights and vertical positions are adjusted.
666 // The tab view will only resize the currently selected tab, so resize both
667 // tab content views manually.
668 [self setWidthOfView:contentView_ to:contentWidth];
669 [self setWidthOfView:permissionsTabContentView_ to:contentWidth];
670 [self setWidthOfView:connectionTabContentView_ to:contentWidth];
672 // Place the identity status immediately below the identity.
673 [self sizeTextFieldHeightToFit:identityField_];
674 [self sizeTextFieldHeightToFit:identityStatusField_];
675 CGFloat yPos = NSMaxY([identityField_ frame]) + kConnectionHeadlineSpacing;
676 yPos = [self setYPositionOfView:identityStatusField_ to:yPos];
678 // Lay out the Permissions tab.
680 yPos = [self setYPositionOfView:cookiesView_ to:kFramePadding];
682 // Put the permission info just below the link button.
683 yPos = [self setYPositionOfView:permissionsView_
684 to:NSMaxY([cookiesView_ frame]) + kFramePadding];
686 // Put the link button for site settings just below the permissions.
687 // TODO(palmer): set the position of this based on RTL/LTR.
688 // http://code.google.com/p/chromium/issues/detail?id=525304
689 [siteSettingsButton_ setFrameOrigin:NSMakePoint(kFramePadding, yPos)];
691 // Lay out the Connection tab.
693 // Lay out the identity status section.
694 [self sizeTextFieldHeightToFit:identityStatusDescriptionField_];
695 yPos = std::max(NSMaxY([identityStatusDescriptionField_ frame]),
696 NSMaxY([identityStatusIcon_ frame]));
697 if (certificateInfoButton_) {
698 NSRect certificateButtonFrame = [certificateInfoButton_ frame];
699 certificateButtonFrame.origin.x = NSMinX(
700 [identityStatusDescriptionField_ frame]);
701 certificateButtonFrame.origin.y = yPos + kVerticalSpacing;
702 [certificateInfoButton_ setFrame:certificateButtonFrame];
703 yPos = NSMaxY(certificateButtonFrame);
705 if (resetDecisionsButton_) {
706 NSRect resetDecisionsButtonFrame = [resetDecisionsButton_ frame];
707 resetDecisionsButtonFrame.origin.x =
708 NSMinX([identityStatusDescriptionField_ frame]);
709 resetDecisionsButtonFrame.origin.y = yPos + kVerticalSpacing;
710 [resetDecisionsButton_ setFrame:resetDecisionsButtonFrame];
711 yPos = NSMaxY(resetDecisionsButtonFrame);
713 yPos = [self setYPositionOfView:separatorAfterIdentity_
714 to:yPos + kVerticalSpacing];
715 yPos += kVerticalSpacing;
717 // Lay out the connection status section.
718 [self sizeTextFieldHeightToFit:connectionStatusDescriptionField_];
719 [self setYPositionOfView:connectionStatusIcon_ to:yPos];
720 [self setYPositionOfView:connectionStatusDescriptionField_ to:yPos];
721 yPos = std::max(NSMaxY([connectionStatusDescriptionField_ frame]),
722 NSMaxY([connectionStatusIcon_ frame]));
723 yPos = [self setYPositionOfView:separatorAfterConnection_
724 to:yPos + kVerticalSpacing];
725 yPos += kVerticalSpacing;
727 [self setYPositionOfView:helpButton_ to:yPos];
729 // Adjust the tab view size and place it below the identity status.
731 yPos = NSMaxY([identityStatusField_ frame]) + kTabStripTopSpacing;
732 yPos = [self setYPositionOfView:segmentedControl_ to:yPos];
734 CGFloat connectionTabHeight = NSMaxY([helpButton_ frame]) + kVerticalSpacing;
736 NSRect tabViewFrame = [tabView_ frame];
737 tabViewFrame.origin.y = yPos;
738 tabViewFrame.size.height = std::max(
739 connectionTabHeight, NSMaxY([siteSettingsButton_ frame]) + kFramePadding);
740 tabViewFrame.size.width = contentWidth;
741 [tabView_ setFrame:tabViewFrame];
743 // Adjust the contentView to fit everything.
744 [contentView_ setFrame:NSMakeRect(
745 0, 0, NSWidth(tabViewFrame), NSMaxY(tabViewFrame))];
747 [self sizeAndPositionWindow];
750 // Adjust the size of the window to match the size of the content, and position
751 // the bubble anchor appropriately.
752 - (void)sizeAndPositionWindow {
753 NSRect windowFrame = [contentView_ frame];
754 windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
756 // Adjust the origin by the difference in height.
757 windowFrame.origin = [[self window] frame].origin;
758 windowFrame.origin.y -= NSHeight(windowFrame) -
759 NSHeight([[self window] frame]);
761 // Resize the window. Only animate if the window is visible, otherwise it
762 // could be "growing" while it's opening, looking awkward.
763 [[self window] setFrame:windowFrame
765 animate:[[self window] isVisible]];
767 // Adjust the anchor for the bubble.
768 [self setAnchorPoint:AnchorPointForWindow([self parentWindow])];
771 // Sets properties on the given |field| to act as the title or description
772 // labels in the bubble.
773 - (void)configureTextFieldAsLabel:(NSTextField*)textField {
774 [textField setEditable:NO];
775 [textField setSelectable:YES];
776 [textField setDrawsBackground:NO];
777 [textField setBezeled:NO];
780 // Adjust the height of the given text field to match its text.
781 - (void)sizeTextFieldHeightToFit:(NSTextField*)textField {
782 NSRect frame = [textField frame];
784 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
786 [textField setFrame:frame];
789 // Create a new text field and add it to the given array of subviews.
790 // The array will retain a reference to the object.
791 - (NSTextField*)addText:(const base::string16&)text
792 withSize:(CGFloat)fontSize
795 atPoint:(NSPoint)point {
796 // Size the text to take up the full available width, with some padding.
797 // The height is arbitrary as it will be adjusted later.
798 CGFloat width = NSWidth([view frame]) - point.x - kFramePadding;
799 NSRect frame = NSMakeRect(point.x, point.y, width, 100);
800 base::scoped_nsobject<NSTextField> textField(
801 [[NSTextField alloc] initWithFrame:frame]);
802 [self configureTextFieldAsLabel:textField.get()];
803 [textField setStringValue:base::SysUTF16ToNSString(text)];
804 NSFont* font = bold ? [NSFont boldSystemFontOfSize:fontSize]
805 : [NSFont systemFontOfSize:fontSize];
806 [textField setFont:font];
807 [self sizeTextFieldHeightToFit:textField];
808 [textField setAutoresizingMask:NSViewWidthSizable];
809 [view addSubview:textField.get()];
810 return textField.get();
813 // Add an image as a subview of the given view, placed at a pre-determined x
814 // position and the given y position. Return the new NSImageView.
815 - (NSImageView*)addImageWithSize:(NSSize)size
817 atPoint:(NSPoint)point {
818 NSRect frame = NSMakeRect(point.x, point.y, size.width, size.height);
819 base::scoped_nsobject<NSImageView> imageView(
820 [[NSImageView alloc] initWithFrame:frame]);
821 [imageView setImageFrameStyle:NSImageFrameNone];
822 [view addSubview:imageView.get()];
823 return imageView.get();
826 // Add a separator as a subview of the given view. Return the new view.
827 - (NSView*)addSeparatorToView:(NSView*)view {
828 // Take up almost the full width of the container's frame.
829 CGFloat width = NSWidth([view frame]) - 2 * kFramePadding;
831 // Use an arbitrary position; it will be adjusted in performLayout.
832 NSBox* spacer = [self horizontalSeparatorWithFrame:NSMakeRect(
833 kFramePadding, 0, width, 0)];
834 [view addSubview:spacer];
838 // Add a link button with the given text to |view|.
839 - (NSButton*)addLinkButtonWithText:(NSString*)text toView:(NSView*)view {
840 // Frame size is arbitrary; it will be adjusted by the layout tweaker.
841 NSRect frame = NSMakeRect(kFramePadding, 0, 100, 10);
842 base::scoped_nsobject<NSButton> button(
843 [[NSButton alloc] initWithFrame:frame]);
844 base::scoped_nsobject<HyperlinkButtonCell> cell(
845 [[HyperlinkButtonCell alloc] initTextCell:text]);
846 [cell setControlSize:NSSmallControlSize];
847 [button setCell:cell.get()];
848 [button setButtonType:NSMomentaryPushInButton];
849 [button setBezelStyle:NSRegularSquareBezelStyle];
850 [view addSubview:button.get()];
852 [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
856 // Add a button with the given text to |view| setting the max size appropriately
857 // for the connection info section.
858 - (NSButton*)addButtonWithTextToConnectionSection:(NSString*)text
859 toView:(NSView*)view {
860 NSRect containerFrame = [view frame];
861 // Frame size is arbitrary; it will be adjusted by the layout tweaker.
862 NSRect frame = NSMakeRect(kFramePadding, 0, 100, 10);
863 base::scoped_nsobject<NSButton> button(
864 [[NSButton alloc] initWithFrame:frame]);
866 // Determine the largest possible size for this button. The size is the width
867 // of the connection section minus the padding on both sides minus the
868 // connection image size and spacing.
869 CGFloat maxTitleWidth = containerFrame.size.width - kFramePadding * 2 -
870 kConnectionImageSize - kConnectionImageSpacing;
872 base::scoped_nsobject<NSButtonCell> cell(
873 [[NSButtonCell alloc] initTextCell:text]);
874 [button setCell:cell.get()];
875 [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
877 // Ensure the containing view is large enough to contain the button with its
878 // widest possible title.
879 NSRect buttonFrame = [button frame];
880 buttonFrame.size.width = maxTitleWidth;
882 [button setFrame:buttonFrame];
883 [button setButtonType:NSMomentaryPushInButton];
884 [button setBezelStyle:NSRegularSquareBezelStyle];
885 [view addSubview:button.get()];
890 // Add a pop-up button for |permissionInfo| to the given view.
891 - (NSPopUpButton*)addPopUpButtonForPermission:
892 (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
894 atPoint:(NSPoint)point {
896 GURL url = webContents_ ? webContents_->GetURL() : GURL();
897 __block WebsiteSettingsBubbleController* weakSelf = self;
898 PermissionMenuModel::ChangeCallback callback =
899 base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) {
900 [weakSelf onPermissionChanged:permission.type to:permission.setting];
902 base::scoped_nsobject<PermissionSelectorButton> button(
903 [[PermissionSelectorButton alloc] initWithPermissionInfo:permissionInfo
905 withCallback:callback]);
906 // Determine the largest possible size for this button.
907 CGFloat maxTitleWidth = [button
908 maxTitleWidthForContentSettingsType:permissionInfo.type
909 withDefaultSetting:permissionInfo.default_setting];
911 // Ensure the containing view is large enough to contain the button with its
912 // widest possible title.
913 NSRect containerFrame = [view frame];
914 containerFrame.size.width = std::max(
915 NSWidth(containerFrame), point.x + maxTitleWidth + kFramePadding);
916 [view setFrame:containerFrame];
917 [view addSubview:button.get()];
921 // Called when the user changes the setting of a permission.
922 - (void)onPermissionChanged:(ContentSettingsType)permissionType
923 to:(ContentSetting)newSetting {
925 presenter_->OnSitePermissionChanged(permissionType, newSetting);
928 // Called when the user changes the selected segment in the segmented control.
929 - (void)tabSelected:(id)sender {
930 NSInteger index = [segmentedControl_ selectedSegment];
932 case WebsiteSettingsUI::TAB_ID_PERMISSIONS:
933 presenter_->RecordWebsiteSettingsAction(
934 WebsiteSettings::WEBSITE_SETTINGS_PERMISSIONS_TAB_SELECTED);
936 case WebsiteSettingsUI::TAB_ID_CONNECTION:
937 presenter_->RecordWebsiteSettingsAction(
938 WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_TAB_SELECTED);
943 [tabView_ selectTabViewItemAtIndex:index];
946 // Adds a new row to the UI listing the permissions. Returns the NSPoint of the
947 // last UI element added (either the permission button, in LTR, or the text
949 - (NSPoint)addPermission:
950 (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
952 atPoint:(NSPoint)point {
953 base::string16 labelText =
954 WebsiteSettingsUI::PermissionTypeToUIString(permissionInfo.type) +
955 base::ASCIIToUTF16(":");
957 base::i18n::RIGHT_TO_LEFT == base::i18n::GetStringDirection(labelText);
958 base::scoped_nsobject<NSImage> image(
959 [WebsiteSettingsUI::GetPermissionIcon(permissionInfo).ToNSImage()
963 NSImageView* imageView;
964 NSPopUpButton* button;
967 CGFloat viewWidth = NSWidth([view frame]);
970 point.x = NSWidth([view frame]) - kPermissionImageSize -
971 kPermissionImageSpacing - kFramePadding;
972 imageView = [self addImageWithSize:[image size] toView:view atPoint:point];
973 [imageView setImage:image];
974 point.x -= kPermissionImageSpacing;
976 label = [self addText:labelText
977 withSize:[NSFont smallSystemFontSize]
982 point.x -= NSWidth([label frame]);
983 [label setFrameOrigin:point];
985 position = NSMakePoint(point.x, point.y);
986 button = [self addPopUpButtonForPermission:permissionInfo
989 position.x -= NSWidth([button frame]);
990 [button setFrameOrigin:position];
992 imageView = [self addImageWithSize:[image size] toView:view atPoint:point];
993 [imageView setImage:image];
994 point.x += kPermissionImageSize + kPermissionImageSpacing;
996 label = [self addText:labelText
997 withSize:[NSFont smallSystemFontSize]
1003 position = NSMakePoint(NSMaxX([label frame]), point.y);
1004 button = [self addPopUpButtonForPermission:permissionInfo
1009 [view setFrameSize:NSMakeSize(viewWidth, NSHeight([view frame]))];
1011 // Adjust the vertical position of the button so that its title text is
1012 // aligned with the label. Assumes that the text is the same size in both.
1013 // Also adjust the horizontal position to remove excess space due to the
1015 NSRect titleRect = [[button cell] titleRectForBounds:[button bounds]];
1017 position.x += kPermissionPopUpXSpacing;
1019 position.x -= titleRect.origin.x - kPermissionPopUpXSpacing;
1021 position.y -= titleRect.origin.y;
1022 [button setFrameOrigin:position];
1024 // Align the icon with the text.
1025 [self alignPermissionIcon:imageView withTextField:label];
1027 // Permissions specified by policy or an extension cannot be changed.
1028 if (permissionInfo.source == content_settings::SETTING_SOURCE_EXTENSION ||
1029 permissionInfo.source == content_settings::SETTING_SOURCE_POLICY) {
1030 [button setEnabled:NO];
1033 NSRect buttonFrame = [button frame];
1034 return NSMakePoint(NSMaxX(buttonFrame), NSMaxY(buttonFrame));
1037 // Align an image with a text field by vertically centering the image on
1038 // the cap height of the first line of text.
1039 - (void)alignPermissionIcon:(NSImageView*)imageView
1040 withTextField:(NSTextField*)textField {
1041 NSFont* font = [textField font];
1043 // Calculate the offset from the top of the text field.
1044 CGFloat capHeight = [font capHeight];
1045 CGFloat offset = (kPermissionImageSize - capHeight) / 2 -
1046 ([font ascender] - capHeight) - kPermissionImageYAdjust;
1048 NSRect frame = [imageView frame];
1049 frame.origin.y -= offset;
1050 [imageView setFrame:frame];
1053 // Set the content of the identity and identity status fields.
1054 - (void)setIdentityInfo:(const WebsiteSettingsUI::IdentityInfo&)identityInfo {
1055 [identityField_ setStringValue:
1056 base::SysUTF8ToNSString(identityInfo.site_identity)];
1057 [identityStatusField_ setStringValue:base::SysUTF16ToNSString(
1058 identityInfo.GetSecuritySummary())];
1060 // If there is a certificate, add a button for viewing the certificate info.
1061 certificateId_ = identityInfo.cert_id;
1062 if (certificateId_) {
1063 if (!certificateInfoButton_) {
1064 NSString* text = l10n_util::GetNSString(IDS_PAGEINFO_CERT_INFO_BUTTON);
1065 certificateInfoButton_ = [self addLinkButtonWithText:text
1066 toView:connectionTabContentView_];
1068 [certificateInfoButton_ setTarget:self];
1069 [certificateInfoButton_ setAction:@selector(showCertificateInfo:)];
1072 // Check if a security decision has been made, and if so, add a button to
1073 // allow the user to retract their decision.
1074 if (identityInfo.show_ssl_decision_revoke_button) {
1075 NSString* text = l10n_util::GetNSString(
1076 IDS_PAGEINFO_RESET_INVALID_CERTIFICATE_DECISIONS_BUTTON);
1077 resetDecisionsButton_ =
1078 [self addButtonWithTextToConnectionSection:text
1079 toView:connectionTabContentView_];
1080 [resetDecisionsButton_ setTarget:self];
1081 [resetDecisionsButton_ setAction:@selector(resetCertificateDecisions:)];
1084 certificateInfoButton_ = nil;
1087 [identityStatusIcon_ setImage:WebsiteSettingsUI::GetIdentityIcon(
1088 identityInfo.identity_status).ToNSImage()];
1089 [identityStatusDescriptionField_ setStringValue:
1090 base::SysUTF8ToNSString(identityInfo.identity_status_description)];
1092 [connectionStatusIcon_ setImage:WebsiteSettingsUI::GetConnectionIcon(
1093 identityInfo.connection_status).ToNSImage()];
1094 [connectionStatusDescriptionField_ setStringValue:
1095 base::SysUTF8ToNSString(identityInfo.connection_status_description)];
1097 [self performLayout];
1100 - (void)setCookieInfo:(const CookieInfoList&)cookieInfoList {
1101 // A result of re-ordering of the permissions (crbug.com/444244) is
1102 // that sometimes permissions may not be displayed at all, so it's
1103 // incorrect to check they are set before the cookie info.
1105 // |cookieInfoList| should only ever have 2 items: first- and third-party
1107 DCHECK_EQ(cookieInfoList.size(), 2u);
1108 base::string16 firstPartyLabelText;
1109 base::string16 thirdPartyLabelText;
1110 for (const auto& i : cookieInfoList) {
1111 if (i.is_first_party) {
1112 firstPartyLabelText =
1113 l10n_util::GetStringFUTF16(IDS_WEBSITE_SETTINGS_FIRST_PARTY_SITE_DATA,
1114 base::IntToString16(i.allowed));
1116 thirdPartyLabelText =
1117 l10n_util::GetStringFUTF16(IDS_WEBSITE_SETTINGS_THIRD_PARTY_SITE_DATA,
1118 base::IntToString16(i.allowed));
1122 base::string16 sectionTitle =
1123 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_TITLE_SITE_DATA);
1124 bool isRTL = base::i18n::RIGHT_TO_LEFT ==
1125 base::i18n::GetStringDirection(firstPartyLabelText);
1127 [cookiesView_ setSubviews:[NSArray array]];
1128 NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1132 CGFloat viewWidth = NSWidth([cookiesView_ frame]);
1134 NSTextField* header = [self addText:sectionTitle
1135 withSize:[NSFont smallSystemFontSize]
1138 atPoint:controlOrigin];
1142 controlOrigin.x = viewWidth - kFramePadding - NSWidth([header frame]);
1143 [header setFrameOrigin:controlOrigin];
1145 controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1146 controlOrigin.y += kPermissionsTabSpacing;
1148 // Reset X for the cookie image.
1150 controlOrigin.x = viewWidth - kPermissionImageSize -
1151 kPermissionImageSpacing - kFramePadding;
1154 WebsiteSettingsUI::PermissionInfo info;
1155 info.type = CONTENT_SETTINGS_TYPE_COOKIES;
1156 info.setting = CONTENT_SETTING_ALLOW;
1157 NSImage* image = WebsiteSettingsUI::GetPermissionIcon(info).ToNSImage();
1158 NSImageView* imageView = [self addImageWithSize:[image size]
1160 atPoint:controlOrigin];
1161 [imageView setImage:image];
1163 base::string16 comma = base::ASCIIToUTF16(", ");
1164 NSString* cookieButtonText = base::SysUTF16ToNSString(firstPartyLabelText);
1167 NSButton* cookiesButton =
1168 [self addLinkButtonWithText:cookieButtonText toView:cookiesView_];
1169 [cookiesButton setTarget:self];
1170 [cookiesButton setAction:@selector(showCookiesAndSiteData:)];
1171 controlOrigin.x -= NSWidth([cookiesButton frame]);
1172 [cookiesButton setFrameOrigin:controlOrigin];
1174 label = [self addText:comma + thirdPartyLabelText
1175 withSize:[NSFont smallSystemFontSize]
1178 atPoint:controlOrigin];
1180 controlOrigin.x -= NSWidth([label frame]) - kTextLabelXPadding;
1181 [label setFrameOrigin:controlOrigin];
1183 controlOrigin.x += kPermissionImageSize + kPermissionImageSpacing;
1185 NSButton* cookiesButton =
1186 [self addLinkButtonWithText:cookieButtonText toView:cookiesView_];
1187 [cookiesButton setTarget:self];
1188 [cookiesButton setAction:@selector(showCookiesAndSiteData:)];
1189 [cookiesButton setFrameOrigin:controlOrigin];
1191 controlOrigin.x += NSWidth([cookiesButton frame]) - kTextLabelXPadding;
1193 label = [self addText:comma + thirdPartyLabelText
1194 withSize:[NSFont smallSystemFontSize]
1197 atPoint:controlOrigin];
1201 // Align the icon with the text.
1202 [self alignPermissionIcon:imageView withTextField:label];
1204 controlOrigin.y += NSHeight([label frame]) + kPermissionsTabSpacing;
1206 [cookiesView_ setFrameSize:
1207 NSMakeSize(NSWidth([cookiesView_ frame]), controlOrigin.y)];
1209 [self performLayout];
1212 - (void)setPermissionInfo:(const PermissionInfoList&)permissionInfoList {
1213 [permissionsView_ setSubviews:[NSArray array]];
1214 NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1216 if (permissionInfoList.size() > 0) {
1217 base::string16 sectionTitle = l10n_util::GetStringUTF16(
1218 IDS_WEBSITE_SETTINGS_TITLE_SITE_PERMISSIONS);
1219 bool isRTL = base::i18n::RIGHT_TO_LEFT ==
1220 base::i18n::GetStringDirection(sectionTitle);
1221 NSTextField* header = [self addText:sectionTitle
1222 withSize:[NSFont smallSystemFontSize]
1224 toView:permissionsView_
1225 atPoint:controlOrigin];
1228 controlOrigin.x = NSWidth([permissionsView_ frame]) - kFramePadding -
1229 NSWidth([header frame]);
1230 [header setFrameOrigin:controlOrigin];
1232 controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1234 for (PermissionInfoList::const_iterator permission =
1235 permissionInfoList.begin();
1236 permission != permissionInfoList.end();
1238 controlOrigin.y += kPermissionsTabSpacing;
1239 NSPoint rowBottomRight = [self addPermission:*permission
1240 toView:permissionsView_
1241 atPoint:controlOrigin];
1242 controlOrigin.y = rowBottomRight.y;
1244 controlOrigin.y += kFramePadding;
1247 [permissionsView_ setFrameSize:
1248 NSMakeSize(NSWidth([permissionsView_ frame]), controlOrigin.y)];
1249 [self performLayout];
1252 - (void)setSelectedTab:(WebsiteSettingsUI::TabId)tabId {
1253 NSInteger index = static_cast<NSInteger>(tabId);
1254 [segmentedControl_ setSelectedSegment:index];
1255 [tabView_ selectTabViewItemAtIndex:index];
1260 WebsiteSettingsUIBridge::WebsiteSettingsUIBridge(
1261 content::WebContents* web_contents)
1262 : content::WebContentsObserver(web_contents),
1263 web_contents_(web_contents),
1264 bubble_controller_(nil) {}
1266 WebsiteSettingsUIBridge::~WebsiteSettingsUIBridge() {
1269 void WebsiteSettingsUIBridge::set_bubble_controller(
1270 WebsiteSettingsBubbleController* controller) {
1271 bubble_controller_ = controller;
1274 void WebsiteSettingsUIBridge::Show(gfx::NativeWindow parent,
1276 content::WebContents* web_contents,
1278 const content::SSLStatus& ssl) {
1279 if (chrome::ToolkitViewsDialogsEnabled()) {
1280 chrome::ShowWebsiteSettingsBubbleViewsAtPoint(
1281 gfx::ScreenPointFromNSPoint(AnchorPointForWindow(parent)), profile,
1282 web_contents, url, ssl);
1286 bool is_internal_page = InternalChromePage(url);
1288 // Create the bridge. This will be owned by the bubble controller.
1289 WebsiteSettingsUIBridge* bridge = new WebsiteSettingsUIBridge(web_contents);
1291 // Create the bubble controller. It will dealloc itself when it closes.
1292 WebsiteSettingsBubbleController* bubble_controller =
1293 [[WebsiteSettingsBubbleController alloc]
1294 initWithParentWindow:parent
1295 websiteSettingsUIBridge:bridge
1296 webContents:web_contents
1297 isInternalPage:is_internal_page];
1299 if (!is_internal_page) {
1300 // Initialize the presenter, which holds the model and controls the UI.
1301 // This is also owned by the bubble controller.
1302 WebsiteSettings* presenter = new WebsiteSettings(
1304 TabSpecificContentSettings::FromWebContents(web_contents), web_contents,
1305 url, ssl, content::CertStore::GetInstance());
1306 [bubble_controller setPresenter:presenter];
1309 [bubble_controller showWindow:nil];
1312 void WebsiteSettingsUIBridge::SetIdentityInfo(
1313 const WebsiteSettingsUI::IdentityInfo& identity_info) {
1314 [bubble_controller_ setIdentityInfo:identity_info];
1317 void WebsiteSettingsUIBridge::RenderFrameDeleted(
1318 content::RenderFrameHost* render_frame_host) {
1319 if (render_frame_host == web_contents_->GetMainFrame()) {
1320 [bubble_controller_ close];
1324 void WebsiteSettingsUIBridge::SetCookieInfo(
1325 const CookieInfoList& cookie_info_list) {
1326 [bubble_controller_ setCookieInfo:cookie_info_list];
1329 void WebsiteSettingsUIBridge::SetPermissionInfo(
1330 const PermissionInfoList& permission_info_list) {
1331 [bubble_controller_ setPermissionInfo:permission_info_list];
1334 void WebsiteSettingsUIBridge::SetSelectedTab(TabId tab_id) {
1335 [bubble_controller_ setSelectedTab:tab_id];