Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(559)

Side by Side Diff: chrome/browser/cocoa/page_info_bubble_controller.mm

Issue 3461016: [Mac] Convert the page info window to a bubble. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Address nits Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2010 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/cocoa/page_info_bubble_controller.h"
6
7 #include "app/l10n_util_mac.h"
8 #include "app/resource_bundle.h"
9 #include "base/sys_string_conversions.h"
10 #include "chrome/browser/cert_store.h"
11 #include "chrome/browser/certificate_viewer.h"
12 #import "chrome/browser/cocoa/browser_window_controller.h"
13 #import "chrome/browser/cocoa/location_bar/location_bar_view_mac.h"
14 #import "chrome/browser/cocoa/info_bubble_view.h"
15 #import "chrome/browser/cocoa/info_bubble_window.h"
16 #include "chrome/browser/profile.h"
17 #include "grit/generated_resources.h"
18 #include "grit/theme_resources.h"
19 #include "net/base/cert_status_flags.h"
20 #include "net/base/x509_certificate.h"
21 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
22
23 @interface PageInfoBubbleController (Private)
24 - (PageInfoModel*)model;
25 - (NSButton*)certificateButtonWithFrame:(NSRect)frame;
26 - (NSImage*)statusIconForState:(PageInfoModel::SectionInfoState)state;
27 - (void)configureTextFieldAsLabel:(NSTextField*)textField;
28 - (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info
29 toSubviews:(NSMutableArray*)subviews
30 atPoint:(NSPoint)point;
31 - (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
32 toSubviews:(NSMutableArray*)subviews
33 atPoint:(NSPoint)point;
34 - (CGFloat)addCertificateButtonToSubviews:(NSMutableArray*)subviews
35 atOffset:(CGFloat)offset;
36 - (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
37 toSubviews:(NSMutableArray*)subviews
38 atOffset:(CGFloat)offset;
39 @end
40
41 namespace {
42
43 // The width of the window, in view coordinates. The height will be determined
44 // by the content.
45 const NSInteger kWindowWidth = 380;
46
47 // Spacing in between sections.
48 const NSInteger kVerticalSpacing = 10;
49
50 // Padding along on the X-axis between the window frame and content.
51 const NSInteger kFramePadding = 20;
52
53 // Spacing between the title and description text views.
54 const NSInteger kTitleSpacing = 2;
55
56 // Spacing between the image and the text.
57 const NSInteger kImageSpacing = 10;
58
59 // Square size of the image.
60 const CGFloat kImageSize = 30;
61
62 // The X position of the text fields. Variants for with and without an image.
63 const CGFloat kTextXPositionNoImage = kFramePadding;
64 const CGFloat kTextXPosition = kTextXPositionNoImage + kImageSize +
65 kImageSpacing;
66
67 // Width of the text fields.
68 const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
69 kFramePadding * 2);
70
71 // Takes in the bubble's height and the parent window, which should be a
72 // BrowserWindow, and gets the proper anchor point for the bubble. The point is
73 // in screen coordinates.
74 NSPoint AnchorPointFromParentWindow(NSWindow* parent, CGFloat bubbleHeight) {
75 BrowserWindowController* controller = [parent windowController];
76 NSPoint origin = NSZeroPoint;
77 if ([controller isKindOfClass:[BrowserWindowController class]]) {
78 LocationBarViewMac* location_bar = [controller locationBarBridge];
79 if (location_bar) {
80 NSPoint arrowTip = location_bar->GetPageInfoBubblePoint();
81 origin = [parent convertBaseToScreen:arrowTip];
82 origin.y -= bubbleHeight;
83 }
84 }
85 return origin;
86 }
87
88 // Bridge that listens for change notifications from the model.
89 class PageInfoModelBubbleBridge : public PageInfoModel::PageInfoModelObserver {
90 public:
91 PageInfoModelBubbleBridge() : controller_(nil) {}
92
93 // PageInfoModelObserver implementation.
94 virtual void ModelChanged() {
95 [controller_ performLayout];
96 }
97
98 // Sets the controller.
99 void set_controller(PageInfoBubbleController* controller) {
100 controller_ = controller;
101 }
102
103 private:
104 PageInfoBubbleController* controller_; // weak
105 };
106
107 } // namespace
108
109 namespace browser {
110
111 void ShowPageInfoBubble(gfx::NativeWindow parent,
112 Profile* profile,
113 const GURL& url,
114 const NavigationEntry::SSLStatus& ssl,
115 bool show_history) {
116 PageInfoModelBubbleBridge* bridge = new PageInfoModelBubbleBridge();
117 PageInfoModel* model =
118 new PageInfoModel(profile, url, ssl, show_history, bridge);
119 PageInfoBubbleController* controller =
120 [[PageInfoBubbleController alloc] initWithPageInfoModel:model
121 modelObserver:bridge
122 parentWindow:parent];
123 bridge->set_controller(controller);
124 [controller setCertID:ssl.cert_id()];
125 [controller showWindow:nil];
126 }
127
128 } // namespace browser
129
130 @implementation PageInfoBubbleController
131
132 @synthesize certID = certID_;
133
134 - (id)initWithPageInfoModel:(PageInfoModel*)model
135 modelObserver:(PageInfoModel::PageInfoModelObserver*)bridge
136 parentWindow:(NSWindow*)parentWindow {
137 // Use an arbitrary height because it will be changed by the bridge.
138 NSRect contentRect = NSMakeRect(0, 0, kWindowWidth, 50);
139 // Create an empty window into which content is placed.
140 scoped_nsobject<InfoBubbleWindow> window(
141 [[InfoBubbleWindow alloc] initWithContentRect:contentRect
142 styleMask:NSBorderlessWindowMask
143 backing:NSBackingStoreBuffered
144 defer:NO]);
145
146 NSPoint anchorPoint = AnchorPointFromParentWindow(parentWindow, 0);
147 if ((self = [super initWithWindow:window.get()
148 parentWindow:parentWindow
149 anchoredAt:anchorPoint])) {
150 model_.reset(model);
151 bridge_.reset(bridge);
152
153 // Load the image refs.
154 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
155 okImage_.reset([rb.GetNSImageNamed(IDR_PAGEINFO_GOOD) retain]);
156 DCHECK_GE(kImageSize, [okImage_ size].width);
157 DCHECK_GE(kImageSize, [okImage_ size].height);
158 warningMinorImage_.reset(
159 [rb.GetNSImageNamed(IDR_PAGEINFO_WARNING_MINOR) retain]);
160 DCHECK_GE(kImageSize, [warningMinorImage_ size].width);
161 DCHECK_GE(kImageSize, [warningMinorImage_ size].height);
162 warningMajorImage_.reset(
163 [rb.GetNSImageNamed(IDR_PAGEINFO_WARNING_MAJOR) retain]);
164 DCHECK_GE(kImageSize, [warningMajorImage_ size].width);
165 DCHECK_GE(kImageSize, [warningMajorImage_ size].height);
166 errorImage_.reset([rb.GetNSImageNamed(IDR_PAGEINFO_BAD) retain]);
167 DCHECK_GE(kImageSize, [errorImage_ size].width);
168 DCHECK_GE(kImageSize, [errorImage_ size].height);
169
170 [[self bubble] setArrowLocation:info_bubble::kTopLeft];
171
172 [self performLayout];
173 }
174 return self;
175 }
176
177 - (PageInfoModel*)model {
178 return model_.get();
179 }
180
181 - (IBAction)showCertWindow:(id)sender {
182 DCHECK(certID_ != 0);
183 ShowCertificateViewerByID([self parentWindow], certID_);
184 }
185
186 // This will create the subviews for the page info window. The general layout
187 // is 2 or 3 boxed and titled sections, each of which has a status image to
188 // provide visual feedback and a description that explains it. The description
189 // text is usually only 1 or 2 lines, but can be much longer. At the bottom of
190 // the window is a button to view the SSL certificate, which is disabled if
191 // not using HTTPS.
192 - (void)performLayout {
193 // |offset| is the Y position that should be drawn at next.
194 CGFloat offset = kVerticalSpacing;
195
196 // Keep the new subviews in an array that gets replaced at the end.
197 NSMutableArray* subviews = [NSMutableArray array];
198
199 // Build the window from bottom-up because Cocoa's coordinate origin is the
200 // lower left.
201 for (int i = model_->GetSectionCount() - 1; i >= 0; --i) {
202 PageInfoModel::SectionInfo info = model_->GetSectionInfo(i);
203
204 // Only certain sections have images. This affects the X position.
205 BOOL hasImage = info.type == PageInfoModel::SECTION_INFO_IDENTITY ||
206 info.type == PageInfoModel::SECTION_INFO_CONNECTION;
207 CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage);
208
209 if (info.type == PageInfoModel::SECTION_INFO_IDENTITY) {
210 offset += [self addCertificateButtonToSubviews:subviews
211 atOffset:offset];
212 }
213
214 // Create the description of the state.
215 offset += [self addDescriptionViewForInfo:info
216 toSubviews:subviews
217 atPoint:NSMakePoint(xPosition, offset)];
218
219 // Add the title.
220 offset += kTitleSpacing;
221 offset += [self addTitleViewForInfo:info
222 toSubviews:subviews
223 atPoint:NSMakePoint(xPosition, offset)];
224
225 // Insert the image subview for sections that are appropriate.
226 if (hasImage) {
227 [self addImageViewForInfo:info toSubviews:subviews atOffset:offset];
228 }
229
230 // Add the separators.
231 offset += kVerticalSpacing;
232 if (i != 0) {
233 scoped_nsobject<NSBox> spacer(
234 [[NSBox alloc] initWithFrame:NSMakeRect(0, offset, kWindowWidth, 1)]);
235 [spacer setBoxType:NSBoxSeparator];
236 [spacer setBorderType:NSLineBorder];
237 [subviews addObject:spacer.get()];
238 offset += kVerticalSpacing;
239 }
240 }
241
242 // Replace the window's content.
243 [[[self window] contentView] setSubviews:subviews];
244
245 offset += kFramePadding;
246
247 // TODO(rsesek): Remove constant value to account for http://crbug.com/57306.
248 NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, 50);
249 windowFrame.size.height += offset;
250 windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
251 toView:nil];
252 // Just setting |size| will cause the window to grow upwards. Shift the
253 // origin up by grow amount, which causes the window to grow downwards.
254 windowFrame.origin = AnchorPointFromParentWindow([self parentWindow],
255 NSHeight(windowFrame));
256
257 // Resize the window. Only animate if the window is visible, otherwise it
258 // could be "growing" while it's opening, looking awkward.
259 [[self window] setFrame:windowFrame
260 display:YES
261 animate:[[self window] isVisible]];
262 }
263
264 // Creates the button with a given |frame| that, when clicked, will show the
265 // SSL certificate information.
266 - (NSButton*)certificateButtonWithFrame:(NSRect)frame {
267 NSButton* certButton = [[[NSButton alloc] initWithFrame:frame] autorelease];
268 [certButton setTitle:
269 l10n_util::GetNSStringWithFixup(IDS_PAGEINFO_CERT_INFO_BUTTON)];
270 [certButton setButtonType:NSMomentaryPushInButton];
271 [certButton setBezelStyle:NSRoundRectBezelStyle];
272 [certButton setTarget:self];
273 [certButton setAction:@selector(showCertWindow:)];
274 return certButton;
275 }
276
277 // Returns a weak reference to the NSImage instance to used, or nil if none, for
278 // the specified info |state|.
279 - (NSImage*)statusIconForState:(PageInfoModel::SectionInfoState)state {
280 switch (state) {
281 case PageInfoModel::SECTION_STATE_OK:
282 return okImage_.get();
283 case PageInfoModel::SECTION_STATE_WARNING_MINOR:
284 return warningMinorImage_.get();
285 case PageInfoModel::SECTION_STATE_WARNING_MAJOR:
286 return warningMajorImage_.get();
287 case PageInfoModel::SECTION_STATE_ERROR:
288 return errorImage_.get();
289 default:
290 return nil;
291 }
292 }
293
294 // Sets proprties on the given |field| to act as the title or description labels
295 // in the bubble.
296 - (void)configureTextFieldAsLabel:(NSTextField*)textField {
297 [textField setEditable:NO];
298 [textField setDrawsBackground:NO];
299 [textField setBezeled:NO];
300 }
301
302 // Adds the title text field at the given x,y position, and returns the y
303 // position for the next element.
304 - (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info
305 toSubviews:(NSMutableArray*)subviews
306 atPoint:(NSPoint)point {
307 NSRect frame = NSMakeRect(point.x, point.y, kTextWidth, kImageSpacing);
308 scoped_nsobject<NSTextField> titleField(
309 [[NSTextField alloc] initWithFrame:frame]);
310 [self configureTextFieldAsLabel:titleField.get()];
311 [titleField setStringValue:base::SysUTF16ToNSString(info.title)];
312 [titleField setFont:[NSFont boldSystemFontOfSize:11]];
313 frame.size.height +=
314 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
315 titleField];
316 [titleField setFrame:frame];
317 [subviews addObject:titleField.get()];
318 return NSHeight(frame);
319 }
320
321 // Adds the description text field at the given x,y position, and returns the y
322 // position for the next element.
323 - (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
324 toSubviews:(NSMutableArray*)subviews
325 atPoint:(NSPoint)point {
326 NSRect frame = NSMakeRect(point.x, point.y, kTextWidth, kImageSize);
327 scoped_nsobject<NSTextField> textField(
328 [[NSTextField alloc] initWithFrame:frame]);
329 [self configureTextFieldAsLabel:textField.get()];
330 [textField setStringValue:base::SysUTF16ToNSString(info.description)];
331 [textField setFont:[NSFont labelFontOfSize:11]];
332
333 // If the text is oversized, resize the text field.
334 frame.size.height +=
335 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
336 textField];
337 [subviews addObject:textField.get()];
338 return NSHeight(frame);
339 }
340
341 // Adds the certificate button at a pre-determined x position and the given y.
342 // Returns the y position for the next element.
343 - (CGFloat)addCertificateButtonToSubviews:(NSMutableArray*)subviews
344 atOffset:(CGFloat)offset {
345 // Create the certificate button. The frame will be fixed up by GTM, so
346 // use arbitrary values.
347 NSRect frame = NSMakeRect(kTextXPosition, offset, 100, 20);
348 NSButton* certButton = [self certificateButtonWithFrame:frame];
349 [subviews addObject:certButton];
350 [GTMUILocalizerAndLayoutTweaker sizeToFitView:certButton];
351
352 // By default, assume that we don't have certificate information to show.
353 [certButton setEnabled:NO];
354 if (certID_) {
355 scoped_refptr<net::X509Certificate> cert;
356 CertStore::GetSharedInstance()->RetrieveCert(certID_, &cert);
357
358 // Don't bother showing certificates if there isn't one. Gears runs
359 // with no OS root certificate.
360 if (cert.get() && cert->os_cert_handle()) {
361 [certButton setEnabled:YES];
362 }
363 }
364
365 return NSHeight(frame) + kVerticalSpacing;
366 }
367
368 // Adds the state image at a pre-determined x position and the given y. This
369 // does not affect the next Y position because the image is placed next to
370 // a text field that is larger and accounts for the image's size.
371 - (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
372 toSubviews:(NSMutableArray*)subviews
373 atOffset:(CGFloat)offset {
374 NSRect frame = NSMakeRect(kFramePadding, offset - kImageSize, kImageSize,
375 kImageSize);
376 scoped_nsobject<NSImageView> imageView(
377 [[NSImageView alloc] initWithFrame:frame]);
378 [imageView setImageFrameStyle:NSImageFrameNone];
379 [imageView setImage:[self statusIconForState:info.state]];
380 [subviews addObject:imageView.get()];
381 }
382
383 @end
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/page_info_bubble_controller.h ('k') | chrome/browser/cocoa/page_info_bubble_controller_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698