Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / website_settings / website_settings_bubble_controller.mm
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/website_settings/website_settings_bubble_controller.h"
6
7 #include <cmath>
8
9 #import <AppKit/AppKit.h>
10
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/browser_dialogs.h"
17 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
18 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
19 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
20 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
21 #import "chrome/browser/ui/cocoa/website_settings/permission_selector_button.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 "content/public/browser/cert_store.h"
26 #include "content/public/browser/page_navigator.h"
27 #include "content/public/browser/user_metrics.h"
28 #include "content/public/browser/web_contents.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "grit/theme_resources.h"
32 #include "grit/ui_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"
39
40 namespace {
41
42 // The default width of the window, in view coordinates. It may be larger to
43 // fit the content.
44 const CGFloat kDefaultWindowWidth = 310;
45
46 // Spacing in between sections.
47 const CGFloat kVerticalSpacing = 10;
48
49 // Padding between the window frame and content.
50 const CGFloat kFramePadding = 20;
51
52 // Padding between the window frame and content for the internal page bubble.
53 const CGFloat kInternalPageFramePadding = 10;
54
55 // Spacing between the headlines and description text on the Connection tab.
56 const CGFloat kConnectionHeadlineSpacing = 2;
57
58 // Spacing between images on the Connection tab and the text.
59 const CGFloat kConnectionImageSpacing = 10;
60
61 // Spacing between the image and text for internal pages.
62 const CGFloat kInternalPageImageSpacing = 10;
63
64 // Square size of the images on the Connections tab.
65 const CGFloat kConnectionImageSize = 30;
66
67 // Square size of the image that is shown for internal pages.
68 const CGFloat kInternalPageImageSize = 26;
69
70 // Square size of the permission images.
71 const CGFloat kPermissionImageSize = 19;
72
73 // Vertical adjustment for the permission images.
74 // They have an extra pixel of padding on the bottom edge.
75 const CGFloat kPermissionImageYAdjust = 1;
76
77 // Spacing between a permission image and the text.
78 const CGFloat kPermissionImageSpacing = 3;
79
80 // The spacing between individual items in the Permissions tab.
81 const CGFloat kPermissionsTabSpacing = 12;
82
83 // Extra spacing after a headline on the Permissions tab.
84 const CGFloat kPermissionsHeadlineSpacing = 2;
85
86 // The amount of horizontal space between a permission label and the popup.
87 const CGFloat kPermissionPopUpXSpacing = 3;
88
89 // The extra space to the left of the first tab in the tab strip.
90 const CGFloat kTabStripXPadding = kFramePadding;
91
92 // The amount of space between the visual borders of adjacent tabs.
93 const CGFloat kTabSpacing = 4;
94
95 // The amount of space above the tab strip.
96 const CGFloat kTabStripTopSpacing = 14;
97
98 // The height of the clickable area of the tab.
99 const CGFloat kTabHeight = 28;
100
101 // The amount of space above tab labels.
102 const CGFloat kTabLabelTopPadding = 6;
103
104 // The amount of padding to leave on either side of the tab label.
105 const CGFloat kTabLabelXPadding = 12;
106
107 // Return the text color to use for the indentity 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];
116 }
117
118 }  // namespace
119
120 @interface WebsiteSettingsTabSegmentedCell : NSSegmentedCell {
121  @private
122   base::scoped_nsobject<NSImage> tabstripCenterImage_;
123   base::scoped_nsobject<NSImage> tabstripLeftImage_;
124   base::scoped_nsobject<NSImage> tabstripRightImage_;
125
126   base::scoped_nsobject<NSImage> tabCenterImage_;
127   base::scoped_nsobject<NSImage> tabLeftImage_;
128   base::scoped_nsobject<NSImage> tabRightImage_;
129
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_;
133 }
134 @end
135
136 @implementation WebsiteSettingsTabSegmentedCell
137 - (id)init {
138   if ((self = [super init])) {
139     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
140     tabstripCenterImage_.reset(rb.GetNativeImageNamed(
141         IDR_WEBSITE_SETTINGS_TABSTRIP_CENTER).CopyNSImage());
142     tabstripLeftImage_.reset(rb.GetNativeImageNamed(
143         IDR_WEBSITE_SETTINGS_TABSTRIP_LEFT).CopyNSImage());
144     tabstripRightImage_.reset(rb.GetNativeImageNamed(
145         IDR_WEBSITE_SETTINGS_TABSTRIP_RIGHT).CopyNSImage());
146
147     tabCenterImage_.reset(
148         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_CENTER2).CopyNSImage());
149     tabLeftImage_.reset(
150         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_LEFT2).CopyNSImage());
151     tabRightImage_.reset(
152         rb.GetNativeImageNamed(IDR_WEBSITE_SETTINGS_TAB_RIGHT2).CopyNSImage());
153   }
154   return self;
155 }
156
157 // Called when keyboard focus in the segmented control is moved forward.
158 - (void)makeNextSegmentKey {
159   [super makeNextSegmentKey];
160   keySegment_ = (keySegment_ + 1) % [self segmentCount];
161 }
162
163 // Called when keyboard focus in the segmented control is moved backwards.
164 - (void)makePreviousSegmentKey {
165   [super makePreviousSegmentKey];
166   if (--keySegment_ < 0)
167     keySegment_ += [self segmentCount];
168 }
169
170 - (void)setSelectedSegment:(NSInteger)selectedSegment {
171   keySegment_ = selectedSegment;
172   [super setSelectedSegment:selectedSegment];
173 }
174
175 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
176   CGFloat tabstripHeight = [tabCenterImage_ size].height;
177
178   // Draw the tab for the selected segment.
179   NSRect tabRect = [self hitRectForSegment:[self selectedSegment]];
180   tabRect.origin.y = 0;
181   tabRect.size.height = tabstripHeight;
182
183   NSDrawThreePartImage(tabRect,
184                        tabLeftImage_,
185                        tabCenterImage_,
186                        tabRightImage_,
187                        /*vertical=*/ NO,
188                        NSCompositeSourceOver,
189                        1,
190                        /*flipped=*/ YES);
191
192   // Draw the background to the left of the selected tab.
193   NSRect backgroundRect = NSMakeRect(0, 0, NSMinX(tabRect), tabstripHeight);
194   NSDrawThreePartImage(backgroundRect,
195                        nil,
196                        tabstripCenterImage_,
197                        tabstripLeftImage_,
198                        /*vertical=*/ NO,
199                        NSCompositeSourceOver,
200                        1,
201                        /*flipped=*/ YES);
202
203   // Draw the background to the right of the selected tab.
204   backgroundRect.origin.x = NSMaxX(tabRect);
205   backgroundRect.size.width = NSMaxX(cellFrame) - NSMaxX(tabRect);
206   NSDrawThreePartImage(backgroundRect,
207                        tabstripRightImage_,
208                        tabstripCenterImage_,
209                        nil,
210                        /*vertical=*/ NO,
211                        NSCompositeSourceOver,
212                        1,
213                        /*flipped=*/ YES);
214
215   // Call the superclass method to trigger drawing of the tab labels.
216   [self drawInteriorWithFrame:cellFrame inView:controlView];
217   if ([[controlView window] firstResponder] == controlView)
218     [self drawFocusRect];
219 }
220
221 - (void)drawFocusRect {
222   gfx::ScopedNSGraphicsContextSaveGState scoped_state;
223   NSSetFocusRingStyle(NSFocusRingOnly);
224   [[NSColor keyboardFocusIndicatorColor] set];
225   NSFrameRect([self hitRectForSegment:keySegment_]);
226 }
227
228 // Return the hit rect (i.e., the visual bounds of the tab) for
229 // the given segment.
230 - (NSRect)hitRectForSegment:(NSInteger)segment {
231   CGFloat tabstripHeight = [tabCenterImage_ size].height;
232   DCHECK_GT(tabstripHeight, kTabHeight);
233
234   NSRect rect = NSMakeRect(0, tabstripHeight - kTabHeight,
235                            [self widthForSegment:segment], kTabHeight);
236   for (NSInteger i = 0; i < segment; ++i) {
237     rect.origin.x += [self widthForSegment:i];
238   }
239   int xAdjust = segment == 0 ? kTabStripXPadding : 0;
240   rect.size.width = std::floor(
241       [self widthForSegment:segment] - kTabSpacing - xAdjust);
242   rect.origin.x = std::floor(rect.origin.x + kTabSpacing / 2 + xAdjust);
243
244   return rect;
245 }
246
247 - (void)drawSegment:(NSInteger)segment
248             inFrame:(NSRect)frame
249            withView:(NSView*)controlView {
250   // Call the superclass to draw the label, adjusting the rectangle so that
251   // the label appears centered in the tab.
252   if (segment == 0) {
253     frame.origin.x += kTabStripXPadding / 2;
254     frame.size.width -= kTabStripXPadding;
255   }
256   frame.origin.y += kTabLabelTopPadding;
257   frame.size.height -= kTabLabelTopPadding;
258   [super drawSegment:segment inFrame:frame withView:controlView];
259 }
260
261 // Overrides the default tracking behavior to only respond to clicks inside the
262 // visual borders of the tab.
263 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
264   NSInteger segmentCount = [self segmentCount];
265   for (NSInteger i = 0; i < segmentCount; ++i) {
266     if (NSPointInRect(startPoint, [self hitRectForSegment:i]))
267       return YES;
268   }
269   return NO;
270 }
271
272 // Overrides the default cell height to take up the full height of the
273 // segmented control. Otherwise, clicks on the lower part of a tab will be
274 // ignored.
275 - (NSSize)cellSizeForBounds:(NSRect)aRect {
276   return NSMakeSize([super cellSizeForBounds:aRect].width,
277                     [tabstripCenterImage_ size].height);
278 }
279
280 // Returns the minimum size required to display this cell.
281 // It should always be exactly as tall as the tabstrip background image.
282 - (NSSize)cellSize {
283   return NSMakeSize([super cellSize].width,
284                     [tabstripCenterImage_ size].height);
285 }
286 @end
287
288 @implementation WebsiteSettingsBubbleController
289
290 - (CGFloat)defaultWindowWidth {
291   return kDefaultWindowWidth;
292 }
293
294 - (id)initWithParentWindow:(NSWindow*)parentWindow
295    websiteSettingsUIBridge:(WebsiteSettingsUIBridge*)bridge
296                webContents:(content::WebContents*)webContents
297             isInternalPage:(BOOL)isInternalPage {
298   DCHECK(parentWindow);
299
300   webContents_ = webContents;
301
302   // Use an arbitrary height; it will be changed in performLayout.
303   NSRect contentRect = NSMakeRect(0, 0, [self defaultWindowWidth], 1);
304   // Create an empty window into which content is placed.
305   base::scoped_nsobject<InfoBubbleWindow> window(
306       [[InfoBubbleWindow alloc] initWithContentRect:contentRect
307                                           styleMask:NSBorderlessWindowMask
308                                             backing:NSBackingStoreBuffered
309                                               defer:NO]);
310
311   if ((self = [super initWithWindow:window.get()
312                        parentWindow:parentWindow
313                          anchoredAt:NSZeroPoint])) {
314     [[self bubble] setArrowLocation:info_bubble::kTopLeft];
315
316     // Create the container view that uses flipped coordinates.
317     NSRect contentFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
318     contentView_.reset(
319         [[FlippedView alloc] initWithFrame:contentFrame]);
320
321     // Replace the window's content.
322     [[[self window] contentView] setSubviews:
323         [NSArray arrayWithObject:contentView_.get()]];
324
325     if (isInternalPage)
326       [self initializeContentsForInternalPage];
327     else
328       [self initializeContents];
329
330     bridge_.reset(bridge);
331     bridge_->set_bubble_controller(self);
332   }
333   return self;
334 }
335
336 - (void)windowWillClose:(NSNotification*)notification {
337   if (presenter_.get())
338     presenter_->OnUIClosing();
339   [super windowWillClose:notification];
340 }
341
342 - (void)setPresenter:(WebsiteSettings*)presenter {
343   presenter_.reset(presenter);
344 }
345
346 // Create the subviews for the bubble for internal Chrome pages.
347 - (void)initializeContentsForInternalPage {
348   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
349
350   NSPoint controlOrigin = NSMakePoint(
351       kInternalPageFramePadding,
352       kInternalPageFramePadding + info_bubble::kBubbleArrowHeight);
353   NSSize imageSize = NSMakeSize(kInternalPageImageSize,
354                                 kInternalPageImageSize);
355   NSImageView* imageView = [self addImageWithSize:imageSize
356                                            toView:contentView_
357                                           atPoint:controlOrigin];
358   [imageView setImage:rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_26).ToNSImage()];
359
360   controlOrigin.x += NSWidth([imageView frame]) + kInternalPageImageSpacing;
361   base::string16 text = l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE);
362   NSTextField* textField = [self addText:text
363                                 withSize:[NSFont smallSystemFontSize]
364                                     bold:NO
365                                   toView:contentView_
366                                  atPoint:controlOrigin];
367   // Center the text vertically with the image.
368   NSRect textFrame = [textField frame];
369   textFrame.origin.y += (imageSize.height - NSHeight(textFrame)) / 2;
370   [textField setFrame:textFrame];
371
372   // Adjust the contentView to fit everything.
373   CGFloat maxY = std::max(NSMaxY([imageView frame]), NSMaxY(textFrame));
374   [contentView_ setFrame:NSMakeRect(
375       0, 0, [self defaultWindowWidth], maxY + kInternalPageFramePadding)];
376
377   [self sizeAndPositionWindow];
378 }
379
380 // Create the subviews for the website settings bubble.
381 - (void)initializeContents {
382   // Keeps track of the position that the next control should be drawn at.
383   NSPoint controlOrigin = NSMakePoint(
384       kFramePadding,
385       kFramePadding + info_bubble::kBubbleArrowHeight);
386
387   // Create a text field (empty for now) to show the site identity.
388   identityField_ = [self addText:base::string16()
389                         withSize:[NSFont systemFontSize]
390                             bold:YES
391                           toView:contentView_
392                          atPoint:controlOrigin];
393   controlOrigin.y +=
394       NSHeight([identityField_ frame]) + kConnectionHeadlineSpacing;
395
396   // Create a text field to identity status (e.g. verified, not verified).
397   identityStatusField_ = [self addText:base::string16()
398                               withSize:[NSFont smallSystemFontSize]
399                                   bold:NO
400                                 toView:contentView_
401                                atPoint:controlOrigin];
402
403   // Create the tab view and its two tabs.
404
405   base::scoped_nsobject<WebsiteSettingsTabSegmentedCell> cell(
406       [[WebsiteSettingsTabSegmentedCell alloc] init]);
407   CGFloat tabstripHeight = [cell cellSize].height;
408   NSRect tabstripFrame = NSMakeRect(
409       0, 0, [self defaultWindowWidth], tabstripHeight);
410   segmentedControl_.reset(
411       [[NSSegmentedControl alloc] initWithFrame:tabstripFrame]);
412   [segmentedControl_ setCell:cell];
413   [segmentedControl_ setSegmentCount:WebsiteSettingsUI::NUM_TAB_IDS];
414   [segmentedControl_ setTarget:self];
415   [segmentedControl_ setAction:@selector(tabSelected:)];
416   [segmentedControl_ setAutoresizingMask:NSViewWidthSizable];
417
418   NSFont* smallSystemFont =
419       [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
420   NSDictionary* textAttributes =
421       [NSDictionary dictionaryWithObject:smallSystemFont
422                                   forKey:NSFontAttributeName];
423
424   // Create the "Permissions" tab.
425   NSString* label = l10n_util::GetNSString(
426       IDS_WEBSITE_SETTINGS_TAB_LABEL_PERMISSIONS);
427   [segmentedControl_ setLabel:label
428                    forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
429   NSSize textSize = [label sizeWithAttributes:textAttributes];
430   CGFloat tabWidth = textSize.width + 2 * kTabLabelXPadding;
431   [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
432                    forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
433
434   // Create the "Connection" tab.
435   label = l10n_util::GetNSString(IDS_WEBSITE_SETTINGS_TAB_LABEL_CONNECTION);
436   textSize = [label sizeWithAttributes:textAttributes];
437   [segmentedControl_ setLabel:label
438                    forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
439
440   DCHECK_EQ([segmentedControl_ segmentCount], WebsiteSettingsUI::NUM_TAB_IDS);
441
442   // Make both tabs the width of the widest. The first segment has some
443   // additional padding that is not part of the tab, which is used for drawing
444   // the background of the tab strip.
445   tabWidth = std::max(tabWidth,
446                       textSize.width + 2 * kTabLabelXPadding);
447   [segmentedControl_ setWidth:tabWidth + kTabStripXPadding
448                    forSegment:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
449   [segmentedControl_ setWidth:tabWidth
450                    forSegment:WebsiteSettingsUI::TAB_ID_CONNECTION];
451
452   [segmentedControl_ setFont:smallSystemFont];
453   [contentView_ addSubview:segmentedControl_];
454
455   NSRect tabFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300);
456   tabView_.reset([[NSTabView alloc] initWithFrame:tabFrame]);
457   [tabView_ setTabViewType:NSNoTabsNoBorder];
458   [tabView_ setDrawsBackground:NO];
459   [tabView_ setControlSize:NSSmallControlSize];
460   [contentView_ addSubview:tabView_.get()];
461
462   permissionsTabContentView_ = [self addPermissionsTabToTabView:tabView_];
463   connectionTabContentView_ = [self addConnectionTabToTabView:tabView_];
464   [self setSelectedTab:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
465
466   [self performLayout];
467 }
468
469 // Create the contents of the Permissions tab and add it to the given tab view.
470 // Returns a weak reference to the tab view item's view.
471 - (NSView*)addPermissionsTabToTabView:(NSTabView*)tabView {
472   base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
473   [tabView_ insertTabViewItem:item.get()
474                       atIndex:WebsiteSettingsUI::TAB_ID_PERMISSIONS];
475   base::scoped_nsobject<NSView> contentView(
476       [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
477   [contentView setAutoresizingMask:NSViewWidthSizable];
478   [item setView:contentView.get()];
479
480   // Initialize the two containers that hold the controls. The initial frames
481   // are arbitrary, and will be adjusted after the controls are laid out.
482   cookiesView_ = [[[FlippedView alloc]
483       initWithFrame:[tabView_ contentRect]] autorelease];
484   [cookiesView_ setAutoresizingMask:NSViewWidthSizable];
485
486   permissionsView_ = [[[FlippedView alloc]
487       initWithFrame:[tabView_ contentRect]] autorelease];
488
489   [contentView addSubview:cookiesView_];
490   [contentView addSubview:permissionsView_];
491
492   // Create the link button to view cookies and site data.
493   // Its position will be set in performLayout.
494   NSString* cookieButtonText = l10n_util::GetNSString(
495       IDS_WEBSITE_SETTINGS_SHOW_SITE_DATA);
496   cookiesButton_ = [self addLinkButtonWithText:cookieButtonText
497                                         toView:contentView];
498   [cookiesButton_ setTarget:self];
499   [cookiesButton_ setAction:@selector(showCookiesAndSiteData:)];
500
501   return contentView.get();
502 }
503
504 // Handler for the link button below the list of cookies.
505 - (void)showCookiesAndSiteData:(id)sender {
506   DCHECK(webContents_);
507   content::RecordAction(
508       base::UserMetricsAction("WebsiteSettings_CookiesDialogOpened"));
509   chrome::ShowCollectedCookiesDialog(webContents_);
510 }
511
512 // Handler for the link button to show certificate information.
513 - (void)showCertificateInfo:(id)sender {
514   DCHECK(certificateId_);
515   ShowCertificateViewerByID(webContents_, [self parentWindow], certificateId_);
516 }
517
518 // Handler for the link to show help information about the connection tab.
519 - (void)showHelpPage:(id)sender {
520   webContents_->OpenURL(content::OpenURLParams(
521       GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(),
522       NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false));
523 }
524
525 // Create the contents of the Connection tab and add it to the given tab view.
526 // Returns a weak reference to the tab view item's view.
527 - (NSView*)addConnectionTabToTabView:(NSTabView*)tabView {
528   base::scoped_nsobject<NSTabViewItem> item([[NSTabViewItem alloc] init]);
529   base::scoped_nsobject<NSView> contentView(
530       [[FlippedView alloc] initWithFrame:[tabView_ contentRect]]);
531   [contentView setAutoresizingMask:NSViewWidthSizable];
532
533   // Place all the text and images at the same position. The positions will be
534   // adjusted in performLayout.
535   NSPoint textPosition = NSMakePoint(
536       kFramePadding + kConnectionImageSize + kConnectionImageSpacing,
537       kFramePadding);
538   NSPoint imagePosition = NSMakePoint(kFramePadding, kFramePadding);
539   NSSize imageSize = NSMakeSize(kConnectionImageSize, kConnectionImageSize);
540
541   identityStatusIcon_ = [self addImageWithSize:imageSize
542                                         toView:contentView
543                                        atPoint:imagePosition];
544   identityStatusDescriptionField_ =
545       [self addText:base::string16()
546            withSize:[NSFont smallSystemFontSize]
547                bold:NO
548              toView:contentView.get()
549             atPoint:textPosition];
550
551   separatorAfterIdentity_ = [self addSeparatorToView:contentView];
552   [separatorAfterIdentity_ setAutoresizingMask:NSViewWidthSizable];
553
554   connectionStatusIcon_ = [self addImageWithSize:imageSize
555                                           toView:contentView
556                                          atPoint:imagePosition];
557   connectionStatusDescriptionField_ =
558       [self addText:base::string16()
559            withSize:[NSFont smallSystemFontSize]
560                bold:NO
561              toView:contentView.get()
562             atPoint:textPosition];
563   certificateInfoButton_ = nil;  // This will be created only if necessary.
564   separatorAfterConnection_ = [self addSeparatorToView:contentView];
565   [separatorAfterConnection_ setAutoresizingMask:NSViewWidthSizable];
566
567   firstVisitIcon_ = [self addImageWithSize:imageSize
568                                     toView:contentView
569                                    atPoint:imagePosition];
570   firstVisitHeaderField_ =
571       [self addText:l10n_util::GetStringUTF16(IDS_PAGE_INFO_SITE_INFO_TITLE)
572            withSize:[NSFont smallSystemFontSize]
573                bold:YES
574              toView:contentView.get()
575             atPoint:textPosition];
576   firstVisitDescriptionField_ =
577       [self addText:base::string16()
578            withSize:[NSFont smallSystemFontSize]
579                bold:NO
580              toView:contentView.get()
581             atPoint:textPosition];
582
583   separatorAfterFirstVisit_ = [self addSeparatorToView:contentView];
584   [separatorAfterFirstVisit_ setAutoresizingMask:NSViewWidthSizable];
585
586   NSString* helpButtonText = l10n_util::GetNSString(
587       IDS_PAGE_INFO_HELP_CENTER_LINK);
588   helpButton_ = [self addLinkButtonWithText:helpButtonText
589                                      toView:contentView];
590   [helpButton_ setTarget:self];
591   [helpButton_ setAction:@selector(showHelpPage:)];
592
593   [item setView:contentView.get()];
594   [tabView_ insertTabViewItem:item.get()
595                       atIndex:WebsiteSettingsUI::TAB_ID_CONNECTION];
596
597   return contentView.get();
598 }
599
600 // Set the Y position of |view| to the given position, and return the position
601 // of its bottom edge.
602 - (CGFloat)setYPositionOfView:(NSView*)view to:(CGFloat)position {
603   NSRect frame = [view frame];
604   frame.origin.y = position;
605   [view setFrame:frame];
606   return position + NSHeight(frame);
607 }
608
609 - (void)setWidthOfView:(NSView*)view to:(CGFloat)width {
610   [view setFrameSize:NSMakeSize(width, NSHeight([view frame]))];
611 }
612
613 // Layout all of the controls in the window. This should be called whenever
614 // the content has changed.
615 - (void)performLayout {
616   // Make the content at least as wide as the permissions view.
617   CGFloat contentWidth = std::max([self defaultWindowWidth],
618                                   NSWidth([permissionsView_ frame]));
619
620   // Set the width of the content view now, so that all the text fields will
621   // be sized to fit before their heights and vertical positions are adjusted.
622   // The tab view will only resize the currently selected tab, so resize both
623   // tab content views manually.
624   [self setWidthOfView:contentView_ to:contentWidth];
625   [self setWidthOfView:permissionsTabContentView_ to:contentWidth];
626   [self setWidthOfView:connectionTabContentView_ to:contentWidth];
627
628   // Place the identity status immediately below the identity.
629   [self sizeTextFieldHeightToFit:identityField_];
630   [self sizeTextFieldHeightToFit:identityStatusField_];
631   CGFloat yPos = NSMaxY([identityField_ frame]) + kConnectionHeadlineSpacing;
632   yPos = [self setYPositionOfView:identityStatusField_ to:yPos];
633
634   // Lay out the Permissions tab.
635
636   yPos = [self setYPositionOfView:cookiesView_ to:kFramePadding];
637
638   // Put the link button for cookies and site data just below the cookie info.
639   NSRect cookiesButtonFrame = [cookiesButton_ frame];
640   cookiesButtonFrame.origin.y = yPos;
641   cookiesButtonFrame.origin.x = kFramePadding;
642   [cookiesButton_ setFrame:cookiesButtonFrame];
643
644   // Put the permission info just below the link button.
645   [self setYPositionOfView:permissionsView_
646                         to:NSMaxY(cookiesButtonFrame) + kFramePadding];
647
648   // Lay out the Connection tab.
649
650   // Lay out the identity status section.
651   [self sizeTextFieldHeightToFit:identityStatusDescriptionField_];
652   yPos = std::max(NSMaxY([identityStatusDescriptionField_ frame]),
653                   NSMaxY([identityStatusIcon_ frame]));
654   if (certificateInfoButton_) {
655     NSRect certificateButtonFrame = [certificateInfoButton_ frame];
656     certificateButtonFrame.origin.x = NSMinX(
657         [identityStatusDescriptionField_ frame]);
658     certificateButtonFrame.origin.y = yPos + kVerticalSpacing;
659     [certificateInfoButton_ setFrame:certificateButtonFrame];
660     yPos = NSMaxY(certificateButtonFrame);
661   }
662   yPos = [self setYPositionOfView:separatorAfterIdentity_
663                                to:yPos + kVerticalSpacing];
664   yPos += kVerticalSpacing;
665
666   // Lay out the connection status section.
667   [self sizeTextFieldHeightToFit:connectionStatusDescriptionField_];
668   [self setYPositionOfView:connectionStatusIcon_ to:yPos];
669   [self setYPositionOfView:connectionStatusDescriptionField_ to:yPos];
670   yPos = std::max(NSMaxY([connectionStatusDescriptionField_ frame]),
671                   NSMaxY([connectionStatusIcon_ frame]));
672   yPos = [self setYPositionOfView:separatorAfterConnection_
673                                to:yPos + kVerticalSpacing];
674   yPos += kVerticalSpacing;
675
676   // Lay out the last visit section.
677   [self setYPositionOfView:firstVisitIcon_ to:yPos];
678   [self sizeTextFieldHeightToFit:firstVisitHeaderField_];
679   yPos = [self setYPositionOfView:firstVisitHeaderField_ to:yPos];
680   yPos += kConnectionHeadlineSpacing;
681   [self sizeTextFieldHeightToFit:firstVisitDescriptionField_];
682   yPos = [self setYPositionOfView:firstVisitDescriptionField_ to:yPos];
683   yPos = [self setYPositionOfView:separatorAfterFirstVisit_
684                                to:yPos + kVerticalSpacing];
685   yPos += kVerticalSpacing;
686   [self setYPositionOfView:helpButton_ to:yPos];
687
688   // Adjust the tab view size and place it below the identity status.
689
690   yPos = NSMaxY([identityStatusField_ frame]) + kTabStripTopSpacing;
691   yPos = [self setYPositionOfView:segmentedControl_ to:yPos];
692
693   CGFloat connectionTabHeight = NSMaxY([helpButton_ frame]) + kVerticalSpacing;
694
695   NSRect tabViewFrame = [tabView_ frame];
696   tabViewFrame.origin.y = yPos;
697   tabViewFrame.size.height =
698       std::max(connectionTabHeight, NSMaxY([permissionsView_ frame]));
699   tabViewFrame.size.width = contentWidth;
700   [tabView_ setFrame:tabViewFrame];
701
702   // Adjust the contentView to fit everything.
703   [contentView_ setFrame:NSMakeRect(
704       0, 0, NSWidth(tabViewFrame), NSMaxY(tabViewFrame))];
705
706   [self sizeAndPositionWindow];
707 }
708
709 // Adjust the size of the window to match the size of the content, and position
710 // the bubble anchor appropriately.
711 - (void)sizeAndPositionWindow {
712   NSRect windowFrame = [contentView_ frame];
713   windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
714                                                        toView:nil];
715   // Adjust the origin by the difference in height.
716   windowFrame.origin = [[self window] frame].origin;
717   windowFrame.origin.y -= NSHeight(windowFrame) -
718       NSHeight([[self window] frame]);
719
720   // Resize the window. Only animate if the window is visible, otherwise it
721   // could be "growing" while it's opening, looking awkward.
722   [[self window] setFrame:windowFrame
723                   display:YES
724                   animate:[[self window] isVisible]];
725
726   // Adjust the anchor for the bubble.
727   NSPoint anchorPoint =
728       [self anchorPointForWindowWithHeight:NSHeight(windowFrame)
729                               parentWindow:[self parentWindow]];
730   [self setAnchorPoint:anchorPoint];
731 }
732
733 // Takes in the bubble's height and the parent window, which should be a
734 // BrowserWindow, and gets the proper anchor point for the bubble. The returned
735 // point is in screen coordinates.
736 - (NSPoint)anchorPointForWindowWithHeight:(CGFloat)bubbleHeight
737                              parentWindow:(NSWindow*)parent {
738   BrowserWindowController* controller = [parent windowController];
739   NSPoint origin = NSZeroPoint;
740   if ([controller isKindOfClass:[BrowserWindowController class]]) {
741     LocationBarViewMac* locationBar = [controller locationBarBridge];
742     if (locationBar) {
743       NSPoint bubblePoint = locationBar->GetPageInfoBubblePoint();
744       origin = [parent convertBaseToScreen:bubblePoint];
745     }
746   }
747   return origin;
748 }
749
750 // Sets properties on the given |field| to act as the title or description
751 // labels in the bubble.
752 - (void)configureTextFieldAsLabel:(NSTextField*)textField {
753   [textField setEditable:NO];
754   [textField setSelectable:YES];
755   [textField setDrawsBackground:NO];
756   [textField setBezeled:NO];
757 }
758
759 // Adjust the height of the given text field to match its text.
760 - (void)sizeTextFieldHeightToFit:(NSTextField*)textField {
761   NSRect frame = [textField frame];
762   frame.size.height +=
763      [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
764          textField];
765   [textField setFrame:frame];
766 }
767
768 // Create a new text field and add it to the given array of subviews.
769 // The array will retain a reference to the object.
770 - (NSTextField*)addText:(const base::string16&)text
771                withSize:(CGFloat)fontSize
772                    bold:(BOOL)bold
773                  toView:(NSView*)view
774                 atPoint:(NSPoint)point {
775   // Size the text to take up the full available width, with some padding.
776   // The height is arbitrary as it will be adjusted later.
777   CGFloat width = NSWidth([view frame]) - point.x - kFramePadding;
778   NSRect frame = NSMakeRect(point.x, point.y, width, 100);
779   base::scoped_nsobject<NSTextField> textField(
780       [[NSTextField alloc] initWithFrame:frame]);
781   [self configureTextFieldAsLabel:textField.get()];
782   [textField setStringValue:base::SysUTF16ToNSString(text)];
783   NSFont* font = bold ? [NSFont boldSystemFontOfSize:fontSize]
784                       : [NSFont systemFontOfSize:fontSize];
785   [textField setFont:font];
786   [self sizeTextFieldHeightToFit:textField];
787   [textField setAutoresizingMask:NSViewWidthSizable];
788   [view addSubview:textField.get()];
789   return textField.get();
790 }
791
792 // Add an image as a subview of the given view, placed at a pre-determined x
793 // position and the given y position. Return the new NSImageView.
794 - (NSImageView*)addImageWithSize:(NSSize)size
795                           toView:(NSView*)view
796                          atPoint:(NSPoint)point {
797   NSRect frame = NSMakeRect(point.x, point.y, size.width, size.height);
798   base::scoped_nsobject<NSImageView> imageView(
799       [[NSImageView alloc] initWithFrame:frame]);
800   [imageView setImageFrameStyle:NSImageFrameNone];
801   [view addSubview:imageView.get()];
802   return imageView.get();
803 }
804
805 // Add a separator as a subview of the given view. Return the new view.
806 - (NSView*)addSeparatorToView:(NSView*)view {
807   // Take up almost the full width of the container's frame.
808   CGFloat width = NSWidth([view frame]) - 2 * kFramePadding;
809
810   // Use an arbitrary position; it will be adjusted in performLayout.
811   NSBox* spacer =
812       [self separatorWithFrame:NSMakeRect(kFramePadding, 0, width, 0)];
813   [view addSubview:spacer];
814   return spacer;
815 }
816
817 // Add a link button with the given text to |view|.
818 - (NSButton*)addLinkButtonWithText:(NSString*)text toView:(NSView*)view {
819   // Frame size is arbitrary; it will be adjusted by the layout tweaker.
820   NSRect frame = NSMakeRect(kFramePadding, 0, 100, 10);
821   base::scoped_nsobject<NSButton> button(
822       [[NSButton alloc] initWithFrame:frame]);
823   base::scoped_nsobject<HyperlinkButtonCell> cell(
824       [[HyperlinkButtonCell alloc] initTextCell:text]);
825   [cell setControlSize:NSSmallControlSize];
826   [button setCell:cell.get()];
827   [button setButtonType:NSMomentaryPushInButton];
828   [button setBezelStyle:NSRegularSquareBezelStyle];
829   [view addSubview:button.get()];
830
831   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()];
832   return button.get();
833 }
834
835 // Add a pop-up button for |permissionInfo| to the given view.
836 - (NSPopUpButton*)addPopUpButtonForPermission:
837     (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
838                                        toView:(NSView*)view
839                                       atPoint:(NSPoint)point {
840
841   GURL url = webContents_ ? webContents_->GetURL() : GURL();
842   __block WebsiteSettingsBubbleController* weakSelf = self;
843   PermissionMenuModel::ChangeCallback callback =
844       base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) {
845           [weakSelf onPermissionChanged:permission.type to:permission.setting];
846       });
847   base::scoped_nsobject<PermissionSelectorButton> button(
848       [[PermissionSelectorButton alloc] initWithPermissionInfo:permissionInfo
849                                                         forURL:url
850                                                   withCallback:callback]);
851   // Determine the largest possible size for this button.
852   CGFloat maxTitleWidth =
853       [button maxTitleWidthWithDefaultSetting:permissionInfo.default_setting];
854
855   // Ensure the containing view is large enough to contain the button with its
856   // widest possible title.
857   NSRect containerFrame = [view frame];
858   containerFrame.size.width = std::max(
859       NSWidth(containerFrame), point.x + maxTitleWidth + kFramePadding);
860   [view setFrame:containerFrame];
861   [view addSubview:button.get()];
862   return button.get();
863 }
864
865 // Called when the user changes the setting of a permission.
866 - (void)onPermissionChanged:(ContentSettingsType)permissionType
867                          to:(ContentSetting)newSetting {
868   if (presenter_)
869     presenter_->OnSitePermissionChanged(permissionType, newSetting);
870 }
871
872 // Called when the user changes the selected segment in the segmented control.
873 - (void)tabSelected:(id)sender {
874   [tabView_ selectTabViewItemAtIndex:[segmentedControl_ selectedSegment]];
875 }
876
877 // Adds a new row to the UI listing the permissions. Returns the amount of
878 // vertical space that was taken up by the row.
879 - (NSPoint)addPermission:
880     (const WebsiteSettingsUI::PermissionInfo&)permissionInfo
881                   toView:(NSView*)view
882                  atPoint:(NSPoint)point {
883   base::scoped_nsobject<NSImage> image(
884       [WebsiteSettingsUI::GetPermissionIcon(permissionInfo).ToNSImage()
885           retain]);
886   NSImageView* imageView = [self addImageWithSize:[image size]
887                                            toView:view
888                                           atPoint:point];
889   [imageView setImage:image];
890   point.x += kPermissionImageSize + kPermissionImageSpacing;
891
892   base::string16 labelText =
893       WebsiteSettingsUI::PermissionTypeToUIString(permissionInfo.type) +
894       base::ASCIIToUTF16(":");
895
896   NSTextField* label = [self addText:labelText
897                             withSize:[NSFont smallSystemFontSize]
898                                 bold:NO
899                               toView:view
900                              atPoint:point];
901   [label sizeToFit];
902   NSPoint popUpPosition = NSMakePoint(NSMaxX([label frame]), point.y);
903   NSPopUpButton* button = [self addPopUpButtonForPermission:permissionInfo
904                                                      toView:view
905                                                     atPoint:popUpPosition];
906
907   // Adjust the vertical position of the button so that its title text is
908   // aligned with the label. Assumes that the text is the same size in both.
909   // Also adjust the horizontal position to remove excess space due to the
910   // invisible bezel.
911   NSRect titleRect = [[button cell] titleRectForBounds:[button bounds]];
912   popUpPosition.x -= titleRect.origin.x - kPermissionPopUpXSpacing;
913   popUpPosition.y -= titleRect.origin.y;
914   [button setFrameOrigin:popUpPosition];
915
916   // Align the icon with the text.
917   [self alignPermissionIcon:imageView withTextField:label];
918
919   // Permissions specified by policy or an extension cannot be changed.
920   if (permissionInfo.source == content_settings::SETTING_SOURCE_EXTENSION ||
921       permissionInfo.source == content_settings::SETTING_SOURCE_POLICY) {
922     [button setEnabled:NO];
923   }
924
925   NSRect buttonFrame = [button frame];
926   return NSMakePoint(NSMaxX(buttonFrame), NSMaxY(buttonFrame));
927 }
928
929 // Align an image with a text field by vertically centering the image on
930 // the cap height of the first line of text.
931 - (void)alignPermissionIcon:(NSImageView*)imageView
932               withTextField:(NSTextField*)textField {
933   NSFont* font = [textField font];
934
935   // Calculate the offset from the top of the text field.
936   CGFloat capHeight = [font capHeight];
937   CGFloat offset = (kPermissionImageSize - capHeight) / 2 -
938       ([font ascender] - capHeight) - kPermissionImageYAdjust;
939
940   NSRect frame = [imageView frame];
941   frame.origin.y -= offset;
942   [imageView setFrame:frame];
943 }
944
945 - (CGFloat)addCookieInfo:
946     (const WebsiteSettingsUI::CookieInfo&)cookieInfo
947                   toView:(NSView*)view
948                  atPoint:(NSPoint)point {
949   WebsiteSettingsUI::PermissionInfo info;
950   info.type = CONTENT_SETTINGS_TYPE_COOKIES;
951   info.setting = CONTENT_SETTING_ALLOW;
952   NSImage* image = WebsiteSettingsUI::GetPermissionIcon(info).ToNSImage();
953   NSImageView* imageView = [self addImageWithSize:[image size]
954                                            toView:view
955                                           atPoint:point];
956   [imageView setImage:image];
957   point.x += kPermissionImageSize + kPermissionImageSpacing;
958
959   base::string16 labelText = l10n_util::GetStringFUTF16(
960       IDS_WEBSITE_SETTINGS_SITE_DATA_STATS_LINE,
961       base::UTF8ToUTF16(cookieInfo.cookie_source),
962       base::IntToString16(cookieInfo.allowed),
963       base::IntToString16(cookieInfo.blocked));
964
965   NSTextField* label = [self addText:labelText
966                             withSize:[NSFont smallSystemFontSize]
967                                 bold:NO
968                               toView:view
969                              atPoint:point];
970
971   // Align the icon with the text.
972   [self alignPermissionIcon:imageView withTextField:label];
973
974   return NSHeight([label frame]);
975 }
976
977 // Set the content of the identity and identity status fields.
978 - (void)setIdentityInfo:(const WebsiteSettingsUI::IdentityInfo&)identityInfo {
979   [identityField_ setStringValue:
980       base::SysUTF8ToNSString(identityInfo.site_identity)];
981   [identityStatusField_ setStringValue:
982       base::SysUTF16ToNSString(identityInfo.GetIdentityStatusText())];
983
984   WebsiteSettings::SiteIdentityStatus status = identityInfo.identity_status;
985   if (status == WebsiteSettings::SITE_IDENTITY_STATUS_CERT ||
986       status == WebsiteSettings::SITE_IDENTITY_STATUS_EV_CERT) {
987     [identityStatusField_ setTextColor:IdentityVerifiedTextColor()];
988   }
989   // If there is a certificate, add a button for viewing the certificate info.
990   certificateId_ = identityInfo.cert_id;
991   if (certificateId_) {
992     if (!certificateInfoButton_) {
993       NSString* text = l10n_util::GetNSString(IDS_PAGEINFO_CERT_INFO_BUTTON);
994       certificateInfoButton_ = [self addLinkButtonWithText:text
995           toView:connectionTabContentView_];
996
997       [certificateInfoButton_ setTarget:self];
998       [certificateInfoButton_ setAction:@selector(showCertificateInfo:)];
999     }
1000   } else {
1001     certificateInfoButton_ = nil;
1002   }
1003
1004   [identityStatusIcon_ setImage:WebsiteSettingsUI::GetIdentityIcon(
1005       identityInfo.identity_status).ToNSImage()];
1006   [identityStatusDescriptionField_ setStringValue:
1007       base::SysUTF8ToNSString(identityInfo.identity_status_description)];
1008
1009   [connectionStatusIcon_ setImage:WebsiteSettingsUI::GetConnectionIcon(
1010       identityInfo.connection_status).ToNSImage()];
1011   [connectionStatusDescriptionField_ setStringValue:
1012       base::SysUTF8ToNSString(identityInfo.connection_status_description)];
1013
1014   [self performLayout];
1015 }
1016
1017 - (void)setCookieInfo:(const CookieInfoList&)cookieInfoList {
1018   // The contents of the permissions view can cause the whole window to get
1019   // bigger, but currently permissions are always set before cookie info.
1020   // Check to make sure that's still the case.
1021   DCHECK_GT([[permissionsView_ subviews] count], 0U);
1022
1023   [cookiesView_ setSubviews:[NSArray array]];
1024   NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1025
1026   base::string16 sectionTitle = l10n_util::GetStringUTF16(
1027       IDS_WEBSITE_SETTINGS_TITLE_SITE_DATA);
1028   NSTextField* header = [self addText:sectionTitle
1029                              withSize:[NSFont smallSystemFontSize]
1030                                  bold:YES
1031                                toView:cookiesView_
1032                               atPoint:controlOrigin];
1033   controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1034
1035   for (CookieInfoList::const_iterator it = cookieInfoList.begin();
1036        it != cookieInfoList.end();
1037        ++it) {
1038     controlOrigin.y += kPermissionsTabSpacing;
1039     CGFloat rowHeight = [self addCookieInfo:*it
1040                                      toView:cookiesView_
1041                                     atPoint:controlOrigin];
1042     controlOrigin.y += rowHeight;
1043   }
1044
1045   controlOrigin.y += kPermissionsTabSpacing;
1046   [cookiesView_ setFrameSize:
1047       NSMakeSize(NSWidth([cookiesView_ frame]), controlOrigin.y)];
1048
1049   [self performLayout];
1050 }
1051
1052 - (void)setPermissionInfo:(const PermissionInfoList&)permissionInfoList {
1053   [permissionsView_ setSubviews:[NSArray array]];
1054   NSPoint controlOrigin = NSMakePoint(kFramePadding, 0);
1055
1056   base::string16 sectionTitle = l10n_util::GetStringUTF16(
1057       IDS_WEBSITE_SETTINGS_TITLE_SITE_PERMISSIONS);
1058   NSTextField* header = [self addText:sectionTitle
1059                              withSize:[NSFont smallSystemFontSize]
1060                                  bold:YES
1061                                toView:permissionsView_
1062                               atPoint:controlOrigin];
1063   [self sizeTextFieldHeightToFit:header];
1064   controlOrigin.y += NSHeight([header frame]) + kPermissionsHeadlineSpacing;
1065
1066   for (PermissionInfoList::const_iterator permission =
1067            permissionInfoList.begin();
1068        permission != permissionInfoList.end();
1069        ++permission) {
1070     controlOrigin.y += kPermissionsTabSpacing;
1071     NSPoint rowBottomRight = [self addPermission:*permission
1072                                           toView:permissionsView_
1073                                          atPoint:controlOrigin];
1074     controlOrigin.y = rowBottomRight.y;
1075   }
1076   controlOrigin.y += kFramePadding;
1077   [permissionsView_ setFrameSize:
1078       NSMakeSize(NSWidth([permissionsView_ frame]), controlOrigin.y)];
1079   [self performLayout];
1080 }
1081
1082 - (void)setFirstVisit:(const base::string16&)firstVisit {
1083   [firstVisitIcon_ setImage:
1084       WebsiteSettingsUI::GetFirstVisitIcon(firstVisit).ToNSImage()];
1085   [firstVisitDescriptionField_ setStringValue:
1086       base::SysUTF16ToNSString(firstVisit)];
1087   [self performLayout];
1088 }
1089
1090 - (void)setSelectedTab:(WebsiteSettingsUI::TabId)tabId {
1091   NSInteger index = static_cast<NSInteger>(tabId);
1092   [segmentedControl_ setSelectedSegment:index];
1093   [tabView_ selectTabViewItemAtIndex:index];
1094 }
1095
1096 @end
1097
1098 WebsiteSettingsUIBridge::WebsiteSettingsUIBridge()
1099   : bubble_controller_(nil) {
1100 }
1101
1102 WebsiteSettingsUIBridge::~WebsiteSettingsUIBridge() {
1103 }
1104
1105 void WebsiteSettingsUIBridge::set_bubble_controller(
1106     WebsiteSettingsBubbleController* controller) {
1107   bubble_controller_ = controller;
1108 }
1109
1110 void WebsiteSettingsUIBridge::Show(gfx::NativeWindow parent,
1111                                    Profile* profile,
1112                                    content::WebContents* web_contents,
1113                                    const GURL& url,
1114                                    const content::SSLStatus& ssl) {
1115   bool is_internal_page = InternalChromePage(url);
1116
1117   // Create the bridge. This will be owned by the bubble controller.
1118   WebsiteSettingsUIBridge* bridge = new WebsiteSettingsUIBridge();
1119
1120   // Create the bubble controller. It will dealloc itself when it closes.
1121   WebsiteSettingsBubbleController* bubble_controller =
1122       [[WebsiteSettingsBubbleController alloc]
1123           initWithParentWindow:parent
1124        websiteSettingsUIBridge:bridge
1125                    webContents:web_contents
1126                 isInternalPage:is_internal_page];
1127
1128   if (!is_internal_page) {
1129     // Initialize the presenter, which holds the model and controls the UI.
1130     // This is also owned by the bubble controller.
1131     WebsiteSettings* presenter = new WebsiteSettings(
1132         bridge,
1133         profile,
1134         TabSpecificContentSettings::FromWebContents(web_contents),
1135         InfoBarService::FromWebContents(web_contents),
1136         url,
1137         ssl,
1138         content::CertStore::GetInstance());
1139     [bubble_controller setPresenter:presenter];
1140   }
1141
1142   [bubble_controller showWindow:nil];
1143 }
1144
1145 void WebsiteSettingsUIBridge::SetIdentityInfo(
1146     const WebsiteSettingsUI::IdentityInfo& identity_info) {
1147   [bubble_controller_ setIdentityInfo:identity_info];
1148 }
1149
1150 void WebsiteSettingsUIBridge::SetCookieInfo(
1151     const CookieInfoList& cookie_info_list) {
1152   [bubble_controller_ setCookieInfo:cookie_info_list];
1153 }
1154
1155 void WebsiteSettingsUIBridge::SetPermissionInfo(
1156     const PermissionInfoList& permission_info_list) {
1157   [bubble_controller_ setPermissionInfo:permission_info_list];
1158 }
1159
1160 void WebsiteSettingsUIBridge::SetFirstVisit(const base::string16& first_visit) {
1161   [bubble_controller_ setFirstVisit:first_visit];
1162 }
1163
1164 void WebsiteSettingsUIBridge::SetSelectedTab(TabId tab_id) {
1165   [bubble_controller_ setSelectedTab:tab_id];
1166 }