Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / website_settings / website_settings_bubble_controller.mm
blob5306b1e6178b167c0472501e7036c08ab67cdb02
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"
7 #include <cmath>
9 #import <AppKit/AppKit.h>
11 #include "base/mac/bind_objc_block.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/sys_string_conversions.h"
14 #import "chrome/browser/certificate_viewer.h"
15 #include "chrome/browser/infobars/infobar_service.h"
16 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
17 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
18 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
19 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
20 #import "chrome/browser/ui/cocoa/website_settings/permission_selector_button.h"
21 #import "chrome/browser/ui/tab_dialogs.h"
22 #include "chrome/browser/ui/website_settings/permission_menu_model.h"
23 #include "chrome/browser/ui/website_settings/website_settings_utils.h"
24 #include "chrome/common/url_constants.h"
25 #include "chrome/grit/chromium_strings.h"
26 #include "chrome/grit/generated_resources.h"
27 #include "content/public/browser/cert_store.h"
28 #include "content/public/browser/page_navigator.h"
29 #include "content/public/browser/ssl_host_state_delegate.h"
30 #include "content/public/browser/user_metrics.h"
31 #include "content/public/browser/web_contents.h"
32 #include "grit/theme_resources.h"
33 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
34 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
35 #import "ui/base/cocoa/flipped_view.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
40 namespace {
42 // The default width of the window, in view coordinates. It may be larger to
43 // fit the content.
44 const CGFloat kDefaultWindowWidth = 310;
46 // Spacing in between sections.
47 const CGFloat kVerticalSpacing = 10;
49 // Padding between the window frame and content.
50 const CGFloat kFramePadding = 20;
52 // Padding between the window frame and content for the internal page bubble.
53 const CGFloat kInternalPageFramePadding = 10;
55 // Spacing between the headlines and description text on the Connection tab.
56 const CGFloat kConnectionHeadlineSpacing = 2;
58 // Spacing between images on the Connection tab and the text.
59 const CGFloat kConnectionImageSpacing = 10;
61 // Spacing between the image and text for internal pages.
62 const CGFloat kInternalPageImageSpacing = 10;
64 // Square size of the images on the Connections tab.
65 const CGFloat kConnectionImageSize = 30;
67 // Square size of the image that is shown for internal pages.
68 const CGFloat kInternalPageImageSize = 26;
70 // Square size of the permission images.
71 const CGFloat kPermissionImageSize = 19;
73 // Vertical adjustment for the permission images. They have an extra pixel of
74 // padding on the bottom edge.
75 const CGFloat kPermissionImageYAdjust = 1;
77 // Spacing between a permission image and the text.
78 const CGFloat kPermissionImageSpacing = 3;
80 // The spacing between individual items in the Permissions tab.
81 const CGFloat kPermissionsTabSpacing = 12;
83 // Extra spacing after a headline on the Permissions tab.
84 const CGFloat kPermissionsHeadlineSpacing = 2;
86 // The amount of horizontal space between a permission label and the popup.
87 const CGFloat kPermissionPopUpXSpacing = 3;
89 // The extra space to the left of the first tab in the tab strip.
90 const CGFloat kTabStripXPadding = kFramePadding;
92 // The amount of space between the visual borders of adjacent tabs.
93 const CGFloat kTabSpacing = 4;
95 // The amount of space above the tab strip.
96 const CGFloat kTabStripTopSpacing = 14;
98 // The height of the clickable area of the tab.
99 const CGFloat kTabHeight = 28;
101 // The amount of space above tab labels.
102 const CGFloat kTabLabelTopPadding = 6;
104 // The amount of padding to leave on either side of the tab label.
105 const CGFloat kTabLabelXPadding = 12;
107 // Return the text color to use for the identity status when the site's
108 // identity has been verified.
109 NSColor* IdentityVerifiedTextColor() {
110   // RGB components are specified using integer RGB [0-255] values for easy
111   // comparison to other platforms.
112   return [NSColor colorWithCalibratedRed:0x07/255.0
113                                    green:0x95/255.0
114                                     blue:0
115                                    alpha:1.0];
118 }  // namespace
120 @interface WebsiteSettingsTabSegmentedCell : NSSegmentedCell {
121  @private
122   base::scoped_nsobject<NSImage> tabstripCenterImage_;
123   base::scoped_nsobject<NSImage> tabstripLeftImage_;
124   base::scoped_nsobject<NSImage> tabstripRightImage_;
126   base::scoped_nsobject<NSImage> tabCenterImage_;
127   base::scoped_nsobject<NSImage> tabLeftImage_;
128   base::scoped_nsobject<NSImage> tabRightImage_;
130   // Key track of the index of segment which has keyboard focus. This is not
131   // the same as the currently selected segment.
132   NSInteger keySegment_;
135 // The text attributes to use for the tab labels.
136 + (NSDictionary*)textAttributes;
138 @end
140 @implementation WebsiteSettingsTabSegmentedCell
142 + (NSDictionary*)textAttributes {
143   NSFont* smallSystemFont =
144       [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
145   return @{ NSFontAttributeName : smallSystemFont };
148 - (id)init {
149   if ((self = [super init])) {
150     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
151     tabstripCenterImage_.reset(rb.GetNativeImageNamed(
152         IDR_WEBSITE_SETTINGS_TABSTRIP_CENTER).CopyNSImage());
153     tabstripLeftImage_.reset(rb.GetNativeImageNamed(
154         IDR_WEBSITE_SETTINGS_TABSTRIP_LEFT).CopyNSImage());
155     tabstripRightImage_.reset(rb.GetNativeImageNamed(
156         IDR_WEBSITE_SETTINGS_TABSTRIP_RIGHT).CopyNSImage());
158     tabCenterImage_.reset(
159         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_CENTER2).CopyNSImage());
160     tabLeftImage_.reset(
161         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_LEFT2).CopyNSImage());
162     tabRightImage_.reset(
163         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_RIGHT2).CopyNSImage());
164   }
165   return self;
168 // Called when keyboard focus in the segmented control is moved forward.
169 - (void)makeNextSegmentKey {
170   [super makeNextSegmentKey];
171   keySegment_ = (keySegment_ + 1) % [self segmentCount];
174 // Called when keyboard focus in the segmented control is moved backwards.
175 - (void)makePreviousSegmentKey {
176   [super makePreviousSegmentKey];
177   if (--keySegment_ < 0)
178     keySegment_ += [self segmentCount];
181 - (void)setSelectedSegment:(NSInteger)selectedSegment {
182   keySegment_ = selectedSegment;
183   [super setSelectedSegment:selectedSegment];
186 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
187   CGFloat tabstripHeight = [tabCenterImage_ size].height;
189   // Draw the tab for the selected segment.
190   NSRect tabRect = [self hitRectForSegment:[self selectedSegment]];
191   tabRect.origin.y = 0;
192   tabRect.size.height = tabstripHeight;
194   NSDrawThreePartImage(tabRect,
195                        tabLeftImage_,
196                        tabCenterImage_,
197                        tabRightImage_,
198                        /*vertical=*/ NO,
199                        NSCompositeSourceOver,
200                        1,
201                        /*flipped=*/ YES);
203   // Draw the background to the left of the selected tab.
204   NSRect backgroundRect = NSMakeRect(0, 0, NSMinX(tabRect), tabstripHeight);
205   NSDrawThreePartImage(backgroundRect,
206                        nil,
207                        tabstripCenterImage_,
208                        tabstripLeftImage_,
209                        /*vertical=*/ NO,
210                        NSCompositeSourceOver,
211                        1,
212                        /*flipped=*/ YES);
214   // Draw the background to the right of the selected tab.
215   backgroundRect.origin.x = NSMaxX(tabRect);
216   backgroundRect.size.width = NSMaxX(cellFrame) - NSMaxX(tabRect);
217   NSDrawThreePartImage(backgroundRect,
218                        tabstripRightImage_,
219                        tabstripCenterImage_,
220                        nil,
221                        /*vertical=*/ NO,
222                        NSCompositeSourceOver,
223                        1,
224                        /*flipped=*/ YES);
226   // Call the superclass method to trigger drawing of the tab labels.
227   NSRect interiorFrame = cellFrame;
228   interiorFrame.size.width = 0;
229   for (NSInteger i = 0; i < [self segmentCount]; ++i)
230     interiorFrame.size.width += [self widthForSegment:i];
231   [self drawInteriorWithFrame:interiorFrame inView:controlView];
233   if ([[controlView window] firstResponder] == controlView)
234     [self drawFocusRect];
237 - (void)drawFocusRect {
238   gfx::ScopedNSGraphicsContextSaveGState scoped_state;
239   NSSetFocusRingStyle(NSFocusRingOnly);
240   [[NSColor keyboardFocusIndicatorColor] set];
241   NSFrameRect([self hitRectForSegment:keySegment_]);
244 // Returns the segment number for the left-most positioned segment.
245 // On Right-to-Left languages, segment 0 is on the right.
246 - (NSInteger)leftSegment {
247   BOOL isRTL = [self userInterfaceLayoutDirection] ==
248                    NSUserInterfaceLayoutDirectionRightToLeft;
249   return isRTL ? [self segmentCount] - 1 : 0;
252 // Return the hit rect (i.e., the visual bounds of the tab) for
253 // the given segment.
254 - (NSRect)hitRectForSegment:(NSInteger)segment {
255   CGFloat tabstripHeight = [tabCenterImage_ size].height;
256   DCHECK_GT(tabstripHeight, kTabHeight);
257   DCHECK([self segmentCount] == 2);  // Assume 2 segments to keep things simple.
258   NSInteger leftSegment = [self leftSegment];
259   CGFloat xOrigin = segment == leftSegment ? kTabStripXPadding
260                                            : [self widthForSegment:leftSegment];
261   NSRect rect = NSMakeRect(xOrigin, tabstripHeight - kTabHeight,
262                            [self widthForSegment:segment] - kTabStripXPadding,
263                            kTabHeight);
264   return NSInsetRect(rect, kTabSpacing / 2, 0);
267 - (void)drawSegment:(NSInteger)segment
268             inFrame:(NSRect)tabFrame
269            withView:(NSView*)controlView {
270   // Adjust the tab's frame so that the label appears centered in the tab.
271   if (segment == [self leftSegment])
272     tabFrame.origin.x += kTabStripXPadding;
273   tabFrame.size.width -= kTabStripXPadding;
274   tabFrame.origin.y += kTabLabelTopPadding;
275   tabFrame.size.height -= kTabLabelTopPadding;
277   // Center the label's frame in the tab's frame.
278   NSString* label = [self labelForSegment:segment];
279   NSDictionary* textAttributes =
280       [WebsiteSettingsTabSegmentedCell textAttributes];
281   NSSize textSize = [label sizeWithAttributes:textAttributes];
282   NSRect labelFrame;
283   labelFrame.size = textSize;
284   labelFrame.origin.x =
285       tabFrame.origin.x + (NSWidth(tabFrame) - textSize.width) / 2.0;
286   labelFrame.origin.y =
287       tabFrame.origin.y + (NSHeight(tabFrame) - textSize.height) / 2.0;
289   [label drawInRect:labelFrame withAttributes:textAttributes];
292 // Overrides the default tracking behavior to only respond to clicks inside the
293 // visual borders of the tab.
294 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
295   NSInteger segmentCount = [self segmentCount];
296   for (NSInteger i = 0; i < segmentCount; ++i) {
297     if (NSPointInRect(startPoint, [self hitRectForSegment:i]))
298       return YES;
299   }
300   return NO;
303 // Overrides the default cell height to take up the full height of the
304 // segmented control. Otherwise, clicks on the lower part of a tab will be
305 // ignored.
306 - (NSSize)cellSizeForBounds:(NSRect)aRect {
307   return NSMakeSize([super cellSizeForBounds:aRect].width,
308                     [tabstripCenterImage_ size].height);
311 // Returns the minimum size required to display this cell.
312 // It should always be exactly as tall as the tabstrip background image.
313 - (NSSize)cellSize {
314   return NSMakeSize([super cellSize].width,
315                     [tabstripCenterImage_ size].height);
317 @end
319 @implementation WebsiteSettingsBubbleController
321 - (CGFloat)defaultWindowWidth {
322   return kDefaultWindowWidth;
325 - (id)initWithParentWindow:(NSWindow*)parentWindow
326    websiteSettingsUIBridge:(WebsiteSettingsUIBridge*)bridge
327                webContents:(content::WebContents*)webContents
328             isInternalPage:(BOOL)isInternalPage {
329   DCHECK(parentWindow);
331   webContents_ = webContents;
333   // Use an arbitrary height; it will be changed in performLayout.
334   NSRect contentRect = NSMakeRect(0, 0, [self defaultWindowWidth], 1);
335   // Create an empty window into which content is placed.
336   base::scoped_nsobject<InfoBubbleWindow> window(
337       [[InfoBubbleWindow alloc] initWithContentRect:contentRect
338                                           styleMask:NSBorderlessWindowMask
339                                             backing:NSBackingStoreBuffered
340                                               defer:NO]);
342   if ((self = [super initWithWindow:window.get()
343                        parentWindow:parentWindow
344                          anchoredAt:NSZeroPoint])) {
345     [[self bubble] setArrowLocation:info_bubble::kTopLeft];
347     // Create the container view that uses flipped coordinates.
348     NSRect contentFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
349     contentView_.reset(
350         [[FlippedView alloc] initWithFrame:contentFrame]);
352     // Replace the window's content.
353     [[[self window] contentView] setSubviews:
354         [NSArray arrayWithObject:contentView_.get()]];
356     if (isInternalPage)
357       [self initializeContentsForInternalPage];
358     else
359       [self initializeContents];
361     bridge_.reset(bridge);
362     bridge_->set_bubble_controller(self);
363   }
364   return self;
367 - (void)windowWillClose:(NSNotification*)notification {
368   if (presenter_.get())
369     presenter_->OnUIClosing();
370   presenter_.reset();
371   [super windowWillClose:notification];
374 - (void)setPresenter:(WebsiteSettings*)presenter {
375   presenter_.reset(presenter);
378 // Create the subviews for the bubble for internal Chrome pages.
379 - (void)initializeContentsForInternalPage {
380   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
382   NSPoint controlOrigin = NSMakePoint(
383       kInternalPageFramePadding,
384       kInternalPageFramePadding + info_bubble::kBubbleArrowHeight);
385   NSSize imageSize = NSMakeSize(kInternalPageImageSize,
386                                 kInternalPageImageSize);
387   NSImageView* imageView = [self addImageWithSize:imageSize
388                                            toView:contentView_
389                                           atPoint:controlOrigin];
390   [imageView setImage:rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_26).ToNSImage()];
392   controlOrigin.x += NSWidth([imageView frame]) + kInternalPageImageSpacing;
393   base::string16 text = l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE);
394   NSTextField* textField = [self addText:text
395                                 withSize:[NSFont smallSystemFontSize]
396                                     bold:NO
397                                   toView:contentView_
398                                  atPoint:controlOrigin];
399   // Center the text vertically with the image.
400   NSRect textFrame = [textField frame];
401   textFrame.origin.y += (imageSize.height - NSHeight(textFrame)) / 2;
402   [textField setFrame:textFrame];
404   // Adjust the contentView to fit everything.
405   CGFloat maxY = std::max(NSMaxY([imageView frame]), NSMaxY(textFrame));
406   [contentView_ setFrame:NSMakeRect(
407       0, 0, [self defaultWindowWidth], maxY + kInternalPageFramePadding)];
409   [self sizeAndPositionWindow];
412 // Create the subviews for the website settings bubble.
413 - (void)initializeContents {
414   // Keeps track of the position that the next control should be drawn at.
415   NSPoint controlOrigin = NSMakePoint(
416       kFramePadding,
417       kFramePadding + info_bubble::kBubbleArrowHeight);
419   // Create a text field (empty for now) to show the site identity.
420   identityField_ = [self addText:base::string16()
421                         withSize:[NSFont systemFontSize]
422                             bold:YES
423                           toView:contentView_
424                          atPoint:controlOrigin];
425   controlOrigin.y +=
426       NSHeight([identityField_ frame]) + kConnectionHeadlineSpacing;
428   // Create a text field to identity status (e.g. verified, not verified).
429   identityStatusField_ = [self addText:base::string16()
430                               withSize:[NSFont smallSystemFontSize]
431                                   bold:NO
432                                 toView:contentView_
433                                atPoint:controlOrigin];
435   // Create the tab view and its two tabs.
437   base::scoped_nsobject<WebsiteSettingsTabSegmentedCell> cell(
438       [[WebsiteSettingsTabSegmentedCell alloc] init]);
439   CGFloat tabstripHeight = [cell cellSize].height;
440   NSRect tabstripFrame = NSMakeRect(
441       0, 0, [self defaultWindowWidth], tabstripHeight);
442   segmentedControl_.reset(
443       [[NSSegmentedControl alloc] initWithFrame:tabstripFrame]);
444   [segmentedControl_ setCell:cell];
445   [segmentedControl_ setSegmentCount:WebsiteSettingsUI::NUM_TAB_IDS];
446   [segmentedControl_ setTarget:self];
447   [segmentedControl_ setAction:@selector(tabSelected:)];
448   [segmentedControl_ setAutoresizingMask:NSViewWidthSizable];
450   NSDictionary* textAttributes =
451       [WebsiteSettingsTabSegmentedCell textAttributes];
453   // Create the "Permissions" tab.
454   NSString* label = l10n_util::GetNSString(
455       IDS_WEBSITE_SETTINGS_TAB_LABEL_PERMISSIONS);
456   [segmentedControl_ setLabel:label
457                    forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
458   NSSize textSize = [label sizeWithAttributes:textAttributes];
459   CGFloat tabWidth = textSize.width + 2 * kTabLabelXPadding;
461   // Create the "Connection" tab.
462   label = l10n_util::GetNSString(IDS_WEBSITE_SETTINGS_TAB_LABEL_CONNECTION);
463   textSize = [label sizeWithAttributes:textAttributes];
464   [segmentedControl_ setLabel:label
465                    forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
467   DCHECK_EQ([segmentedControl_ segmentCount], WebsiteSettingsUI::NUM_TAB_IDS);
469   // Make both tabs the width of the widest. The first segment has some
470   // additional padding that is not part of the tab, which is used for drawing
471   // the background of the tab strip.
472   tabWidth = std::max(tabWidth,
473                       textSize.width + 2 * kTabLabelXPadding);
474   [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
475                    forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
476   [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
477                    forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
479   [segmentedControl_ setFont:[textAttributes objectForKey:NSFontAttributeName]];
480   [contentView_ addSubview:segmentedControl_];
482   NSRect tabFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
483   tabView_.reset([[NSTabView alloc] initWithFrame:tabFrame]);
484   [tabView_ setTabViewType:NSNoTabsNoBorder];
485   [tabView_ setDrawsBackground:NO];
486   [tabView_ setControlSize:NSSmallControlSize];
487   [contentView_ addSubview:tabView_.get()];
489   permissionsTabContentView_ = [self addPermissionsTabToTabView:tabView_];
490   connectionTabContentView_ = [self addConnectionTabToTabView:tabView_];
491   [self setSelectedTab:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
493   [self performLayout];
496 // Create the contents of the Permissions tab and add it to the given tab view.
497 // Returns a weak reference to the tab view item's view.
498 - (NSView*)addPermissionsTabToTabView:(NSTabView*)tabView {
499   base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
500   [tabView_ insertTabViewItem:item.get()
501                       atIndex:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
502   base::scoped_nsobject<NSView> contentView(
503       [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
504   [contentView setAutoresizingMask:NSViewWidthSizable];
505   [item setView:contentView.get()];
507   // Initialize the two containers that hold the controls. The initial frames
508   // are arbitrary, and will be adjusted after the controls are laid out.
509   cookiesView_ = [[[FlippedView alloc]
510       initWithFrame:[tabView_ contentRect]] autorelease];
511   [cookiesView_ setAutoresizingMask:NSViewWidthSizable];
513   permissionsView_ = [[[FlippedView alloc]
514       initWithFrame:[tabView_ contentRect]] autorelease];
516   [contentView addSubview:cookiesView_];
517   [contentView addSubview:permissionsView_];
519   // Create the link button to view cookies and site data. Its position will be
520   // set in performLayout.
521   NSString* cookieButtonText = l10n_util::GetNSString(
522       IDS_WEBSITE_SETTINGS_SHOW_SITE_DATA);
523   cookiesButton_ = [self addLinkButtonWithText:cookieButtonText
524                                         toView:contentView];
525   [cookiesButton_ setTarget:self];
526   [cookiesButton_ setAction:@selector(showCookiesAndSiteData:)];
528   // Create the link button to view site settings. Its position will be set in
529   // performLayout.
530   NSString* siteSettingsButtonText =
531       l10n_util::GetNSString(IDS_PAGE_INFO_SITE_SETTINGS_LINK);
532   siteSettingsButton_ =
533       [self addLinkButtonWithText:siteSettingsButtonText toView:contentView];
534   [siteSettingsButton_ setTarget:self];
535   [siteSettingsButton_ setAction:@selector(showSiteSettingsData:)];
537   return contentView.get();
540 // Handler for the link button below the list of cookies.
541 - (void)showCookiesAndSiteData:(id)sender {
542   DCHECK(webContents_);
543   DCHECK(presenter_);
544   presenter_->RecordWebsiteSettingsAction(
545       WebsiteSettings::WEBSITE_SETTINGS_COOKIES_DIALOG_OPENED);
546   TabDialogs::FromWebContents(webContents_)->ShowCollectedCookies();
549 // Handler for the site settings button below the list of permissions.
550 - (void)showSiteSettingsData:(id)sender {
551   DCHECK(webContents_);
552   DCHECK(presenter_);
553   presenter_->RecordWebsiteSettingsAction(
554       WebsiteSettings::WEBSITE_SETTINGS_SITE_SETTINGS_OPENED);
555   webContents_->OpenURL(content::OpenURLParams(
556       GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
557       NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
560 // Handler for the link button to show certificate information.
561 - (void)showCertificateInfo:(id)sender {
562   DCHECK(certificateId_);
563   presenter_->RecordWebsiteSettingsAction(
564       WebsiteSettings::WEBSITE_SETTINGS_CERTIFICATE_DIALOG_OPENED);
565   ShowCertificateViewerByID(webContents_, [self parentWindow], certificateId_);
568 // Handler for the link button to revoke user certificate decisions.
569 - (void)resetCertificateDecisions:(id)sender {
570   DCHECK(resetDecisionsButton_);
571   presenter_->OnRevokeSSLErrorBypassButtonPressed();
572   [self close];
575 // Handler for the link to show help information about the connection tab.
576 - (void)showHelpPage:(id)sender {
577   presenter_->RecordWebsiteSettingsAction(
578       WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_HELP_OPENED);
579   webContents_->OpenURL(content::OpenURLParams(
580       GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(),
581       NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
584 // Create the contents of the Connection tab and add it to the given tab view.
585 // Returns a weak reference to the tab view item's view.
586 - (NSView*)addConnectionTabToTabView:(NSTabView*)tabView {
587   base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
588   base::scoped_nsobject<NSView> contentView(
589       [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
590   [contentView setAutoresizingMask:NSViewWidthSizable];
592   // Place all the text and images at the same position. The positions will be
593   // adjusted in performLayout.
594   NSPoint textPosition = NSMakePoint(
595       kFramePadding + kConnectionImageSize + kConnectionImageSpacing,
596       kFramePadding);
597   NSPoint imagePosition = NSMakePoint(kFramePadding, kFramePadding);
598   NSSize imageSize = NSMakeSize(kConnectionImageSize, kConnectionImageSize);
600   identityStatusIcon_ = [self addImageWithSize:imageSize
601                                         toView:contentView
602                                        atPoint:imagePosition];
603   identityStatusDescriptionField_ =
604       [self addText:base::string16()
605            withSize:[NSFont smallSystemFontSize]
606                bold:NO
607              toView:contentView.get()
608             atPoint:textPosition];
610   separatorAfterIdentity_ = [self addSeparatorToView:contentView];
611   [separatorAfterIdentity_ setAutoresizingMask:NSViewWidthSizable];
613   connectionStatusIcon_ = [self addImageWithSize:imageSize
614                                           toView:contentView
615                                          atPoint:imagePosition];
616   connectionStatusDescriptionField_ =
617       [self addText:base::string16()
618            withSize:[NSFont smallSystemFontSize]
619                bold:NO
620              toView:contentView.get()
621             atPoint:textPosition];
622   certificateInfoButton_ = nil;  // This will be created only if necessary.
623   resetDecisionsButton_ = nil;   // This will be created only if necessary.
624   separatorAfterConnection_ = [self addSeparatorToView:contentView];
625   [separatorAfterConnection_ setAutoresizingMask:NSViewWidthSizable];
627   firstVisitIcon_ = [self addImageWithSize:imageSize
628                                     toView:contentView
629                                    atPoint:imagePosition];
630   firstVisitHeaderField_ =
631       [self addText:l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_INFO_TITLE)
632            withSize:[NSFont smallSystemFontSize]
633                bold:YES
634              toView:contentView.get()
635             atPoint:textPosition];
636   firstVisitDescriptionField_ =
637       [self addText:base::string16()
638            withSize:[NSFont smallSystemFontSize]
639                bold:NO
640              toView:contentView.get()
641             atPoint:textPosition];
643   separatorAfterFirstVisit_ = [self addSeparatorToView:contentView];
644   [separatorAfterFirstVisit_ setAutoresizingMask:NSViewWidthSizable];
646   NSString* helpButtonText = l10n_util::GetNSString(
647       IDS_PAGE_INFO_HELP_CENTER_LINK);
648   helpButton_ = [self addLinkButtonWithText:helpButtonText
649                                      toView:contentView];
650   [helpButton_ setTarget:self];
651   [helpButton_ setAction:@selector(showHelpPage:)];
653   [item setView:contentView.get()];
654   [tabView_ insertTabViewItem:item.get()
655                       atIndex:WebsiteSettingsUI::TAB_ID_CONNECTION];
657   return contentView.get();
660 // Set the Y position of |view| to the given position, and return the position
661 // of its bottom edge.
662 - (CGFloat)setYPositionOfView:(NSView*)view to:(CGFloat)position {
663   NSRect frame = [view frame];
664   frame.origin.y = position;
665   [view setFrame:frame];
666   return position + NSHeight(frame);
669 - (void)setWidthOfView:(NSView*)view to:(CGFloat)width {
670   [view setFrameSize:NSMakeSize(width, NSHeight([view frame]))];
673 // Layout all of the controls in the window. This should be called whenever
674 // the content has changed.
675 - (void)performLayout {
676   // Make the content at least as wide as the permissions view.
677   CGFloat contentWidth = std::max([self defaultWindowWidth],
678                                   NSWidth([permissionsView_ frame]));
680   // Set the width of the content view now, so that all the text fields will
681   // be sized to fit before their heights and vertical positions are adjusted.
682   // The tab view will only resize the currently selected tab, so resize both
683   // tab content views manually.
684   [self setWidthOfView:contentView_ to:contentWidth];
685   [self setWidthOfView:permissionsTabContentView_ to:contentWidth];
686   [self setWidthOfView:connectionTabContentView_ to:contentWidth];
688   // Place the identity status immediately below the identity.
689   [self sizeTextFieldHeightToFit:identityField_];
690   [self sizeTextFieldHeightToFit:identityStatusField_];
691   CGFloat yPos = NSMaxY([identityField_ frame]) + kConnectionHeadlineSpacing;
692   yPos = [self setYPositionOfView:identityStatusField_ to:yPos];
694   // Lay out the Permissions tab.
696   yPos = [self setYPositionOfView:cookiesView_ to:kFramePadding];
698   // Put the link button for cookies and site data just below the cookie info.
699   [cookiesButton_ setFrameOrigin:NSMakePoint(kFramePadding, yPos)];
701   // Put the permission info just below the link button.
702   yPos =
703       [self setYPositionOfView:permissionsView_
704                             to:NSMaxY([cookiesButton_ frame]) + kFramePadding];
706   // Put the link button for site settings just below the permissions.
707   [siteSettingsButton_ setFrameOrigin:NSMakePoint(kFramePadding, yPos)];
709   // Lay out the Connection tab.
711   // Lay out the identity status section.
712   [self sizeTextFieldHeightToFit:identityStatusDescriptionField_];
713   yPos = std::max(NSMaxY([identityStatusDescriptionField_ frame]),
714                   NSMaxY([identityStatusIcon_ frame]));
715   if (certificateInfoButton_) {
716     NSRect certificateButtonFrame = [certificateInfoButton_ frame];
717     certificateButtonFrame.origin.x = NSMinX(
718         [identityStatusDescriptionField_ frame]);
719     certificateButtonFrame.origin.y = yPos + kVerticalSpacing;
720     [certificateInfoButton_ setFrame:certificateButtonFrame];
721     yPos = NSMaxY(certificateButtonFrame);
722   }
723   if (resetDecisionsButton_) {
724     NSRect resetDecisionsButtonFrame = [resetDecisionsButton_ frame];
725     resetDecisionsButtonFrame.origin.x =
726         NSMinX([identityStatusDescriptionField_ frame]);
727     resetDecisionsButtonFrame.origin.y = yPos + kVerticalSpacing;
728     [resetDecisionsButton_ setFrame:resetDecisionsButtonFrame];
729     yPos = NSMaxY(resetDecisionsButtonFrame);
730   }
731   yPos = [self setYPositionOfView:separatorAfterIdentity_
732                                to:yPos + kVerticalSpacing];
733   yPos += kVerticalSpacing;
735   // Lay out the connection status section.
736   [self sizeTextFieldHeightToFit:connectionStatusDescriptionField_];
737   [self setYPositionOfView:connectionStatusIcon_ to:yPos];
738   [self setYPositionOfView:connectionStatusDescriptionField_ to:yPos];
739   yPos = std::max(NSMaxY([connectionStatusDescriptionField_ frame]),
740                   NSMaxY([connectionStatusIcon_ frame]));
741   yPos = [self setYPositionOfView:separatorAfterConnection_
742                                to:yPos + kVerticalSpacing];
743   yPos += kVerticalSpacing;
745   // Lay out the last visit section.
746   [self setYPositionOfView:firstVisitIcon_ to:yPos];
747   [self sizeTextFieldHeightToFit:firstVisitHeaderField_];
748   yPos = [self setYPositionOfView:firstVisitHeaderField_ to:yPos];
749   yPos += kConnectionHeadlineSpacing;
750   [self sizeTextFieldHeightToFit:firstVisitDescriptionField_];
751   yPos = [self setYPositionOfView:firstVisitDescriptionField_ to:yPos];
752   yPos = [self setYPositionOfView:separatorAfterFirstVisit_
753                                to:yPos + kVerticalSpacing];
754   yPos += kVerticalSpacing;
755   [self setYPositionOfView:helpButton_ to:yPos];
757   // Adjust the tab view size and place it below the identity status.
759   yPos = NSMaxY([identityStatusField_ frame]) + kTabStripTopSpacing;
760   yPos = [self setYPositionOfView:segmentedControl_ to:yPos];
762   CGFloat connectionTabHeight = NSMaxY([helpButton_ frame]) + kVerticalSpacing;
764   NSRect tabViewFrame = [tabView_ frame];
765   tabViewFrame.origin.y = yPos;
766   tabViewFrame.size.height = std::max(
767       connectionTabHeight, NSMaxY([siteSettingsButton_ frame]) + kFramePadding);
768   tabViewFrame.size.width = contentWidth;
769   [tabView_ setFrame:tabViewFrame];
771   // Adjust the contentView to fit everything.
772   [contentView_ setFrame:NSMakeRect(
773       0, 0, NSWidth(tabViewFrame), NSMaxY(tabViewFrame))];
775   [self sizeAndPositionWindow];
778 // Adjust the size of the window to match the size of the content, and position
779 // the bubble anchor appropriately.
780 - (void)sizeAndPositionWindow {
781   NSRect windowFrame = [contentView_ frame];
782   windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
783                                                        toView:nil];
784   // Adjust the origin by the difference in height.
785   windowFrame.origin = [[self window] frame].origin;
786   windowFrame.origin.y -= NSHeight(windowFrame) -
787       NSHeight([[self window] frame]);
789   // Resize the window. Only animate if the window is visible, otherwise it
790   // could be "growing" while it's opening, looking awkward.
791   [[self window] setFrame:windowFrame
792                   display:YES
793                   animate:[[self window] isVisible]];
795   // Adjust the anchor for the bubble.
796   NSPoint anchorPoint =
797       [self anchorPointForWindowWithHeight:NSHeight(windowFrame)
798                               parentWindow:[self parentWindow]];
799   [self setAnchorPoint:anchorPoint];
802 // Takes in the bubble's height and the parent window, which should be a
803 // BrowserWindow, and gets the proper anchor point for the bubble. The returned
804 // point is in screen coordinates.
805 - (NSPoint)anchorPointForWindowWithHeight:(CGFloat)bubbleHeight
806                              parentWindow:(NSWindow*)parent {
807   BrowserWindowController* controller = [parent windowController];
808   NSPoint origin = NSZeroPoint;
809   if ([controller isKindOfClass:[BrowserWindowController class]]) {
810     LocationBarViewMac* locationBar = [controller locationBarBridge];
811     if (locationBar) {
812       NSPoint bubblePoint = locationBar->GetPageInfoBubblePoint();
813       origin = [parent convertBaseToScreen:bubblePoint];
814     }
815   }
816   return origin;
819 // Sets properties on the given |field| to act as the title or description
820 // labels in the bubble.
821 - (void)configureTextFieldAsLabel:(NSTextField*)textField {
822   [textField setEditable:NO];
823   [textField setSelectable:YES];
824   [textField setDrawsBackground:NO];
825   [textField setBezeled:NO];
828 // Adjust the height of the given text field to match its text.
829 - (void)sizeTextFieldHeightToFit:(NSTextField*)textField {
830   NSRect frame = [textField frame];
831   frame.size.height +=
832      [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
833          textField];
834   [textField setFrame:frame];
837 // Create a new text field and add it to the given array of subviews.
838 // The array will retain a reference to the object.
839 - (NSTextField*)addText:(const base::string16&)text
840                withSize:(CGFloat)fontSize
841                    bold:(BOOL)bold
842                  toView:(NSView*)view
843                 atPoint:(NSPoint)point {
844   // Size the text to take up the full available width, with some padding.
845   // The height is arbitrary as it will be adjusted later.
846   CGFloat width = NSWidth([view frame]) - point.x - kFramePadding;
847   NSRect frame = NSMakeRect(point.x, point.y, width, 100);
848   base::scoped_nsobject<NSTextField> textField(
849       [[NSTextField alloc] initWithFrame:frame]);
850   [self configureTextFieldAsLabel:textField.get()];
851   [textField setStringValue:base::SysUTF16ToNSString(text)];
852   NSFont* font = bold ? [NSFont boldSystemFontOfSize:fontSize]
853                       : [NSFont systemFontOfSize:fontSize];
854   [textField setFont:font];
855   [self sizeTextFieldHeightToFit:textField];
856   [textField setAutoresizingMask:NSViewWidthSizable];
857   [view addSubview:textField.get()];
858   return textField.get();
861 // Add an image as a subview of the given view, placed at a pre-determined x
862 // position and the given y position. Return the new NSImageView.
863 - (NSImageView*)addImageWithSize:(NSSize)size
864                           toView:(NSView*)view
865                          atPoint:(NSPoint)point {
866   NSRect frame = NSMakeRect(point.x, point.y, size.width, size.height);
867   base::scoped_nsobject<NSImageView> imageView(
868       [[NSImageView alloc] initWithFrame:frame]);
869   [imageView setImageFrameStyle:NSImageFrameNone];
870   [view addSubview:imageView.get()];
871   return imageView.get();
874 // Add a separator as a subview of the given view. Return the new view.
875 - (NSView*)addSeparatorToView:(NSView*)view {
876   // Take up almost the full width of the container's frame.
877   CGFloat width = NSWidth([view frame]) - 2 * kFramePadding;
879   // Use an arbitrary position; it will be adjusted in performLayout.
880   NSBox* spacer = [self horizontalSeparatorWithFrame:NSMakeRect(
881       kFramePadding, 0, width, 0)];
882   [view addSubview:spacer];
883   return spacer;
886 // Add a link button with the given text to |view|.
887 - (NSButton*)addLinkButtonWithText:(NSString*)text toView:(NSView*)view {
888   // Frame size is arbitrary; it will be adjusted by the layout tweaker.
889   NSRect frame = NSMakeRect(kFramePadding, 0, 100, 10);
890   base::scoped_nsobject<NSButton> button(
891       [[NSButton alloc] initWithFrame:frame]);
892   base::scoped_nsobject<HyperlinkButtonCell> cell(
893       [[HyperlinkButtonCell alloc] initTextCell:text]);
894   [cell setControlSize:NSSmallControlSize];
895   [button setCell:cell.get()];
896   [button setButtonType:NSMomentaryPushInButton];
897   [button setBezelStyle:NSRegularSquareBezelStyle];
898   [view addSubview:button.get()];
900   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
901   return button.get();
904 // Add a button with the given text to |view| setting the max size appropriately
905 // for the connection info section.
906 - (NSButton*)addButtonWithTextToConnectionSection:(NSString*)text
907                                            toView:(NSView*)view {
908   NSRect containerFrame = [view frame];
909   // Frame size is arbitrary; it will be adjusted by the layout tweaker.
910   NSRect frame = NSMakeRect(kFramePadding, 0, 100, 10);
911   base::scoped_nsobject<NSButton> button(
912       [[NSButton alloc] initWithFrame:frame]);
914   // Determine the largest possible size for this button. The size is the width
915   // of the connection section minus the padding on both sides minus the
916   // connection image size and spacing.
917   CGFloat maxTitleWidth = containerFrame.size.width - kFramePadding * 2 -
918                           kConnectionImageSize - kConnectionImageSpacing;
920   base::scoped_nsobject<NSButtonCell> cell(
921       [[NSButtonCell alloc] initTextCell:text]);
922   [button setCell:cell.get()];
923   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
925   // Ensure the containing view is large enough to contain the button with its
926   // widest possible title.
927   NSRect buttonFrame = [button frame];
928   buttonFrame.size.width = maxTitleWidth;
930   [button setFrame:buttonFrame];
931   [button setButtonType:NSMomentaryPushInButton];
932   [button setBezelStyle:NSRegularSquareBezelStyle];
933   [view addSubview:button.get()];
935   return button.get();
938 // Add a pop-up button for |permissionInfo| to the given view.
939 - (NSPopUpButton*)addPopUpButtonForPermission:
940     (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
941                                        toView:(NSView*)view
942                                       atPoint:(NSPoint)point {
944   GURL url = webContents_ ? webContents_->GetURL() : GURL();
945   __block WebsiteSettingsBubbleController* weakSelf = self;
946   PermissionMenuModel::ChangeCallback callback =
947       base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) {
948           [weakSelf onPermissionChanged:permission.type to:permission.setting];
949       });
950   base::scoped_nsobject<PermissionSelectorButton> button(
951       [[PermissionSelectorButton alloc] initWithPermissionInfo:permissionInfo
952                                                         forURL:url
953                                                   withCallback:callback]);
954   // Determine the largest possible size for this button.
955   CGFloat maxTitleWidth = [button
956       maxTitleWidthForContentSettingsType:permissionInfo.type
957                        withDefaultSetting:permissionInfo.default_setting];
959   // Ensure the containing view is large enough to contain the button with its
960   // widest possible title.
961   NSRect containerFrame = [view frame];
962   containerFrame.size.width = std::max(
963       NSWidth(containerFrame), point.x + maxTitleWidth + kFramePadding);
964   [view setFrame:containerFrame];
965   [view addSubview:button.get()];
966   return button.get();
969 // Called when the user changes the setting of a permission.
970 - (void)onPermissionChanged:(ContentSettingsType)permissionType
971                          to:(ContentSetting)newSetting {
972   if (presenter_)
973     presenter_->OnSitePermissionChanged(permissionType, newSetting);
976 // Called when the user changes the selected segment in the segmented control.
977 - (void)tabSelected:(id)sender {
978   NSInteger index = [segmentedControl_ selectedSegment];
979   switch (index) {
980     case WebsiteSettingsUI::TAB_ID_PERMISSIONS:
981       presenter_->RecordWebsiteSettingsAction(
982           WebsiteSettings::WEBSITE_SETTINGS_PERMISSIONS_TAB_SELECTED);
983       break;
984     case WebsiteSettingsUI::TAB_ID_CONNECTION:
985       presenter_->RecordWebsiteSettingsAction(
986           WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_TAB_SELECTED);
987       break;
988     default:
989       NOTREACHED();
990   }
991   [tabView_ selectTabViewItemAtIndex:index];
994 // Adds a new row to the UI listing the permissions. Returns the amount of
995 // vertical space that was taken up by the row.
996 - (NSPoint)addPermission:
997     (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
998                   toView:(NSView*)view
999                  atPoint:(NSPoint)point {
1000   base::scoped_nsobject<NSImage> image(
1001       [WebsiteSettingsUI::GetPermissionIcon(permissionInfo).ToNSImage()
1002           retain]);
1003   NSImageView* imageView = [self addImageWithSize:[image size]
1004                                            toView:view
1005                                           atPoint:point];
1006   [imageView setImage:image];
1007   point.x += kPermissionImageSize + kPermissionImageSpacing;
1009   base::string16 labelText =
1010       WebsiteSettingsUI::PermissionTypeToUIString(permissionInfo.type) +
1011       base::ASCIIToUTF16(":");
1013   NSTextField* label = [self addText:labelText
1014                             withSize:[NSFont smallSystemFontSize]
1015                                 bold:NO
1016                               toView:view
1017                              atPoint:point];
1018   [label sizeToFit];
1019   NSPoint popUpPosition = NSMakePoint(NSMaxX([label frame]), point.y);
1020   NSPopUpButton* button = [self addPopUpButtonForPermission:permissionInfo
1021                                                      toView:view
1022                                                     atPoint:popUpPosition];
1024   // Adjust the vertical position of the button so that its title text is
1025   // aligned with the label. Assumes that the text is the same size in both.
1026   // Also adjust the horizontal position to remove excess space due to the
1027   // invisible bezel.
1028   NSRect titleRect = [[button cell] titleRectForBounds:[button bounds]];
1029   popUpPosition.x -= titleRect.origin.x - kPermissionPopUpXSpacing;
1030   popUpPosition.y -= titleRect.origin.y;
1031   [button setFrameOrigin:popUpPosition];
1033   // Align the icon with the text.
1034   [self alignPermissionIcon:imageView withTextField:label];
1036   // Permissions specified by policy or an extension cannot be changed.
1037   if (permissionInfo.source == content_settings::SETTING_SOURCE_EXTENSION ||
1038       permissionInfo.source == content_settings::SETTING_SOURCE_POLICY) {
1039     [button setEnabled:NO];
1040   }
1042   NSRect buttonFrame = [button frame];
1043   return NSMakePoint(NSMaxX(buttonFrame), NSMaxY(buttonFrame));
1046 // Align an image with a text field by vertically centering the image on
1047 // the cap height of the first line of text.
1048 - (void)alignPermissionIcon:(NSImageView*)imageView
1049               withTextField:(NSTextField*)textField {
1050   NSFont* font = [textField font];
1052   // Calculate the offset from the top of the text field.
1053   CGFloat capHeight = [font capHeight];
1054   CGFloat offset = (kPermissionImageSize - capHeight) / 2 -
1055       ([font ascender] - capHeight) - kPermissionImageYAdjust;
1057   NSRect frame = [imageView frame];
1058   frame.origin.y -= offset;
1059   [imageView setFrame:frame];
1062 - (CGFloat)addCookieInfo:
1063     (const WebsiteSettingsUI::CookieInfo&)cookieInfo
1064                   toView:(NSView*)view
1065                  atPoint:(NSPoint)point {
1066   WebsiteSettingsUI::PermissionInfo info;
1067   info.type = CONTENT_SETTINGS_TYPE_COOKIES;
1068   info.setting = CONTENT_SETTING_ALLOW;
1069   NSImage* image = WebsiteSettingsUI::GetPermissionIcon(info).ToNSImage();
1070   NSImageView* imageView = [self addImageWithSize:[image size]
1071                                            toView:view
1072                                           atPoint:point];
1073   [imageView setImage:image];
1074   point.x += kPermissionImageSize + kPermissionImageSpacing;
1076   base::string16 labelText = l10n_util::GetStringFUTF16(
1077       IDS_WEBSITE_SETTINGS_SITE_DATA_STATS_LINE,
1078       base::UTF8ToUTF16(cookieInfo.cookie_source),
1079       base::IntToString16(cookieInfo.allowed),
1080       base::IntToString16(cookieInfo.blocked));
1082   NSTextField* label = [self addText:labelText
1083                             withSize:[NSFont smallSystemFontSize]
1084                                 bold:NO
1085                               toView:view
1086                              atPoint:point];
1088   // Align the icon with the text.
1089   [self alignPermissionIcon:imageView withTextField:label];
1091   return NSHeight([label frame]);
1094 // Set the content of the identity and identity status fields.
1095 - (void)setIdentityInfo:(const WebsiteSettingsUI::IdentityInfo&)identityInfo {
1096   [identityField_ setStringValue:
1097       base::SysUTF8ToNSString(identityInfo.site_identity)];
1098   [identityStatusField_ setStringValue:
1099       base::SysUTF16ToNSString(identityInfo.GetIdentityStatusText())];
1101   WebsiteSettings::SiteIdentityStatus status = identityInfo.identity_status;
1102   if (status == WebsiteSettings::SITE_IDENTITY_STATUS_CERT ||
1103       status == WebsiteSettings::SITE_IDENTITY_STATUS_EV_CERT) {
1104     [identityStatusField_ setTextColor:IdentityVerifiedTextColor()];
1105   }
1106   // If there is a certificate, add a button for viewing the certificate info.
1107   certificateId_ = identityInfo.cert_id;
1108   if (certificateId_) {
1109     if (!certificateInfoButton_) {
1110       NSString* text = l10n_util::GetNSString(IDS_PAGEINFO_CERT_INFO_BUTTON);
1111       certificateInfoButton_ = [self addLinkButtonWithText:text
1112           toView:connectionTabContentView_];
1114       [certificateInfoButton_ setTarget:self];
1115       [certificateInfoButton_ setAction:@selector(showCertificateInfo:)];
1116     }
1118     // Check if a security decision has been made, and if so, add a button to
1119     // allow the user to retract their decision.
1120     if (identityInfo.show_ssl_decision_revoke_button) {
1121       NSString* text = l10n_util::GetNSString(
1122           IDS_PAGEINFO_RESET_INVALID_CERTIFICATE_DECISIONS_BUTTON);
1123       resetDecisionsButton_ =
1124           [self addButtonWithTextToConnectionSection:text
1125                                               toView:connectionTabContentView_];
1126       [resetDecisionsButton_ setTarget:self];
1127       [resetDecisionsButton_ setAction:@selector(resetCertificateDecisions:)];
1128     }
1129   } else {
1130     certificateInfoButton_ = nil;
1131   }
1133   [identityStatusIcon_ setImage:WebsiteSettingsUI::GetIdentityIcon(
1134       identityInfo.identity_status).ToNSImage()];
1135   [identityStatusDescriptionField_ setStringValue:
1136       base::SysUTF8ToNSString(identityInfo.identity_status_description)];
1138   [connectionStatusIcon_ setImage:WebsiteSettingsUI::GetConnectionIcon(
1139       identityInfo.connection_status).ToNSImage()];
1140   [connectionStatusDescriptionField_ setStringValue:
1141       base::SysUTF8ToNSString(identityInfo.connection_status_description)];
1143   [self performLayout];
1146 - (void)setCookieInfo:(const CookieInfoList&)cookieInfoList {
1147   // A result of re-ordering of the permissions (crbug.com/444244) is
1148   // that sometimes permissions may not be displayed at all, so it's
1149   // incorrect to check they are set before the cookie info.
1151   [cookiesView_ setSubviews:[NSArray array]];
1152   NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1154   base::string16 sectionTitle = l10n_util::GetStringUTF16(
1155       IDS_WEBSITE_SETTINGS_TITLE_SITE_DATA);
1156   NSTextField* header = [self addText:sectionTitle
1157                              withSize:[NSFont smallSystemFontSize]
1158                                  bold:YES
1159                                toView:cookiesView_
1160                               atPoint:controlOrigin];
1161   controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1163   for (CookieInfoList::const_iterator it = cookieInfoList.begin();
1164        it != cookieInfoList.end();
1165        ++it) {
1166     controlOrigin.y += kPermissionsTabSpacing;
1167     CGFloat rowHeight = [self addCookieInfo:*it
1168                                      toView:cookiesView_
1169                                     atPoint:controlOrigin];
1170     controlOrigin.y += rowHeight;
1171   }
1173   controlOrigin.y += kPermissionsTabSpacing;
1174   [cookiesView_ setFrameSize:
1175       NSMakeSize(NSWidth([cookiesView_ frame]), controlOrigin.y)];
1177   [self performLayout];
1180 - (void)setPermissionInfo:(const PermissionInfoList&)permissionInfoList {
1181   [permissionsView_ setSubviews:[NSArray array]];
1182   NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1184   if (permissionInfoList.size() > 0) {
1185     base::string16 sectionTitle = l10n_util::GetStringUTF16(
1186         IDS_WEBSITE_SETTINGS_TITLE_SITE_PERMISSIONS);
1187     NSTextField* header = [self addText:sectionTitle
1188                                withSize:[NSFont smallSystemFontSize]
1189                                    bold:YES
1190                                  toView:permissionsView_
1191                                 atPoint:controlOrigin];
1192     [self sizeTextFieldHeightToFit:header];
1193     controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1195     for (PermissionInfoList::const_iterator permission =
1196              permissionInfoList.begin();
1197          permission != permissionInfoList.end();
1198          ++permission) {
1199       controlOrigin.y += kPermissionsTabSpacing;
1200       NSPoint rowBottomRight = [self addPermission:*permission
1201                                             toView:permissionsView_
1202                                            atPoint:controlOrigin];
1203       controlOrigin.y = rowBottomRight.y;
1204     }
1205     controlOrigin.y += kFramePadding;
1206   }
1208   [permissionsView_ setFrameSize:
1209       NSMakeSize(NSWidth([permissionsView_ frame]), controlOrigin.y)];
1210   [self performLayout];
1213 - (void)setFirstVisit:(const base::string16&)firstVisit {
1214   [firstVisitIcon_ setImage:
1215       WebsiteSettingsUI::GetFirstVisitIcon(firstVisit).ToNSImage()];
1216   [firstVisitDescriptionField_ setStringValue:
1217       base::SysUTF16ToNSString(firstVisit)];
1218   [self performLayout];
1221 - (void)setSelectedTab:(WebsiteSettingsUI::TabId)tabId {
1222   NSInteger index = static_cast<NSInteger>(tabId);
1223   [segmentedControl_ setSelectedSegment:index];
1224   [tabView_ selectTabViewItemAtIndex:index];
1227 @end
1229 WebsiteSettingsUIBridge::WebsiteSettingsUIBridge()
1230   : bubble_controller_(nil) {
1233 WebsiteSettingsUIBridge::~WebsiteSettingsUIBridge() {
1236 void WebsiteSettingsUIBridge::set_bubble_controller(
1237     WebsiteSettingsBubbleController* controller) {
1238   bubble_controller_ = controller;
1241 void WebsiteSettingsUIBridge::Show(gfx::NativeWindow parent,
1242                                    Profile* profile,
1243                                    content::WebContents* web_contents,
1244                                    const GURL& url,
1245                                    const content::SSLStatus& ssl) {
1246   bool is_internal_page = InternalChromePage(url);
1248   // Create the bridge. This will be owned by the bubble controller.
1249   WebsiteSettingsUIBridge* bridge = new WebsiteSettingsUIBridge();
1251   // Create the bubble controller. It will dealloc itself when it closes.
1252   WebsiteSettingsBubbleController* bubble_controller =
1253       [[WebsiteSettingsBubbleController alloc]
1254           initWithParentWindow:parent
1255        websiteSettingsUIBridge:bridge
1256                    webContents:web_contents
1257                 isInternalPage:is_internal_page];
1259   if (!is_internal_page) {
1260     // Initialize the presenter, which holds the model and controls the UI.
1261     // This is also owned by the bubble controller.
1262     WebsiteSettings* presenter = new WebsiteSettings(
1263         bridge,
1264         profile,
1265         TabSpecificContentSettings::FromWebContents(web_contents),
1266         InfoBarService::FromWebContents(web_contents),
1267         url,
1268         ssl,
1269         content::CertStore::GetInstance());
1270     [bubble_controller setPresenter:presenter];
1271   }
1273   [bubble_controller showWindow:nil];
1276 void WebsiteSettingsUIBridge::SetIdentityInfo(
1277     const WebsiteSettingsUI::IdentityInfo& identity_info) {
1278   [bubble_controller_ setIdentityInfo:identity_info];
1281 void WebsiteSettingsUIBridge::SetCookieInfo(
1282     const CookieInfoList& cookie_info_list) {
1283   [bubble_controller_ setCookieInfo:cookie_info_list];
1286 void WebsiteSettingsUIBridge::SetPermissionInfo(
1287     const PermissionInfoList& permission_info_list) {
1288   [bubble_controller_ setPermissionInfo:permission_info_list];
1291 void WebsiteSettingsUIBridge::SetFirstVisit(const base::string16& first_visit) {
1292   [bubble_controller_ setFirstVisit:first_visit];
1295 void WebsiteSettingsUIBridge::SetSelectedTab(TabId tab_id) {
1296   [bubble_controller_ setSelectedTab:tab_id];