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