| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |  | 
| 2 // Use of this source code is governed by a BSD-style license that can be |  | 
| 3 // found in the LICENSE file. |  | 
| 4 |  | 
| 5 #import "chrome/browser/ui/cocoa/page_info/website_settings_bubble_controller.h" |  | 
| 6 |  | 
| 7 #import <AppKit/AppKit.h> |  | 
| 8 |  | 
| 9 #include <cmath> |  | 
| 10 |  | 
| 11 #include "base/i18n/rtl.h" |  | 
| 12 #include "base/mac/bind_objc_block.h" |  | 
| 13 #include "base/memory/ptr_util.h" |  | 
| 14 #include "base/strings/sys_string_conversions.h" |  | 
| 15 #import "chrome/browser/certificate_viewer.h" |  | 
| 16 #include "chrome/browser/infobars/infobar_service.h" |  | 
| 17 #include "chrome/browser/profiles/profile.h" |  | 
| 18 #include "chrome/browser/ui/browser_dialogs.h" |  | 
| 19 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |  | 
| 20 #import "chrome/browser/ui/cocoa/info_bubble_view.h" |  | 
| 21 #import "chrome/browser/ui/cocoa/info_bubble_window.h" |  | 
| 22 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |  | 
| 23 #import "chrome/browser/ui/cocoa/page_info/permission_selector_button.h" |  | 
| 24 #include "chrome/browser/ui/page_info/permission_menu_model.h" |  | 
| 25 #import "chrome/browser/ui/tab_dialogs.h" |  | 
| 26 #include "chrome/common/url_constants.h" |  | 
| 27 #include "chrome/grit/chromium_strings.h" |  | 
| 28 #include "chrome/grit/generated_resources.h" |  | 
| 29 #include "chrome/grit/theme_resources.h" |  | 
| 30 #include "components/strings/grit/components_chromium_strings.h" |  | 
| 31 #include "components/strings/grit/components_strings.h" |  | 
| 32 #include "content/public/browser/page_navigator.h" |  | 
| 33 #include "content/public/browser/ssl_host_state_delegate.h" |  | 
| 34 #include "content/public/browser/user_metrics.h" |  | 
| 35 #include "content/public/browser/web_contents.h" |  | 
| 36 #include "content/public/common/url_constants.h" |  | 
| 37 #include "extensions/common/constants.h" |  | 
| 38 #include "skia/ext/skia_utils_mac.h" |  | 
| 39 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTw
      eaker.h" |  | 
| 40 #import "ui/base/cocoa/a11y_util.h" |  | 
| 41 #include "ui/base/cocoa/cocoa_base_utils.h" |  | 
| 42 #import "ui/base/cocoa/controls/hyperlink_button_cell.h" |  | 
| 43 #import "ui/base/cocoa/flipped_view.h" |  | 
| 44 #import "ui/base/cocoa/hover_image_button.h" |  | 
| 45 #include "ui/base/l10n/l10n_util.h" |  | 
| 46 #include "ui/base/material_design/material_design_controller.h" |  | 
| 47 #include "ui/base/resource/resource_bundle.h" |  | 
| 48 #import "ui/gfx/mac/coordinate_conversion.h" |  | 
| 49 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |  | 
| 50 #include "ui/resources/grit/ui_resources.h" |  | 
| 51 |  | 
| 52 using ChosenObjectInfoPtr = |  | 
| 53     std::unique_ptr<WebsiteSettingsUI::ChosenObjectInfo>; |  | 
| 54 using ChosenObjectDeleteCallback = |  | 
| 55     base::Callback<void(const WebsiteSettingsUI::ChosenObjectInfo&)>; |  | 
| 56 |  | 
| 57 namespace { |  | 
| 58 |  | 
| 59 // General --------------------------------------------------------------------- |  | 
| 60 |  | 
| 61 // The default width of the window, in view coordinates. It may be larger to |  | 
| 62 // fit the content. |  | 
| 63 const CGFloat kDefaultWindowWidth = 320; |  | 
| 64 |  | 
| 65 // Padding around each section |  | 
| 66 const CGFloat kSectionVerticalPadding = 20; |  | 
| 67 const CGFloat kSectionHorizontalPadding = 16; |  | 
| 68 |  | 
| 69 // Links are buttons with invisible padding, so we need to move them back to |  | 
| 70 // align with other text. |  | 
| 71 const CGFloat kLinkButtonXAdjustment = 1; |  | 
| 72 |  | 
| 73 // Built-in margin for NSButton to take into account. |  | 
| 74 const CGFloat kNSButtonBuiltinMargin = 4; |  | 
| 75 |  | 
| 76 // Security Section ------------------------------------------------------------ |  | 
| 77 |  | 
| 78 // Spacing between security summary, security details, and cert decisions text. |  | 
| 79 const CGFloat kSecurityParagraphSpacing = 12; |  | 
| 80 |  | 
| 81 // Site Settings Section ------------------------------------------------------- |  | 
| 82 |  | 
| 83 // Square size of the permission images. |  | 
| 84 const CGFloat kPermissionImageSize = 16; |  | 
| 85 |  | 
| 86 // Spacing between a permission image and the text. |  | 
| 87 const CGFloat kPermissionImageSpacing = 6; |  | 
| 88 |  | 
| 89 // Minimum distance between the label and its corresponding menu. |  | 
| 90 const CGFloat kMinSeparationBetweenLabelAndMenu = 16; |  | 
| 91 |  | 
| 92 // Square size of the permission delete button image. |  | 
| 93 const CGFloat kPermissionDeleteImageSize = 16; |  | 
| 94 |  | 
| 95 // The spacing between individual permissions. |  | 
| 96 const CGFloat kPermissionsVerticalSpacing = 16; |  | 
| 97 |  | 
| 98 // Amount to lower each permission icon to align the icon baseline with the |  | 
| 99 // label text. |  | 
| 100 const CGFloat kPermissionIconYAdjustment = 1; |  | 
| 101 |  | 
| 102 // Amount to lower each permission popup button to make its text align with the |  | 
| 103 // permission label. |  | 
| 104 const CGFloat kPermissionPopupButtonYAdjustment = 3; |  | 
| 105 |  | 
| 106 // Internal Page Bubble -------------------------------------------------------- |  | 
| 107 |  | 
| 108 // Padding between the window frame and content for the internal page bubble. |  | 
| 109 const CGFloat kInternalPageFramePadding = 10; |  | 
| 110 |  | 
| 111 // Spacing between the image and text for internal pages. |  | 
| 112 const CGFloat kInternalPageImageSpacing = 10; |  | 
| 113 |  | 
| 114 // ----------------------------------------------------------------------------- |  | 
| 115 |  | 
| 116 // NOTE: This assumes that there will never be more than one website settings |  | 
| 117 // popup shown, and that the one that is shown is associated with the current |  | 
| 118 // window. This matches the behaviour in views: see WebsiteSettingsPopupView. |  | 
| 119 bool g_is_popup_showing = false; |  | 
| 120 |  | 
| 121 // Takes in the parent window, which should be a BrowserWindow, and gets the |  | 
| 122 // proper anchor point for the bubble. The returned point is in screen |  | 
| 123 // coordinates. |  | 
| 124 NSPoint AnchorPointForWindow(NSWindow* parent) { |  | 
| 125   BrowserWindowController* controller = [parent windowController]; |  | 
| 126   NSPoint origin = NSZeroPoint; |  | 
| 127   if ([controller isKindOfClass:[BrowserWindowController class]]) { |  | 
| 128     LocationBarViewMac* location_bar = [controller locationBarBridge]; |  | 
| 129     if (location_bar) { |  | 
| 130       NSPoint bubble_point = location_bar->GetPageInfoBubblePoint(); |  | 
| 131       origin = ui::ConvertPointFromWindowToScreen(parent, bubble_point); |  | 
| 132     } |  | 
| 133   } |  | 
| 134   return origin; |  | 
| 135 } |  | 
| 136 |  | 
| 137 }  // namespace |  | 
| 138 |  | 
| 139 @interface ChosenObjectDeleteButton : HoverImageButton { |  | 
| 140  @private |  | 
| 141   ChosenObjectInfoPtr objectInfo_; |  | 
| 142   ChosenObjectDeleteCallback callback_; |  | 
| 143 } |  | 
| 144 |  | 
| 145 // Designated initializer. Takes ownership of |objectInfo|. |  | 
| 146 - (instancetype)initWithChosenObject:(ChosenObjectInfoPtr)objectInfo |  | 
| 147                              atPoint:(NSPoint)point |  | 
| 148                         withCallback:(ChosenObjectDeleteCallback)callback; |  | 
| 149 |  | 
| 150 // Action when the button is clicked. |  | 
| 151 - (void)deleteClicked:(id)sender; |  | 
| 152 |  | 
| 153 @end |  | 
| 154 |  | 
| 155 @implementation ChosenObjectDeleteButton |  | 
| 156 |  | 
| 157 - (instancetype)initWithChosenObject:(ChosenObjectInfoPtr)objectInfo |  | 
| 158                              atPoint:(NSPoint)point |  | 
| 159                         withCallback:(ChosenObjectDeleteCallback)callback { |  | 
| 160   NSRect frame = NSMakeRect(point.x, point.y, kPermissionDeleteImageSize, |  | 
| 161                             kPermissionDeleteImageSize); |  | 
| 162   if (self = [super initWithFrame:frame]) { |  | 
| 163     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |  | 
| 164     [self setDefaultImage:rb.GetNativeImageNamed(IDR_CLOSE_2).ToNSImage()]; |  | 
| 165     [self setHoverImage:rb.GetNativeImageNamed(IDR_CLOSE_2_H).ToNSImage()]; |  | 
| 166     [self setPressedImage:rb.GetNativeImageNamed(IDR_CLOSE_2_P).ToNSImage()]; |  | 
| 167     [self setBordered:NO]; |  | 
| 168     [self setToolTip:l10n_util::GetNSString( |  | 
| 169                          objectInfo->ui_info.delete_tooltip_string_id)]; |  | 
| 170     [self setTarget:self]; |  | 
| 171     [self setAction:@selector(deleteClicked:)]; |  | 
| 172     objectInfo_ = std::move(objectInfo); |  | 
| 173     callback_ = callback; |  | 
| 174   } |  | 
| 175   return self; |  | 
| 176 } |  | 
| 177 |  | 
| 178 - (void)deleteClicked:(id)sender { |  | 
| 179   callback_.Run(*objectInfo_); |  | 
| 180 } |  | 
| 181 |  | 
| 182 @end |  | 
| 183 |  | 
| 184 @implementation WebsiteSettingsBubbleController |  | 
| 185 |  | 
| 186 - (CGFloat)defaultWindowWidth { |  | 
| 187   return kDefaultWindowWidth; |  | 
| 188 } |  | 
| 189 |  | 
| 190 bool IsInternalURL(const GURL& url) { |  | 
| 191   return url.SchemeIs(content::kChromeUIScheme) || |  | 
| 192          url.SchemeIs(content::kChromeDevToolsScheme) || |  | 
| 193          url.SchemeIs(extensions::kExtensionScheme) || |  | 
| 194          url.SchemeIs(content::kViewSourceScheme); |  | 
| 195 } |  | 
| 196 |  | 
| 197 - (id)initWithParentWindow:(NSWindow*)parentWindow |  | 
| 198     websiteSettingsUIBridge:(WebsiteSettingsUIBridge*)bridge |  | 
| 199                 webContents:(content::WebContents*)webContents |  | 
| 200                         url:(const GURL&)url { |  | 
| 201   DCHECK(parentWindow); |  | 
| 202 |  | 
| 203   webContents_ = webContents; |  | 
| 204   permissionsPresent_ = NO; |  | 
| 205   url_ = url; |  | 
| 206 |  | 
| 207   // Use an arbitrary height; it will be changed in performLayout. |  | 
| 208   NSRect contentRect = NSMakeRect(0, 0, [self defaultWindowWidth], 1); |  | 
| 209   // Create an empty window into which content is placed. |  | 
| 210   base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc] |  | 
| 211       initWithContentRect:contentRect |  | 
| 212                 styleMask:NSBorderlessWindowMask |  | 
| 213                   backing:NSBackingStoreBuffered |  | 
| 214                     defer:NO]); |  | 
| 215 |  | 
| 216   if ((self = [super initWithWindow:window.get() |  | 
| 217                        parentWindow:parentWindow |  | 
| 218                          anchoredAt:NSZeroPoint])) { |  | 
| 219     [[self bubble] setArrowLocation:info_bubble::kTopLeading]; |  | 
| 220 |  | 
| 221     // Create the container view that uses flipped coordinates. |  | 
| 222     NSRect contentFrame = NSMakeRect(0, 0, [self defaultWindowWidth], 300); |  | 
| 223     contentView_.reset([[FlippedView alloc] initWithFrame:contentFrame]); |  | 
| 224 |  | 
| 225     // Replace the window's content. |  | 
| 226     [[[self window] contentView] |  | 
| 227         setSubviews:[NSArray arrayWithObject:contentView_.get()]]; |  | 
| 228 |  | 
| 229     if (IsInternalURL(url_)) { |  | 
| 230       [self initializeContentsForInternalPage:url_]; |  | 
| 231     } else { |  | 
| 232       [self initializeContents]; |  | 
| 233     } |  | 
| 234 |  | 
| 235     bridge_.reset(bridge); |  | 
| 236     bridge_->set_bubble_controller(self); |  | 
| 237   } |  | 
| 238   return self; |  | 
| 239 } |  | 
| 240 |  | 
| 241 - (LocationBarDecoration*)decorationForBubble { |  | 
| 242   BrowserWindowController* controller = [[self parentWindow] windowController]; |  | 
| 243   LocationBarViewMac* location_bar = [controller locationBarBridge]; |  | 
| 244   return location_bar ? location_bar->GetPageInfoDecoration() : nullptr; |  | 
| 245 } |  | 
| 246 |  | 
| 247 - (Profile*)profile { |  | 
| 248   return Profile::FromBrowserContext(webContents_->GetBrowserContext()); |  | 
| 249 } |  | 
| 250 |  | 
| 251 - (void)windowWillClose:(NSNotification*)notification { |  | 
| 252   if (presenter_.get()) |  | 
| 253     presenter_->OnUIClosing(); |  | 
| 254   presenter_.reset(); |  | 
| 255   [super windowWillClose:notification]; |  | 
| 256 } |  | 
| 257 |  | 
| 258 - (void)setPresenter:(WebsiteSettings*)presenter { |  | 
| 259   presenter_.reset(presenter); |  | 
| 260 } |  | 
| 261 |  | 
| 262 // Create the subviews for the bubble for internal Chrome pages. |  | 
| 263 - (void)initializeContentsForInternalPage:(const GURL&)url { |  | 
| 264   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |  | 
| 265 |  | 
| 266   int text = IDS_PAGE_INFO_INTERNAL_PAGE; |  | 
| 267   int icon = IDR_PRODUCT_LOGO_16; |  | 
| 268   if (url.SchemeIs(extensions::kExtensionScheme)) { |  | 
| 269     text = IDS_PAGE_INFO_EXTENSION_PAGE; |  | 
| 270     icon = IDR_PLUGINS_FAVICON; |  | 
| 271   } else if (url.SchemeIs(content::kViewSourceScheme)) { |  | 
| 272     text = IDS_PAGE_INFO_VIEW_SOURCE_PAGE; |  | 
| 273     // view-source scheme uses the same icon as chrome:// pages. |  | 
| 274     icon = IDR_PRODUCT_LOGO_16; |  | 
| 275   } else if (!url.SchemeIs(content::kChromeUIScheme) && |  | 
| 276              !url.SchemeIs(content::kChromeDevToolsScheme)) { |  | 
| 277     NOTREACHED(); |  | 
| 278   } |  | 
| 279 |  | 
| 280   NSPoint controlOrigin = |  | 
| 281       NSMakePoint(kInternalPageFramePadding, |  | 
| 282                   kInternalPageFramePadding + info_bubble::kBubbleArrowHeight); |  | 
| 283   NSImage* productLogoImage = rb.GetNativeImageNamed(icon).ToNSImage(); |  | 
| 284   NSImageView* imageView = [self addImageWithSize:[productLogoImage size] |  | 
| 285                                            toView:contentView_ |  | 
| 286                                           atPoint:controlOrigin]; |  | 
| 287   [imageView setImage:productLogoImage]; |  | 
| 288 |  | 
| 289   NSRect imageFrame = [imageView frame]; |  | 
| 290   controlOrigin.x += NSWidth(imageFrame) + kInternalPageImageSpacing; |  | 
| 291   NSTextField* textField = [self addText:l10n_util::GetStringUTF16(text) |  | 
| 292                                 withSize:[NSFont smallSystemFontSize] |  | 
| 293                                     bold:NO |  | 
| 294                                   toView:contentView_ |  | 
| 295                                  atPoint:controlOrigin]; |  | 
| 296   // Center the image vertically with the text. Previously this code centered |  | 
| 297   // the text vertically while holding the image in place. That produced correct |  | 
| 298   // results when the image, at 26x26, was taller than (or just slightly |  | 
| 299   // shorter) than the text, but produced incorrect results once the icon |  | 
| 300   // shrank to 16x16. The icon should now always be shorter than the text. |  | 
| 301   // See crbug.com/572044 . |  | 
| 302   NSRect textFrame = [textField frame]; |  | 
| 303   imageFrame.origin.y += (NSHeight(textFrame) - NSHeight(imageFrame)) / 2; |  | 
| 304   [imageView setFrame:imageFrame]; |  | 
| 305 |  | 
| 306   // Adjust the contentView to fit everything. |  | 
| 307   CGFloat maxY = std::max(NSMaxY(imageFrame), NSMaxY(textFrame)); |  | 
| 308   [contentView_ setFrame:NSMakeRect(0, 0, [self defaultWindowWidth], |  | 
| 309                                     maxY + kInternalPageFramePadding)]; |  | 
| 310 |  | 
| 311   [self sizeAndPositionWindow]; |  | 
| 312 } |  | 
| 313 |  | 
| 314 // Create the subviews for the website settings bubble. |  | 
| 315 - (void)initializeContents { |  | 
| 316   securitySectionView_ = [self addSecuritySectionToView:contentView_]; |  | 
| 317   separatorAfterSecuritySection_ = [self addSeparatorToView:contentView_]; |  | 
| 318   siteSettingsSectionView_ = [self addSiteSettingsSectionToView:contentView_]; |  | 
| 319 |  | 
| 320   [self performLayout]; |  | 
| 321 } |  | 
| 322 |  | 
| 323 // Create and return a subview for the security section and add it to the given |  | 
| 324 // |superview|. |superview| retains the new view. |  | 
| 325 - (NSView*)addSecuritySectionToView:(NSView*)superview { |  | 
| 326   base::scoped_nsobject<NSView> securitySectionView( |  | 
| 327       [[FlippedView alloc] initWithFrame:[superview frame]]); |  | 
| 328   [superview addSubview:securitySectionView]; |  | 
| 329 |  | 
| 330   // Create a controlOrigin to place the text fields. The y value doesn't |  | 
| 331   // matter, because the correct value is calculated in -performLayout. |  | 
| 332   NSPoint controlOrigin = NSMakePoint(kSectionHorizontalPadding, 0); |  | 
| 333 |  | 
| 334   // Create a text field for the security summary (private/not private/etc.). |  | 
| 335   securitySummaryField_ = [self addText:base::string16() |  | 
| 336                                withSize:[NSFont systemFontSize] |  | 
| 337                                    bold:NO |  | 
| 338                                  toView:securitySectionView |  | 
| 339                                 atPoint:controlOrigin]; |  | 
| 340 |  | 
| 341   securityDetailsField_ = [self addText:base::string16() |  | 
| 342                                withSize:[NSFont smallSystemFontSize] |  | 
| 343                                    bold:NO |  | 
| 344                                  toView:securitySectionView |  | 
| 345                                 atPoint:controlOrigin]; |  | 
| 346 |  | 
| 347   // These will be created only if necessary. |  | 
| 348   resetDecisionsField_ = nil; |  | 
| 349   resetDecisionsButton_ = nil; |  | 
| 350 |  | 
| 351   NSString* connectionHelpButtonText = l10n_util::GetNSString(IDS_LEARN_MORE); |  | 
| 352   connectionHelpButton_ = [self addLinkButtonWithText:connectionHelpButtonText |  | 
| 353                                                toView:securitySectionView]; |  | 
| 354   [connectionHelpButton_ setTarget:self]; |  | 
| 355   [connectionHelpButton_ setAction:@selector(openConnectionHelp:)]; |  | 
| 356 |  | 
| 357   return securitySectionView.get(); |  | 
| 358 } |  | 
| 359 |  | 
| 360 // Create and return a subview for the site settings and add it to the given |  | 
| 361 // |superview|. |superview| retains the new view. |  | 
| 362 - (NSView*)addSiteSettingsSectionToView:(NSView*)superview { |  | 
| 363   base::scoped_nsobject<NSView> siteSettingsSectionView( |  | 
| 364       [[FlippedView alloc] initWithFrame:[superview frame]]); |  | 
| 365   [superview addSubview:siteSettingsSectionView]; |  | 
| 366 |  | 
| 367   // Initialize the two containers that hold the controls. The initial frames |  | 
| 368   // are arbitrary, and will be adjusted after the controls are laid out. |  | 
| 369   cookiesView_ = |  | 
| 370       [[[FlippedView alloc] initWithFrame:[superview frame]] autorelease]; |  | 
| 371   [cookiesView_ setAutoresizingMask:NSViewWidthSizable]; |  | 
| 372   [siteSettingsSectionView addSubview:cookiesView_]; |  | 
| 373 |  | 
| 374   permissionsView_ = |  | 
| 375       [[[FlippedView alloc] initWithFrame:[superview frame]] autorelease]; |  | 
| 376   [siteSettingsSectionView addSubview:permissionsView_]; |  | 
| 377 |  | 
| 378   // Create the link button to view site settings. Its position will be set in |  | 
| 379   // performLayout. |  | 
| 380   NSString* siteSettingsButtonText = |  | 
| 381       l10n_util::GetNSString(IDS_PAGE_INFO_SITE_SETTINGS_LINK); |  | 
| 382   siteSettingsButton_ = [self addButtonWithText:siteSettingsButtonText |  | 
| 383                                          toView:siteSettingsSectionView]; |  | 
| 384   [GTMUILocalizerAndLayoutTweaker sizeToFitView:siteSettingsButton_]; |  | 
| 385 |  | 
| 386   [siteSettingsButton_ setTarget:self]; |  | 
| 387   [siteSettingsButton_ setAction:@selector(showSiteSettingsData:)]; |  | 
| 388 |  | 
| 389   return siteSettingsSectionView.get(); |  | 
| 390 } |  | 
| 391 |  | 
| 392 // Handler for the link button below the list of cookies. |  | 
| 393 - (void)showCookiesAndSiteData:(id)sender { |  | 
| 394   DCHECK(webContents_); |  | 
| 395   DCHECK(presenter_); |  | 
| 396   presenter_->RecordWebsiteSettingsAction( |  | 
| 397       WebsiteSettings::WEBSITE_SETTINGS_COOKIES_DIALOG_OPENED); |  | 
| 398   TabDialogs::FromWebContents(webContents_)->ShowCollectedCookies(); |  | 
| 399 } |  | 
| 400 |  | 
| 401 // Handler for the site settings button below the list of permissions. |  | 
| 402 - (void)showSiteSettingsData:(id)sender { |  | 
| 403   DCHECK(webContents_); |  | 
| 404   DCHECK(presenter_); |  | 
| 405   presenter_->RecordWebsiteSettingsAction( |  | 
| 406       WebsiteSettings::WEBSITE_SETTINGS_SITE_SETTINGS_OPENED); |  | 
| 407   webContents_->OpenURL(content::OpenURLParams( |  | 
| 408       GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(), |  | 
| 409       WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, |  | 
| 410       false)); |  | 
| 411 } |  | 
| 412 |  | 
| 413 // TODO(lgarron): Move some of this to the presenter for separation of concerns |  | 
| 414 // and platform unification. (https://crbug.com/571533) |  | 
| 415 - (void)openConnectionHelp:(id)sender { |  | 
| 416   DCHECK(webContents_); |  | 
| 417   DCHECK(presenter_); |  | 
| 418   presenter_->RecordWebsiteSettingsAction( |  | 
| 419       WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_HELP_OPENED); |  | 
| 420   webContents_->OpenURL(content::OpenURLParams( |  | 
| 421       GURL(chrome::kPageInfoHelpCenterURL), content::Referrer(), |  | 
| 422       WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, |  | 
| 423       false)); |  | 
| 424 } |  | 
| 425 |  | 
| 426 // Handler for the link button to show certificate information. |  | 
| 427 - (void)showCertificateInfo:(id)sender { |  | 
| 428   DCHECK(certificate_.get()); |  | 
| 429   DCHECK(presenter_); |  | 
| 430   presenter_->RecordWebsiteSettingsAction( |  | 
| 431       WebsiteSettings::WEBSITE_SETTINGS_CERTIFICATE_DIALOG_OPENED); |  | 
| 432   ShowCertificateViewer(webContents_, [self parentWindow], certificate_.get()); |  | 
| 433 } |  | 
| 434 |  | 
| 435 // Handler for the link button to revoke user certificate decisions. |  | 
| 436 - (void)resetCertificateDecisions:(id)sender { |  | 
| 437   DCHECK(resetDecisionsButton_); |  | 
| 438   presenter_->OnRevokeSSLErrorBypassButtonPressed(); |  | 
| 439   [self close]; |  | 
| 440 } |  | 
| 441 |  | 
| 442 - (CGFloat)layoutViewAtRTLStart:(NSView*)view withYPosition:(CGFloat)yPos { |  | 
| 443   CGFloat xPos; |  | 
| 444   if (base::i18n::IsRTL()) { |  | 
| 445     xPos = kDefaultWindowWidth - kSectionHorizontalPadding - |  | 
| 446            NSWidth([view frame]) + kNSButtonBuiltinMargin; |  | 
| 447   } else { |  | 
| 448     xPos = kSectionHorizontalPadding - kNSButtonBuiltinMargin; |  | 
| 449   } |  | 
| 450   [view setFrameOrigin:NSMakePoint(xPos, yPos - kNSButtonBuiltinMargin)]; |  | 
| 451   return yPos + NSHeight([view frame]) - kNSButtonBuiltinMargin; |  | 
| 452 } |  | 
| 453 |  | 
| 454 // Set the Y position of |view| to the given position, and return the position |  | 
| 455 // of its bottom edge. |  | 
| 456 - (CGFloat)setYPositionOfView:(NSView*)view to:(CGFloat)position { |  | 
| 457   NSRect frame = [view frame]; |  | 
| 458   frame.origin.y = position; |  | 
| 459   [view setFrame:frame]; |  | 
| 460   return position + NSHeight(frame); |  | 
| 461 } |  | 
| 462 |  | 
| 463 - (void)setWidthOfView:(NSView*)view to:(CGFloat)width { |  | 
| 464   [view setFrameSize:NSMakeSize(width, NSHeight([view frame]))]; |  | 
| 465 } |  | 
| 466 |  | 
| 467 - (void)setHeightOfView:(NSView*)view to:(CGFloat)height { |  | 
| 468   [view setFrameSize:NSMakeSize(NSWidth([view frame]), height)]; |  | 
| 469 } |  | 
| 470 |  | 
| 471 // Layout all of the controls in the window. This should be called whenever |  | 
| 472 // the content has changed. |  | 
| 473 - (void)performLayout { |  | 
| 474   // Make the content at least as wide as the permissions view. |  | 
| 475   CGFloat contentWidth = |  | 
| 476       std::max([self defaultWindowWidth], NSWidth([permissionsView_ frame])); |  | 
| 477 |  | 
| 478   // Set the width of the content view now, so that all the text fields will |  | 
| 479   // be sized to fit before their heights and vertical positions are adjusted. |  | 
| 480   [self setWidthOfView:contentView_ to:contentWidth]; |  | 
| 481   [self setWidthOfView:securitySectionView_ to:contentWidth]; |  | 
| 482   [self setWidthOfView:siteSettingsSectionView_ to:contentWidth]; |  | 
| 483 |  | 
| 484   CGFloat yPos = 0; |  | 
| 485 |  | 
| 486   [self layoutSecuritySection]; |  | 
| 487   yPos = [self setYPositionOfView:securitySectionView_ to:yPos]; |  | 
| 488 |  | 
| 489   yPos = [self setYPositionOfView:separatorAfterSecuritySection_ to:yPos]; |  | 
| 490 |  | 
| 491   [self layoutSiteSettingsSection]; |  | 
| 492   yPos = [self setYPositionOfView:siteSettingsSectionView_ to:yPos]; |  | 
| 493 |  | 
| 494   [contentView_ setFrame:NSMakeRect(0, 0, NSWidth([contentView_ frame]), yPos)]; |  | 
| 495 |  | 
| 496   [self sizeAndPositionWindow]; |  | 
| 497 } |  | 
| 498 |  | 
| 499 - (void)layoutSecuritySection { |  | 
| 500   // Start the layout with the first element. Margins are handled by the caller. |  | 
| 501   CGFloat yPos = 0; |  | 
| 502 |  | 
| 503   [self sizeTextFieldHeightToFit:securitySummaryField_]; |  | 
| 504   yPos = [self setYPositionOfView:securitySummaryField_ |  | 
| 505                                to:yPos + kSectionVerticalPadding]; |  | 
| 506 |  | 
| 507   [self sizeTextFieldHeightToFit:securityDetailsField_]; |  | 
| 508   yPos = [self setYPositionOfView:securityDetailsField_ |  | 
| 509                                to:yPos + kSecurityParagraphSpacing]; |  | 
| 510 |  | 
| 511   [connectionHelpButton_ setFrameOrigin:NSMakePoint(kSectionHorizontalPadding - |  | 
| 512                                                         kLinkButtonXAdjustment, |  | 
| 513                                                     yPos)]; |  | 
| 514   yPos = NSMaxY([connectionHelpButton_ frame]); |  | 
| 515 |  | 
| 516   if (resetDecisionsButton_) { |  | 
| 517     DCHECK(resetDecisionsField_); |  | 
| 518     yPos = [self setYPositionOfView:resetDecisionsField_ |  | 
| 519                                  to:yPos + kSecurityParagraphSpacing]; |  | 
| 520     [resetDecisionsButton_ |  | 
| 521         setFrameOrigin:NSMakePoint(NSMinX([resetDecisionsButton_ frame]) - |  | 
| 522                                        kLinkButtonXAdjustment, |  | 
| 523                                    yPos)]; |  | 
| 524     yPos = NSMaxY([resetDecisionsButton_ frame]); |  | 
| 525   } |  | 
| 526 |  | 
| 527   // Resize the height based on contents. |  | 
| 528   [self setHeightOfView:securitySectionView_ to:yPos + kSectionVerticalPadding]; |  | 
| 529 } |  | 
| 530 |  | 
| 531 - (void)layoutSiteSettingsSection { |  | 
| 532   // Start the layout with the first element. Margins are handled by the caller. |  | 
| 533   CGFloat yPos = 0; |  | 
| 534 |  | 
| 535   yPos = |  | 
| 536       [self setYPositionOfView:cookiesView_ to:yPos + kSectionVerticalPadding]; |  | 
| 537 |  | 
| 538   if (permissionsPresent_) { |  | 
| 539     // Put the permission info just below the link button. |  | 
| 540     yPos = [self setYPositionOfView:permissionsView_ to:yPos]; |  | 
| 541   } |  | 
| 542 |  | 
| 543   yPos = [self layoutViewAtRTLStart:siteSettingsButton_ withYPosition:yPos]; |  | 
| 544 |  | 
| 545   // Resize the height based on contents. |  | 
| 546   [self setHeightOfView:siteSettingsSectionView_ |  | 
| 547                      to:yPos + kSectionVerticalPadding]; |  | 
| 548 } |  | 
| 549 |  | 
| 550 // Adjust the size of the window to match the size of the content, and position |  | 
| 551 // the bubble anchor appropriately. |  | 
| 552 - (void)sizeAndPositionWindow { |  | 
| 553   NSRect windowFrame = [contentView_ frame]; |  | 
| 554   windowFrame.size = |  | 
| 555       [[[self window] contentView] convertSize:windowFrame.size toView:nil]; |  | 
| 556   // Adjust the origin by the difference in height. |  | 
| 557   windowFrame.origin = [[self window] frame].origin; |  | 
| 558   windowFrame.origin.y -= |  | 
| 559       NSHeight(windowFrame) - NSHeight([[self window] frame]); |  | 
| 560 |  | 
| 561   // Resize the window. Only animate if the window is visible, otherwise it |  | 
| 562   // could be "growing" while it's opening, looking awkward. |  | 
| 563   [[self window] setFrame:windowFrame |  | 
| 564                   display:YES |  | 
| 565                   animate:[[self window] isVisible]]; |  | 
| 566 |  | 
| 567   // Adjust the anchor for the bubble. |  | 
| 568   [self setAnchorPoint:AnchorPointForWindow([self parentWindow])]; |  | 
| 569 } |  | 
| 570 |  | 
| 571 // Sets properties on the given |field| to act as the title or description |  | 
| 572 // labels in the bubble. |  | 
| 573 - (void)configureTextFieldAsLabel:(NSTextField*)textField { |  | 
| 574   [textField setEditable:NO]; |  | 
| 575   [textField setSelectable:YES]; |  | 
| 576   [textField setDrawsBackground:NO]; |  | 
| 577   [textField setBezeled:NO]; |  | 
| 578 } |  | 
| 579 |  | 
| 580 // Adjust the height of the given text field to match its text. |  | 
| 581 - (void)sizeTextFieldHeightToFit:(NSTextField*)textField { |  | 
| 582   NSRect frame = [textField frame]; |  | 
| 583   frame.size.height += |  | 
| 584       [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField]; |  | 
| 585   [textField setFrame:frame]; |  | 
| 586 } |  | 
| 587 |  | 
| 588 // Create a new text field and add it to the given array of subviews. |  | 
| 589 // The array will retain a reference to the object. |  | 
| 590 - (NSTextField*)addText:(const base::string16&)text |  | 
| 591                withSize:(CGFloat)fontSize |  | 
| 592                    bold:(BOOL)bold |  | 
| 593                  toView:(NSView*)view |  | 
| 594                 atPoint:(NSPoint)point { |  | 
| 595   // Size the text to take up the full available width, with some padding. |  | 
| 596   // The height is arbitrary as it will be adjusted later. |  | 
| 597   CGFloat width = NSWidth([view frame]) - point.x - kSectionHorizontalPadding; |  | 
| 598   NSRect frame = NSMakeRect(point.x, point.y, width, 100); |  | 
| 599   base::scoped_nsobject<NSTextField> textField( |  | 
| 600       [[NSTextField alloc] initWithFrame:frame]); |  | 
| 601   [self configureTextFieldAsLabel:textField.get()]; |  | 
| 602   [textField setStringValue:base::SysUTF16ToNSString(text)]; |  | 
| 603   NSFont* font = bold ? [NSFont boldSystemFontOfSize:fontSize] |  | 
| 604                       : [NSFont systemFontOfSize:fontSize]; |  | 
| 605   [textField setFont:font]; |  | 
| 606   [self sizeTextFieldHeightToFit:textField]; |  | 
| 607   [textField setAutoresizingMask:NSViewWidthSizable]; |  | 
| 608   [view addSubview:textField.get()]; |  | 
| 609   return textField.get(); |  | 
| 610 } |  | 
| 611 |  | 
| 612 // Add an image as a subview of the given view, placed at a pre-determined x |  | 
| 613 // position and the given y position. The image is not in the accessibility |  | 
| 614 // order, since the image is always accompanied by text in this bubble. Return |  | 
| 615 // the new NSImageView. |  | 
| 616 - (NSImageView*)addImageWithSize:(NSSize)size |  | 
| 617                           toView:(NSView*)view |  | 
| 618                          atPoint:(NSPoint)point { |  | 
| 619   NSRect frame = NSMakeRect(point.x, point.y, size.width, size.height); |  | 
| 620   base::scoped_nsobject<NSImageView> imageView( |  | 
| 621       [[NSImageView alloc] initWithFrame:frame]); |  | 
| 622   ui::a11y_util::HideImageFromAccessibilityOrder(imageView); |  | 
| 623   [imageView setImageFrameStyle:NSImageFrameNone]; |  | 
| 624   [view addSubview:imageView.get()]; |  | 
| 625   return imageView.get(); |  | 
| 626 } |  | 
| 627 |  | 
| 628 // Add a separator as a subview of the given view. Return the new view. |  | 
| 629 - (NSView*)addSeparatorToView:(NSView*)view { |  | 
| 630   // Use an arbitrary position; it will be adjusted in performLayout. |  | 
| 631   NSBox* spacer = [self |  | 
| 632       horizontalSeparatorWithFrame:NSMakeRect(0, 0, NSWidth([view frame]), 0)]; |  | 
| 633   [view addSubview:spacer]; |  | 
| 634   return spacer; |  | 
| 635 } |  | 
| 636 |  | 
| 637 // Add a link button with the given text to |view|. |  | 
| 638 - (NSButton*)addLinkButtonWithText:(NSString*)text toView:(NSView*)view { |  | 
| 639   // Frame size is arbitrary; it will be adjusted by the layout tweaker. |  | 
| 640   NSRect frame = NSMakeRect(kSectionHorizontalPadding, 0, 100, 10); |  | 
| 641   base::scoped_nsobject<NSButton> button( |  | 
| 642       [[NSButton alloc] initWithFrame:frame]); |  | 
| 643   base::scoped_nsobject<HyperlinkButtonCell> cell( |  | 
| 644       [[HyperlinkButtonCell alloc] initTextCell:text]); |  | 
| 645   [cell setControlSize:NSSmallControlSize]; |  | 
| 646   [button setCell:cell.get()]; |  | 
| 647   [button setButtonType:NSMomentaryPushInButton]; |  | 
| 648   [button setBezelStyle:NSRegularSquareBezelStyle]; |  | 
| 649   [view addSubview:button.get()]; |  | 
| 650 |  | 
| 651   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()]; |  | 
| 652   return button.get(); |  | 
| 653 } |  | 
| 654 |  | 
| 655 // Create and return a button with the specified text and add it to the given |  | 
| 656 // |view|. |view| retains the new button. |  | 
| 657 - (NSButton*)addButtonWithText:(NSString*)text toView:(NSView*)view { |  | 
| 658   NSRect containerFrame = [view frame]; |  | 
| 659   // Frame size is arbitrary; it will be adjusted by the layout tweaker. |  | 
| 660   NSRect frame = NSMakeRect(kSectionHorizontalPadding, 0, 100, 10); |  | 
| 661   base::scoped_nsobject<NSButton> button( |  | 
| 662       [[NSButton alloc] initWithFrame:frame]); |  | 
| 663 |  | 
| 664   // Determine the largest possible size for this button. The size is the width |  | 
| 665   // of the connection section minus the padding on both sides minus the |  | 
| 666   // connection image size and spacing. |  | 
| 667   // TODO(lgarron): handle this sizing in -performLayout. |  | 
| 668   CGFloat maxTitleWidth = |  | 
| 669       containerFrame.size.width - kSectionHorizontalPadding * 2; |  | 
| 670 |  | 
| 671   base::scoped_nsobject<NSButtonCell> cell( |  | 
| 672       [[NSButtonCell alloc] initTextCell:text]); |  | 
| 673   [button setCell:cell.get()]; |  | 
| 674   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button.get()]; |  | 
| 675 |  | 
| 676   // Ensure the containing view is large enough to contain the button with its |  | 
| 677   // widest possible title. |  | 
| 678   NSRect buttonFrame = [button frame]; |  | 
| 679   buttonFrame.size.width = maxTitleWidth; |  | 
| 680 |  | 
| 681   [button setFrame:buttonFrame]; |  | 
| 682   [button setButtonType:NSMomentaryPushInButton]; |  | 
| 683   [button setBezelStyle:NSRegularSquareBezelStyle]; |  | 
| 684   [view addSubview:button.get()]; |  | 
| 685 |  | 
| 686   return button.get(); |  | 
| 687 } |  | 
| 688 |  | 
| 689 // Set the content of the identity and identity status fields. |  | 
| 690 - (void)setIdentityInfo:(const WebsiteSettingsUI::IdentityInfo&)identityInfo { |  | 
| 691   std::unique_ptr<WebsiteSettingsUI::SecurityDescription> security_description = |  | 
| 692       identityInfo.GetSecurityDescription(); |  | 
| 693   [securitySummaryField_ |  | 
| 694       setStringValue:base::SysUTF16ToNSString(security_description->summary)]; |  | 
| 695 |  | 
| 696   [securityDetailsField_ |  | 
| 697       setStringValue:SysUTF16ToNSString(security_description->details)]; |  | 
| 698 |  | 
| 699   certificate_ = identityInfo.certificate; |  | 
| 700 |  | 
| 701   if (certificate_ && identityInfo.show_ssl_decision_revoke_button) { |  | 
| 702     resetDecisionsField_ = |  | 
| 703         [self addText:base::string16() |  | 
| 704              withSize:[NSFont smallSystemFontSize] |  | 
| 705                  bold:NO |  | 
| 706                toView:securitySectionView_ |  | 
| 707               atPoint:NSMakePoint(kSectionHorizontalPadding, 0)]; |  | 
| 708     [resetDecisionsField_ |  | 
| 709         setStringValue:l10n_util::GetNSString( |  | 
| 710                            IDS_PAGEINFO_INVALID_CERTIFICATE_DESCRIPTION)]; |  | 
| 711     [self sizeTextFieldHeightToFit:resetDecisionsField_]; |  | 
| 712 |  | 
| 713     resetDecisionsButton_ = |  | 
| 714         [self addLinkButtonWithText: |  | 
| 715                   l10n_util::GetNSString( |  | 
| 716                       IDS_PAGEINFO_RESET_INVALID_CERTIFICATE_DECISIONS_BUTTON) |  | 
| 717                              toView:securitySectionView_]; |  | 
| 718     [resetDecisionsButton_ setTarget:self]; |  | 
| 719     [resetDecisionsButton_ setAction:@selector(resetCertificateDecisions:)]; |  | 
| 720   } |  | 
| 721 |  | 
| 722   [self performLayout]; |  | 
| 723 } |  | 
| 724 |  | 
| 725 // Add a pop-up button for |permissionInfo| to the given view. |  | 
| 726 - (NSPopUpButton*)addPopUpButtonForPermission: |  | 
| 727                       (const WebsiteSettingsUI::PermissionInfo&)permissionInfo |  | 
| 728                                        toView:(NSView*)view |  | 
| 729                                       atPoint:(NSPoint)point { |  | 
| 730   GURL url = webContents_ ? webContents_->GetURL() : GURL(); |  | 
| 731   __block WebsiteSettingsBubbleController* weakSelf = self; |  | 
| 732   PermissionMenuModel::ChangeCallback callback = |  | 
| 733       base::BindBlock(^(const WebsiteSettingsUI::PermissionInfo& permission) { |  | 
| 734         [weakSelf onPermissionChanged:permission.type to:permission.setting]; |  | 
| 735       }); |  | 
| 736   base::scoped_nsobject<PermissionSelectorButton> button( |  | 
| 737       [[PermissionSelectorButton alloc] initWithPermissionInfo:permissionInfo |  | 
| 738                                                         forURL:url |  | 
| 739                                                   withCallback:callback |  | 
| 740                                                        profile:[self profile]]); |  | 
| 741 |  | 
| 742   // Determine the largest possible size for this button. |  | 
| 743   CGFloat maxTitleWidth = |  | 
| 744       [button maxTitleWidthForContentSettingsType:permissionInfo.type |  | 
| 745                                withDefaultSetting:permissionInfo.default_setting |  | 
| 746                                           profile:[self profile]]; |  | 
| 747 |  | 
| 748   // Ensure the containing view is large enough to contain the button with its |  | 
| 749   // widest possible title. |  | 
| 750   NSRect containerFrame = [view frame]; |  | 
| 751   containerFrame.size.width = |  | 
| 752       std::max(NSWidth(containerFrame), |  | 
| 753                point.x + maxTitleWidth + kSectionHorizontalPadding); |  | 
| 754   [view setFrame:containerFrame]; |  | 
| 755   [view addSubview:button.get()]; |  | 
| 756   return button.get(); |  | 
| 757 } |  | 
| 758 |  | 
| 759 // Add a delete button for |objectInfo| to the given view. |  | 
| 760 - (NSButton*)addDeleteButtonForChosenObject:(ChosenObjectInfoPtr)objectInfo |  | 
| 761                                      toView:(NSView*)view |  | 
| 762                                     atPoint:(NSPoint)point { |  | 
| 763   __block WebsiteSettingsBubbleController* weakSelf = self; |  | 
| 764   auto callback = |  | 
| 765       base::BindBlock(^(const WebsiteSettingsUI::ChosenObjectInfo& objectInfo) { |  | 
| 766         [weakSelf onChosenObjectDeleted:objectInfo]; |  | 
| 767       }); |  | 
| 768   base::scoped_nsobject<ChosenObjectDeleteButton> button( |  | 
| 769       [[ChosenObjectDeleteButton alloc] |  | 
| 770           initWithChosenObject:std::move(objectInfo) |  | 
| 771                        atPoint:point |  | 
| 772                   withCallback:callback]); |  | 
| 773 |  | 
| 774   // Ensure the containing view is large enough to contain the button. |  | 
| 775   NSRect containerFrame = [view frame]; |  | 
| 776   containerFrame.size.width = |  | 
| 777       std::max(NSWidth(containerFrame), point.x + kPermissionDeleteImageSize + |  | 
| 778                                             kSectionHorizontalPadding); |  | 
| 779   [view setFrame:containerFrame]; |  | 
| 780   [view addSubview:button.get()]; |  | 
| 781   return button.get(); |  | 
| 782 } |  | 
| 783 |  | 
| 784 // Called when the user changes the setting of a permission. |  | 
| 785 - (void)onPermissionChanged:(ContentSettingsType)permissionType |  | 
| 786                          to:(ContentSetting)newSetting { |  | 
| 787   if (presenter_) |  | 
| 788     presenter_->OnSitePermissionChanged(permissionType, newSetting); |  | 
| 789 } |  | 
| 790 |  | 
| 791 // Called when the user revokes permission for a previously chosen object. |  | 
| 792 - (void)onChosenObjectDeleted:(const WebsiteSettingsUI::ChosenObjectInfo&)info { |  | 
| 793   if (presenter_) |  | 
| 794     presenter_->OnSiteChosenObjectDeleted(info.ui_info, *info.object); |  | 
| 795 } |  | 
| 796 |  | 
| 797 // Adds a new row to the UI listing the permissions. Returns the NSPoint of the |  | 
| 798 // last UI element added (either the permission button, in LTR, or the text |  | 
| 799 // label, in RTL). |  | 
| 800 - (NSPoint)addPermission: |  | 
| 801                (const WebsiteSettingsUI::PermissionInfo&)permissionInfo |  | 
| 802                   toView:(NSView*)view |  | 
| 803                  atPoint:(NSPoint)point { |  | 
| 804   base::string16 labelText = |  | 
| 805       WebsiteSettingsUI::PermissionTypeToUIString(permissionInfo.type); |  | 
| 806   bool isRTL = base::i18n::IsRTL(); |  | 
| 807   base::scoped_nsobject<NSImage> image( |  | 
| 808       [WebsiteSettingsUI::GetPermissionIcon(permissionInfo) |  | 
| 809               .ToNSImage() retain]); |  | 
| 810 |  | 
| 811   NSPoint position; |  | 
| 812   NSImageView* imageView; |  | 
| 813   NSPopUpButton* button; |  | 
| 814   NSTextField* label; |  | 
| 815 |  | 
| 816   CGFloat viewWidth = NSWidth([view frame]); |  | 
| 817 |  | 
| 818   if (isRTL) { |  | 
| 819     point.x = NSWidth([view frame]) - kPermissionImageSize - |  | 
| 820               kSectionHorizontalPadding; |  | 
| 821     imageView = [self addImageWithSize:[image size] toView:view atPoint:point]; |  | 
| 822     [imageView setImage:image]; |  | 
| 823     point.x -= kPermissionImageSpacing; |  | 
| 824 |  | 
| 825     label = [self addText:labelText |  | 
| 826                  withSize:[NSFont systemFontSize] |  | 
| 827                      bold:NO |  | 
| 828                    toView:view |  | 
| 829                   atPoint:point]; |  | 
| 830     [label sizeToFit]; |  | 
| 831     point.x -= NSWidth([label frame]); |  | 
| 832     [label setFrameOrigin:point]; |  | 
| 833 |  | 
| 834     position = |  | 
| 835         NSMakePoint(point.x, point.y + kPermissionPopupButtonYAdjustment); |  | 
| 836     button = [self addPopUpButtonForPermission:permissionInfo |  | 
| 837                                         toView:view |  | 
| 838                                        atPoint:position]; |  | 
| 839     position.x -= NSWidth([button frame]); |  | 
| 840     [button setFrameOrigin:position]; |  | 
| 841   } else { |  | 
| 842     imageView = [self addImageWithSize:[image size] toView:view atPoint:point]; |  | 
| 843     [imageView setImage:image]; |  | 
| 844     point.x += kPermissionImageSize + kPermissionImageSpacing; |  | 
| 845 |  | 
| 846     label = [self addText:labelText |  | 
| 847                  withSize:[NSFont systemFontSize] |  | 
| 848                      bold:NO |  | 
| 849                    toView:view |  | 
| 850                   atPoint:point]; |  | 
| 851     [label sizeToFit]; |  | 
| 852 |  | 
| 853     position = NSMakePoint(NSMaxX([label frame]), |  | 
| 854                            point.y + kPermissionPopupButtonYAdjustment); |  | 
| 855 |  | 
| 856     button = [self addPopUpButtonForPermission:permissionInfo |  | 
| 857                                         toView:view |  | 
| 858                                        atPoint:position]; |  | 
| 859   } |  | 
| 860   [label setToolTip:base::SysUTF16ToNSString(labelText)]; |  | 
| 861 |  | 
| 862   [view setFrameSize:NSMakeSize(viewWidth, NSHeight([view frame]))]; |  | 
| 863 |  | 
| 864   // Adjust the vertical position of the button so that its title text is |  | 
| 865   // aligned with the label. Assumes that the text is the same size in both. |  | 
| 866   // Also adjust the horizontal position to remove excess space due to the |  | 
| 867   // invisible bezel. |  | 
| 868   NSRect titleRect = [[button cell] titleRectForBounds:[button bounds]]; |  | 
| 869   if (isRTL) { |  | 
| 870     position.x = kSectionHorizontalPadding; |  | 
| 871   } else { |  | 
| 872     position.x = kDefaultWindowWidth - kSectionHorizontalPadding - |  | 
| 873                  [button frame].size.width; |  | 
| 874   } |  | 
| 875   position.y -= titleRect.origin.y; |  | 
| 876   [button setFrameOrigin:position]; |  | 
| 877 |  | 
| 878   // Truncate the label if it's too wide. |  | 
| 879   // This is a workaround for https://crbug.com/654268 until MacViews ships. |  | 
| 880   NSRect labelFrame = [label frame]; |  | 
| 881   if (isRTL) { |  | 
| 882     CGFloat maxLabelWidth = NSMaxX(labelFrame) - NSMaxX([button frame]) - |  | 
| 883                             kMinSeparationBetweenLabelAndMenu; |  | 
| 884     if (NSWidth(labelFrame) > maxLabelWidth) { |  | 
| 885       labelFrame.origin.x = NSMaxX(labelFrame) - maxLabelWidth; |  | 
| 886       labelFrame.size.width = maxLabelWidth; |  | 
| 887       [label setFrame:labelFrame]; |  | 
| 888       [[label cell] setLineBreakMode:NSLineBreakByTruncatingTail]; |  | 
| 889     } |  | 
| 890   } else { |  | 
| 891     CGFloat maxLabelWidth = NSMinX([button frame]) - NSMinX(labelFrame) - |  | 
| 892                             kMinSeparationBetweenLabelAndMenu; |  | 
| 893     if (NSWidth(labelFrame) > maxLabelWidth) { |  | 
| 894       labelFrame.size.width = maxLabelWidth; |  | 
| 895       [label setFrame:labelFrame]; |  | 
| 896       [[label cell] setLineBreakMode:NSLineBreakByTruncatingTail]; |  | 
| 897     } |  | 
| 898   } |  | 
| 899 |  | 
| 900   // Align the icon with the text. |  | 
| 901   [self alignPermissionIcon:imageView withTextField:label]; |  | 
| 902 |  | 
| 903   // Permissions specified by policy or an extension cannot be changed. |  | 
| 904   if (permissionInfo.source == content_settings::SETTING_SOURCE_EXTENSION || |  | 
| 905       permissionInfo.source == content_settings::SETTING_SOURCE_POLICY) { |  | 
| 906     [button setEnabled:NO]; |  | 
| 907   } |  | 
| 908 |  | 
| 909   NSRect buttonFrame = [button frame]; |  | 
| 910   return NSMakePoint(NSMaxX(buttonFrame), NSMaxY(buttonFrame)); |  | 
| 911 } |  | 
| 912 |  | 
| 913 // Adds a new row to the UI listing the permissions. Returns the NSPoint of the |  | 
| 914 // last UI element added (either the permission button, in LTR, or the text |  | 
| 915 // label, in RTL). |  | 
| 916 - (NSPoint)addChosenObject:(ChosenObjectInfoPtr)objectInfo |  | 
| 917                     toView:(NSView*)view |  | 
| 918                    atPoint:(NSPoint)point { |  | 
| 919   base::string16 labelText = l10n_util::GetStringFUTF16( |  | 
| 920       objectInfo->ui_info.label_string_id, |  | 
| 921       WebsiteSettingsUI::ChosenObjectToUIString(*objectInfo)); |  | 
| 922   bool isRTL = base::i18n::IsRTL(); |  | 
| 923   base::scoped_nsobject<NSImage> image( |  | 
| 924       [WebsiteSettingsUI::GetChosenObjectIcon(*objectInfo, false) |  | 
| 925               .ToNSImage() retain]); |  | 
| 926 |  | 
| 927   NSPoint position; |  | 
| 928   NSImageView* imageView; |  | 
| 929   NSButton* button; |  | 
| 930   NSTextField* label; |  | 
| 931 |  | 
| 932   CGFloat viewWidth = NSWidth([view frame]); |  | 
| 933 |  | 
| 934   if (isRTL) { |  | 
| 935     point.x = NSWidth([view frame]) - kPermissionImageSize - |  | 
| 936               kPermissionImageSpacing - kSectionHorizontalPadding; |  | 
| 937     imageView = [self addImageWithSize:[image size] toView:view atPoint:point]; |  | 
| 938     [imageView setImage:image]; |  | 
| 939     point.x -= kPermissionImageSpacing; |  | 
| 940 |  | 
| 941     label = [self addText:labelText |  | 
| 942                  withSize:[NSFont systemFontSize] |  | 
| 943                      bold:NO |  | 
| 944                    toView:view |  | 
| 945                   atPoint:point]; |  | 
| 946     [label sizeToFit]; |  | 
| 947     point.x -= NSWidth([label frame]); |  | 
| 948     [label setFrameOrigin:point]; |  | 
| 949 |  | 
| 950     position = NSMakePoint(point.x, point.y); |  | 
| 951     button = [self addDeleteButtonForChosenObject:std::move(objectInfo) |  | 
| 952                                            toView:view |  | 
| 953                                           atPoint:position]; |  | 
| 954     position.x -= NSWidth([button frame]); |  | 
| 955     [button setFrameOrigin:position]; |  | 
| 956   } else { |  | 
| 957     imageView = [self addImageWithSize:[image size] toView:view atPoint:point]; |  | 
| 958     [imageView setImage:image]; |  | 
| 959     point.x += kPermissionImageSize + kPermissionImageSpacing; |  | 
| 960 |  | 
| 961     label = [self addText:labelText |  | 
| 962                  withSize:[NSFont systemFontSize] |  | 
| 963                      bold:NO |  | 
| 964                    toView:view |  | 
| 965                   atPoint:point]; |  | 
| 966     [label sizeToFit]; |  | 
| 967 |  | 
| 968     position = NSMakePoint(NSMaxX([label frame]), point.y); |  | 
| 969     button = [self addDeleteButtonForChosenObject:std::move(objectInfo) |  | 
| 970                                            toView:view |  | 
| 971                                           atPoint:position]; |  | 
| 972   } |  | 
| 973 |  | 
| 974   [view setFrameSize:NSMakeSize(viewWidth, NSHeight([view frame]))]; |  | 
| 975 |  | 
| 976   // Adjust the vertical position of the button so that its title text is |  | 
| 977   // aligned with the label. Assumes that the text is the same size in both. |  | 
| 978   // Also adjust the horizontal position to remove excess space due to the |  | 
| 979   // invisible bezel. |  | 
| 980   NSRect titleRect = [[button cell] titleRectForBounds:[button bounds]]; |  | 
| 981   position.y -= titleRect.origin.y; |  | 
| 982   [button setFrameOrigin:position]; |  | 
| 983 |  | 
| 984   // Align the icon with the text. |  | 
| 985   [self alignPermissionIcon:imageView withTextField:label]; |  | 
| 986 |  | 
| 987   NSRect buttonFrame = [button frame]; |  | 
| 988   return NSMakePoint(NSMaxX(buttonFrame), NSMaxY(buttonFrame)); |  | 
| 989 } |  | 
| 990 |  | 
| 991 // Align an image with a text field by vertically centering the image on |  | 
| 992 // the cap height of the first line of text. |  | 
| 993 - (void)alignPermissionIcon:(NSImageView*)imageView |  | 
| 994               withTextField:(NSTextField*)textField { |  | 
| 995   NSRect frame = [imageView frame]; |  | 
| 996   frame.origin.y += kPermissionIconYAdjustment; |  | 
| 997   [imageView setFrame:frame]; |  | 
| 998 } |  | 
| 999 |  | 
| 1000 - (void)setCookieInfo:(const CookieInfoList&)cookieInfoList { |  | 
| 1001   // A result of re-ordering of the permissions (crbug.com/444244) is |  | 
| 1002   // that sometimes permissions may not be displayed at all, so it's |  | 
| 1003   // incorrect to check they are set before the cookie info. |  | 
| 1004 |  | 
| 1005   // |cookieInfoList| should only ever have 2 items: first- and third-party |  | 
| 1006   // cookies. |  | 
| 1007   DCHECK_EQ(cookieInfoList.size(), 2u); |  | 
| 1008 |  | 
| 1009   int totalAllowed = 0; |  | 
| 1010   for (const auto& i : cookieInfoList) { |  | 
| 1011     totalAllowed += i.allowed; |  | 
| 1012   } |  | 
| 1013   base::string16 label_text = l10n_util::GetPluralStringFUTF16( |  | 
| 1014       IDS_WEBSITE_SETTINGS_NUM_COOKIES, totalAllowed); |  | 
| 1015 |  | 
| 1016   base::string16 sectionTitle = |  | 
| 1017       l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_TITLE_SITE_DATA); |  | 
| 1018   bool isRTL = base::i18n::IsRTL(); |  | 
| 1019 |  | 
| 1020   [cookiesView_ setSubviews:[NSArray array]]; |  | 
| 1021   NSPoint controlOrigin = NSMakePoint(kSectionHorizontalPadding, 0); |  | 
| 1022 |  | 
| 1023   CGFloat viewWidth = NSWidth([cookiesView_ frame]); |  | 
| 1024 |  | 
| 1025   // Reset X for the cookie image. |  | 
| 1026   if (isRTL) { |  | 
| 1027     controlOrigin.x = viewWidth - kPermissionImageSize - |  | 
| 1028                       kPermissionImageSpacing - kSectionHorizontalPadding; |  | 
| 1029   } |  | 
| 1030 |  | 
| 1031   WebsiteSettingsUI::PermissionInfo info; |  | 
| 1032   info.type = CONTENT_SETTINGS_TYPE_COOKIES; |  | 
| 1033   info.setting = CONTENT_SETTING_ALLOW; |  | 
| 1034   // info.default_setting, info.source, and info.is_incognito have not been set, |  | 
| 1035   // but GetPermissionIcon doesn't use any of those. |  | 
| 1036   NSImage* image = WebsiteSettingsUI::GetPermissionIcon(info).ToNSImage(); |  | 
| 1037   NSImageView* imageView = [self addImageWithSize:[image size] |  | 
| 1038                                            toView:cookiesView_ |  | 
| 1039                                           atPoint:controlOrigin]; |  | 
| 1040   [imageView setImage:image]; |  | 
| 1041 |  | 
| 1042   NSButton* cookiesButton = |  | 
| 1043       [self addLinkButtonWithText:base::SysUTF16ToNSString(label_text) |  | 
| 1044                            toView:cookiesView_]; |  | 
| 1045   [cookiesButton setTarget:self]; |  | 
| 1046   [cookiesButton setAction:@selector(showCookiesAndSiteData:)]; |  | 
| 1047 |  | 
| 1048   if (isRTL) { |  | 
| 1049     controlOrigin.x -= kPermissionImageSpacing; |  | 
| 1050     NSTextField* cookiesLabel = |  | 
| 1051         [self addText:l10n_util::GetStringUTF16(IDS_PAGE_INFO_COOKIES) |  | 
| 1052              withSize:[NSFont systemFontSize] |  | 
| 1053                  bold:NO |  | 
| 1054                toView:cookiesView_ |  | 
| 1055               atPoint:controlOrigin]; |  | 
| 1056     [cookiesLabel sizeToFit]; |  | 
| 1057 |  | 
| 1058     NSPoint cookiesLabelOrigin = [cookiesLabel frame].origin; |  | 
| 1059     cookiesLabelOrigin.x -= NSWidth([cookiesLabel frame]); |  | 
| 1060     [cookiesLabel setFrameOrigin:cookiesLabelOrigin]; |  | 
| 1061 |  | 
| 1062     // Align the icon with the text. |  | 
| 1063     [self alignPermissionIcon:imageView withTextField:cookiesLabel]; |  | 
| 1064 |  | 
| 1065     controlOrigin.y += NSHeight([cookiesLabel frame]); |  | 
| 1066     controlOrigin.x -= NSWidth([cookiesButton frame]) - kLinkButtonXAdjustment; |  | 
| 1067     [cookiesButton setFrameOrigin:controlOrigin]; |  | 
| 1068   } else { |  | 
| 1069     controlOrigin.x += kPermissionImageSize + kPermissionImageSpacing; |  | 
| 1070     NSTextField* cookiesLabel = |  | 
| 1071         [self addText:l10n_util::GetStringUTF16(IDS_PAGE_INFO_COOKIES) |  | 
| 1072              withSize:[NSFont systemFontSize] |  | 
| 1073                  bold:NO |  | 
| 1074                toView:cookiesView_ |  | 
| 1075               atPoint:controlOrigin]; |  | 
| 1076     [cookiesLabel sizeToFit]; |  | 
| 1077 |  | 
| 1078     controlOrigin.y += NSHeight([cookiesLabel frame]); |  | 
| 1079     controlOrigin.x -= kLinkButtonXAdjustment; |  | 
| 1080     [cookiesButton setFrameOrigin:controlOrigin]; |  | 
| 1081 |  | 
| 1082     // Align the icon with the text. |  | 
| 1083     [self alignPermissionIcon:imageView withTextField:cookiesLabel]; |  | 
| 1084   } |  | 
| 1085 |  | 
| 1086   controlOrigin.y += NSHeight([cookiesButton frame]); |  | 
| 1087   [cookiesView_ |  | 
| 1088       setFrameSize:NSMakeSize(NSWidth([cookiesView_ frame]), controlOrigin.y)]; |  | 
| 1089 |  | 
| 1090   [self performLayout]; |  | 
| 1091 } |  | 
| 1092 |  | 
| 1093 - (void)setPermissionInfo:(const PermissionInfoList&)permissionInfoList |  | 
| 1094          andChosenObjects:(ChosenObjectInfoList)chosenObjectInfoList { |  | 
| 1095   [permissionsView_ setSubviews:[NSArray array]]; |  | 
| 1096   NSPoint controlOrigin = NSMakePoint(kSectionHorizontalPadding, 0); |  | 
| 1097 |  | 
| 1098   permissionsPresent_ = YES; |  | 
| 1099 |  | 
| 1100   if (permissionInfoList.size() > 0 || chosenObjectInfoList.size() > 0) { |  | 
| 1101     base::string16 sectionTitle = |  | 
| 1102         l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_TITLE_SITE_PERMISSIONS); |  | 
| 1103 |  | 
| 1104     for (const auto& permission : permissionInfoList) { |  | 
| 1105       controlOrigin.y += kPermissionsVerticalSpacing; |  | 
| 1106       NSPoint rowBottomRight = [self addPermission:permission |  | 
| 1107                                             toView:permissionsView_ |  | 
| 1108                                            atPoint:controlOrigin]; |  | 
| 1109       controlOrigin.y = rowBottomRight.y; |  | 
| 1110     } |  | 
| 1111 |  | 
| 1112     for (auto& object : chosenObjectInfoList) { |  | 
| 1113       controlOrigin.y += kPermissionsVerticalSpacing; |  | 
| 1114       NSPoint rowBottomRight = [self addChosenObject:std::move(object) |  | 
| 1115                                               toView:permissionsView_ |  | 
| 1116                                              atPoint:controlOrigin]; |  | 
| 1117       controlOrigin.y = rowBottomRight.y; |  | 
| 1118     } |  | 
| 1119 |  | 
| 1120     controlOrigin.y += kPermissionsVerticalSpacing; |  | 
| 1121   } |  | 
| 1122 |  | 
| 1123   [permissionsView_ setFrameSize:NSMakeSize(NSWidth([permissionsView_ frame]), |  | 
| 1124                                             controlOrigin.y)]; |  | 
| 1125   [self performLayout]; |  | 
| 1126 } |  | 
| 1127 |  | 
| 1128 @end |  | 
| 1129 |  | 
| 1130 WebsiteSettingsUIBridge::WebsiteSettingsUIBridge( |  | 
| 1131     content::WebContents* web_contents) |  | 
| 1132     : content::WebContentsObserver(web_contents), |  | 
| 1133       web_contents_(web_contents), |  | 
| 1134       bubble_controller_(nil) { |  | 
| 1135   DCHECK(!g_is_popup_showing); |  | 
| 1136   g_is_popup_showing = true; |  | 
| 1137 } |  | 
| 1138 |  | 
| 1139 WebsiteSettingsUIBridge::~WebsiteSettingsUIBridge() { |  | 
| 1140   DCHECK(g_is_popup_showing); |  | 
| 1141   g_is_popup_showing = false; |  | 
| 1142 } |  | 
| 1143 |  | 
| 1144 void WebsiteSettingsUIBridge::set_bubble_controller( |  | 
| 1145     WebsiteSettingsBubbleController* controller) { |  | 
| 1146   bubble_controller_ = controller; |  | 
| 1147 } |  | 
| 1148 |  | 
| 1149 void WebsiteSettingsUIBridge::Show( |  | 
| 1150     gfx::NativeWindow parent, |  | 
| 1151     Profile* profile, |  | 
| 1152     content::WebContents* web_contents, |  | 
| 1153     const GURL& virtual_url, |  | 
| 1154     const security_state::SecurityInfo& security_info) { |  | 
| 1155   if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { |  | 
| 1156     chrome::ShowWebsiteSettingsBubbleViewsAtPoint( |  | 
| 1157         gfx::ScreenPointFromNSPoint(AnchorPointForWindow(parent)), profile, |  | 
| 1158         web_contents, virtual_url, security_info); |  | 
| 1159     return; |  | 
| 1160   } |  | 
| 1161 |  | 
| 1162   // Don't show the popup if it's already being shown. Since this method is |  | 
| 1163   // called each time the location icon is clicked, each click toggles the popup |  | 
| 1164   // in and out. |  | 
| 1165   if (g_is_popup_showing) |  | 
| 1166     return; |  | 
| 1167 |  | 
| 1168   // Create the bridge. This will be owned by the bubble controller. |  | 
| 1169   WebsiteSettingsUIBridge* bridge = new WebsiteSettingsUIBridge(web_contents); |  | 
| 1170 |  | 
| 1171   // Create the bubble controller. It will dealloc itself when it closes, |  | 
| 1172   // resetting |g_is_popup_showing|. |  | 
| 1173   WebsiteSettingsBubbleController* bubble_controller = [ |  | 
| 1174       [WebsiteSettingsBubbleController alloc] initWithParentWindow:parent |  | 
| 1175                                            websiteSettingsUIBridge:bridge |  | 
| 1176                                                        webContents:web_contents |  | 
| 1177                                                                url:virtual_url]; |  | 
| 1178 |  | 
| 1179   if (!IsInternalURL(virtual_url)) { |  | 
| 1180     // Initialize the presenter, which holds the model and controls the UI. |  | 
| 1181     // This is also owned by the bubble controller. |  | 
| 1182     WebsiteSettings* presenter = new WebsiteSettings( |  | 
| 1183         bridge, profile, |  | 
| 1184         TabSpecificContentSettings::FromWebContents(web_contents), web_contents, |  | 
| 1185         virtual_url, security_info); |  | 
| 1186     [bubble_controller setPresenter:presenter]; |  | 
| 1187   } |  | 
| 1188 |  | 
| 1189   [bubble_controller showWindow:nil]; |  | 
| 1190 } |  | 
| 1191 |  | 
| 1192 void WebsiteSettingsUIBridge::SetIdentityInfo( |  | 
| 1193     const WebsiteSettingsUI::IdentityInfo& identity_info) { |  | 
| 1194   [bubble_controller_ setIdentityInfo:identity_info]; |  | 
| 1195 } |  | 
| 1196 |  | 
| 1197 void WebsiteSettingsUIBridge::RenderFrameDeleted( |  | 
| 1198     content::RenderFrameHost* render_frame_host) { |  | 
| 1199   if (render_frame_host == web_contents_->GetMainFrame()) { |  | 
| 1200     [bubble_controller_ close]; |  | 
| 1201   } |  | 
| 1202 } |  | 
| 1203 |  | 
| 1204 void WebsiteSettingsUIBridge::SetCookieInfo( |  | 
| 1205     const CookieInfoList& cookie_info_list) { |  | 
| 1206   [bubble_controller_ setCookieInfo:cookie_info_list]; |  | 
| 1207 } |  | 
| 1208 |  | 
| 1209 void WebsiteSettingsUIBridge::SetPermissionInfo( |  | 
| 1210     const PermissionInfoList& permission_info_list, |  | 
| 1211     ChosenObjectInfoList chosen_object_info_list) { |  | 
| 1212   [bubble_controller_ setPermissionInfo:permission_info_list |  | 
| 1213                        andChosenObjects:std::move(chosen_object_info_list)]; |  | 
| 1214 } |  | 
| OLD | NEW | 
|---|