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

Side by Side Diff: chrome/browser/ui/cocoa/content_setting_bubble_cocoa.mm

Issue 6294008: [Mac] Organize some files into chrome/browser/ui/cocoa/content_settings/.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 11 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 | Annotate | Revision Log
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/ui/cocoa/content_setting_bubble_cocoa.h"
6
7 #include "app/l10n_util.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/sys_string_conversions.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/blocked_content_container.h"
13 #include "chrome/browser/content_setting_bubble_model.h"
14 #include "chrome/browser/content_settings/host_content_settings_map.h"
15 #include "chrome/browser/plugin_updater.h"
16 #import "chrome/browser/ui/cocoa/hyperlink_button_cell.h"
17 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
18 #import "chrome/browser/ui/cocoa/l10n_util.h"
19 #import "chrome/browser/ui/cocoa/options/content_settings_dialog_controller.h"
20 #include "grit/generated_resources.h"
21 #include "skia/ext/skia_utils_mac.h"
22 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
23 #include "webkit/glue/plugins/plugin_list.h"
24
25 namespace {
26
27 // Must match the tag of the unblock radio button in the xib files.
28 const int kAllowTag = 1;
29
30 // Must match the tag of the block radio button in the xib files.
31 const int kBlockTag = 2;
32
33 // Height of one link in the popup list.
34 const int kLinkHeight = 16;
35
36 // Space between two popup links.
37 const int kLinkPadding = 4;
38
39 // Space taken in total by one popup link.
40 const int kLinkLineHeight = kLinkHeight + kLinkPadding;
41
42 // Space between popup list and surrounding UI elements.
43 const int kLinkOuterPadding = 8;
44
45 // Height of each of the labels in the geolocation bubble.
46 const int kGeoLabelHeight = 14;
47
48 // Height of the "Clear" button in the geolocation bubble.
49 const int kGeoClearButtonHeight = 17;
50
51 // Padding between radio buttons and "Load all plugins" button
52 // in the plugin bubble.
53 const int kLoadAllPluginsButtonVerticalPadding = 8;
54
55 // General padding between elements in the geolocation bubble.
56 const int kGeoPadding = 8;
57
58 // Padding between host names in the geolocation bubble.
59 const int kGeoHostPadding = 4;
60
61 // Minimal padding between "Manage" and "Done" buttons.
62 const int kManageDonePadding = 8;
63
64 void SetControlSize(NSControl* control, NSControlSize controlSize) {
65 CGFloat fontSize = [NSFont systemFontSizeForControlSize:controlSize];
66 NSCell* cell = [control cell];
67 NSFont* font = [NSFont fontWithName:[[cell font] fontName] size:fontSize];
68 [cell setFont:font];
69 [cell setControlSize:controlSize];
70 }
71
72 // Returns an autoreleased NSTextField that is configured to look like a Label
73 // looks in Interface Builder.
74 NSTextField* LabelWithFrame(NSString* text, const NSRect& frame) {
75 NSTextField* label = [[NSTextField alloc] initWithFrame:frame];
76 [label setStringValue:text];
77 [label setSelectable:NO];
78 [label setBezeled:NO];
79 return [label autorelease];
80 }
81
82 } // namespace
83
84 @interface ContentSettingBubbleController(Private)
85 - (id)initWithModel:(ContentSettingBubbleModel*)settingsBubbleModel
86 parentWindow:(NSWindow*)parentWindow
87 anchoredAt:(NSPoint)anchoredAt;
88 - (NSButton*)hyperlinkButtonWithFrame:(NSRect)frame
89 title:(NSString*)title
90 icon:(NSImage*)icon
91 referenceFrame:(NSRect)referenceFrame;
92 - (void)initializeBlockedPluginsList;
93 - (void)initializeTitle;
94 - (void)initializeRadioGroup;
95 - (void)initializePopupList;
96 - (void)initializeGeoLists;
97 - (void)sizeToFitLoadPluginsButton;
98 - (void)sizeToFitManageDoneButtons;
99 - (void)removeInfoButton;
100 - (void)popupLinkClicked:(id)sender;
101 - (void)clearGeolocationForCurrentHost:(id)sender;
102 @end
103
104 @implementation ContentSettingBubbleController
105
106 + (ContentSettingBubbleController*)
107 showForModel:(ContentSettingBubbleModel*)contentSettingBubbleModel
108 parentWindow:(NSWindow*)parentWindow
109 anchoredAt:(NSPoint)anchor {
110 // Autoreleases itself on bubble close.
111 return [[ContentSettingBubbleController alloc]
112 initWithModel:contentSettingBubbleModel
113 parentWindow:parentWindow
114 anchoredAt:anchor];
115 }
116
117 - (id)initWithModel:(ContentSettingBubbleModel*)contentSettingBubbleModel
118 parentWindow:(NSWindow*)parentWindow
119 anchoredAt:(NSPoint)anchoredAt {
120 // This method takes ownership of |contentSettingBubbleModel| in all cases.
121 scoped_ptr<ContentSettingBubbleModel> model(contentSettingBubbleModel);
122 DCHECK(model.get());
123
124 NSString* const nibPaths[] = {
125 @"ContentBlockedCookies",
126 @"ContentBlockedImages",
127 @"ContentBlockedJavaScript",
128 @"ContentBlockedPlugins",
129 @"ContentBlockedPopups",
130 @"ContentBubbleGeolocation",
131 @"", // Notifications do not have a bubble.
132 };
133 COMPILE_ASSERT(arraysize(nibPaths) == CONTENT_SETTINGS_NUM_TYPES,
134 nibPaths_requires_an_entry_for_every_setting_type);
135 const int settingsType = model->content_type();
136 // Nofifications do not have a bubble.
137 CHECK_NE(settingsType, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
138 DCHECK_LT(settingsType, CONTENT_SETTINGS_NUM_TYPES);
139 if ((self = [super initWithWindowNibPath:nibPaths[settingsType]
140 parentWindow:parentWindow
141 anchoredAt:anchoredAt])) {
142 contentSettingBubbleModel_.reset(model.release());
143 [self showWindow:nil];
144 }
145 return self;
146 }
147
148 - (void)initializeTitle {
149 if (!titleLabel_)
150 return;
151
152 NSString* label = base::SysUTF8ToNSString(
153 contentSettingBubbleModel_->bubble_content().title);
154 [titleLabel_ setStringValue:label];
155
156 // Layout title post-localization.
157 CGFloat deltaY = [GTMUILocalizerAndLayoutTweaker
158 sizeToFitFixedWidthTextField:titleLabel_];
159 NSRect windowFrame = [[self window] frame];
160 windowFrame.size.height += deltaY;
161 [[self window] setFrame:windowFrame display:NO];
162 NSRect titleFrame = [titleLabel_ frame];
163 titleFrame.origin.y -= deltaY;
164 [titleLabel_ setFrame:titleFrame];
165 }
166
167 - (void)initializeRadioGroup {
168 // Configure the radio group. For now, only deal with the
169 // strictly needed case of group containing 2 radio buttons.
170 const ContentSettingBubbleModel::RadioGroup& radio_group =
171 contentSettingBubbleModel_->bubble_content().radio_group;
172
173 // Select appropriate radio button.
174 [allowBlockRadioGroup_ selectCellWithTag:
175 radio_group.default_item == 0 ? kAllowTag : kBlockTag];
176
177 const ContentSettingBubbleModel::RadioItems& radio_items =
178 radio_group.radio_items;
179 DCHECK_EQ(2u, radio_items.size()) << "Only 2 radio items per group supported";
180 // Set radio group labels from model.
181 NSCell* radioCell = [allowBlockRadioGroup_ cellWithTag:kAllowTag];
182 [radioCell setTitle:base::SysUTF8ToNSString(radio_items[0])];
183
184 radioCell = [allowBlockRadioGroup_ cellWithTag:kBlockTag];
185 [radioCell setTitle:base::SysUTF8ToNSString(radio_items[1])];
186
187 // Layout radio group labels post-localization.
188 [GTMUILocalizerAndLayoutTweaker
189 wrapRadioGroupForWidth:allowBlockRadioGroup_];
190 CGFloat radioDeltaY = [GTMUILocalizerAndLayoutTweaker
191 sizeToFitView:allowBlockRadioGroup_].height;
192 NSRect windowFrame = [[self window] frame];
193 windowFrame.size.height += radioDeltaY;
194 [[self window] setFrame:windowFrame display:NO];
195 }
196
197 - (NSButton*)hyperlinkButtonWithFrame:(NSRect)frame
198 title:(NSString*)title
199 icon:(NSImage*)icon
200 referenceFrame:(NSRect)referenceFrame {
201 scoped_nsobject<HyperlinkButtonCell> cell([[HyperlinkButtonCell alloc]
202 initTextCell:title]);
203 [cell.get() setAlignment:NSNaturalTextAlignment];
204 if (icon) {
205 [cell.get() setImagePosition:NSImageLeft];
206 [cell.get() setImage:icon];
207 } else {
208 [cell.get() setImagePosition:NSNoImage];
209 }
210 [cell.get() setControlSize:NSSmallControlSize];
211
212 NSButton* button = [[[NSButton alloc] initWithFrame:frame] autorelease];
213 // Cell must be set immediately after construction.
214 [button setCell:cell.get()];
215
216 // If the link text is too long, clamp it.
217 [button sizeToFit];
218 int maxWidth = NSWidth([[self bubble] frame]) - 2 * NSMinX(referenceFrame);
219 NSRect buttonFrame = [button frame];
220 if (NSWidth(buttonFrame) > maxWidth) {
221 buttonFrame.size.width = maxWidth;
222 [button setFrame:buttonFrame];
223 }
224
225 [button setTarget:self];
226 [button setAction:@selector(popupLinkClicked:)];
227 return button;
228 }
229
230 - (void)initializeBlockedPluginsList {
231 NSMutableArray* pluginArray = [NSMutableArray array];
232 const std::set<std::string>& plugins =
233 contentSettingBubbleModel_->bubble_content().resource_identifiers;
234 if (plugins.empty()) {
235 int delta = NSMinY([titleLabel_ frame]) -
236 NSMinY([blockedResourcesField_ frame]);
237 [blockedResourcesField_ removeFromSuperview];
238 NSRect frame = [[self window] frame];
239 frame.size.height -= delta;
240 [[self window] setFrame:frame display:NO];
241 } else {
242 for (std::set<std::string>::iterator it = plugins.begin();
243 it != plugins.end(); ++it) {
244 NSString* name = SysUTF16ToNSString(
245 NPAPI::PluginList::Singleton()->GetPluginGroupName(*it));
246 if ([name length] == 0)
247 name = base::SysUTF8ToNSString(*it);
248 [pluginArray addObject:name];
249 }
250 [blockedResourcesField_
251 setStringValue:[pluginArray componentsJoinedByString:@"\n"]];
252 [GTMUILocalizerAndLayoutTweaker
253 sizeToFitFixedWidthTextField:blockedResourcesField_];
254 }
255 }
256
257 - (void)initializePopupList {
258 // I didn't put the buttons into a NSMatrix because then they are only one
259 // entity in the key view loop. This way, one can tab through all of them.
260 const ContentSettingBubbleModel::PopupItems& popupItems =
261 contentSettingBubbleModel_->bubble_content().popup_items;
262
263 // Get the pre-resize frame of the radio group. Its origin is where the
264 // popup list should go.
265 NSRect radioFrame = [allowBlockRadioGroup_ frame];
266
267 // Make room for the popup list. The bubble view and its subviews autosize
268 // themselves when the window is enlarged.
269 // Heading and radio box are already 1 * kLinkOuterPadding apart in the nib,
270 // so only 1 * kLinkOuterPadding more is needed.
271 int delta = popupItems.size() * kLinkLineHeight - kLinkPadding +
272 kLinkOuterPadding;
273 NSSize deltaSize = NSMakeSize(0, delta);
274 deltaSize = [[[self window] contentView] convertSize:deltaSize toView:nil];
275 NSRect windowFrame = [[self window] frame];
276 windowFrame.size.height += deltaSize.height;
277 [[self window] setFrame:windowFrame display:NO];
278
279 // Create popup list.
280 int topLinkY = NSMaxY(radioFrame) + delta - kLinkHeight;
281 int row = 0;
282 for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
283 it(popupItems.begin()); it != popupItems.end(); ++it, ++row) {
284 const SkBitmap& icon = it->bitmap;
285 NSImage* image = nil;
286 if (!icon.empty())
287 image = gfx::SkBitmapToNSImage(icon);
288
289 std::string title(it->title);
290 // The popup may not have committed a load yet, in which case it won't
291 // have a URL or title.
292 if (title.empty())
293 title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
294
295 NSRect linkFrame =
296 NSMakeRect(NSMinX(radioFrame), topLinkY - kLinkLineHeight * row,
297 200, kLinkHeight);
298 NSButton* button = [self
299 hyperlinkButtonWithFrame:linkFrame
300 title:base::SysUTF8ToNSString(title)
301 icon:image
302 referenceFrame:radioFrame];
303 [[self bubble] addSubview:button];
304 popupLinks_[button] = row;
305 }
306 }
307
308 - (void)initializeGeoLists {
309 // Cocoa has its origin in the lower left corner. This means elements are
310 // added from bottom to top, which explains why loops run backwards and the
311 // order of operations is the other way than on Linux/Windows.
312 const ContentSettingBubbleModel::BubbleContent& content =
313 contentSettingBubbleModel_->bubble_content();
314 NSRect containerFrame = [contentsContainer_ frame];
315 NSRect frame = NSMakeRect(0, 0, NSWidth(containerFrame), kGeoLabelHeight);
316
317 // "Clear" button / text field.
318 if (!content.custom_link.empty()) {
319 scoped_nsobject<NSControl> control;
320 if(content.custom_link_enabled) {
321 NSRect buttonFrame = NSMakeRect(0, 0,
322 NSWidth(containerFrame),
323 kGeoClearButtonHeight);
324 NSButton* button = [[NSButton alloc] initWithFrame:buttonFrame];
325 control.reset(button);
326 [button setTitle:base::SysUTF8ToNSString(content.custom_link)];
327 [button setTarget:self];
328 [button setAction:@selector(clearGeolocationForCurrentHost:)];
329 [button setBezelStyle:NSRoundRectBezelStyle];
330 SetControlSize(button, NSSmallControlSize);
331 [button sizeToFit];
332 } else {
333 // Add the notification that settings will be cleared on next reload.
334 control.reset([LabelWithFrame(
335 base::SysUTF8ToNSString(content.custom_link), frame) retain]);
336 SetControlSize(control.get(), NSSmallControlSize);
337 }
338
339 // If the new control is wider than the container, widen the window.
340 CGFloat controlWidth = NSWidth([control frame]);
341 if (controlWidth > NSWidth(containerFrame)) {
342 NSRect windowFrame = [[self window] frame];
343 windowFrame.size.width += controlWidth - NSWidth(containerFrame);
344 [[self window] setFrame:windowFrame display:NO];
345 // Fetch the updated sizes.
346 containerFrame = [contentsContainer_ frame];
347 frame = NSMakeRect(0, 0, NSWidth(containerFrame), kGeoLabelHeight);
348 }
349
350 DCHECK(control);
351 [contentsContainer_ addSubview:control];
352 frame.origin.y = NSMaxY([control frame]) + kGeoPadding;
353 }
354
355 typedef
356 std::vector<ContentSettingBubbleModel::DomainList>::const_reverse_iterator
357 GeolocationGroupIterator;
358 for (GeolocationGroupIterator i = content.domain_lists.rbegin();
359 i != content.domain_lists.rend(); ++i) {
360 // Add all hosts in the current domain list.
361 for (std::set<std::string>::const_reverse_iterator j = i->hosts.rbegin();
362 j != i->hosts.rend(); ++j) {
363 NSTextField* title = LabelWithFrame(base::SysUTF8ToNSString(*j), frame);
364 SetControlSize(title, NSSmallControlSize);
365 [contentsContainer_ addSubview:title];
366
367 frame.origin.y = NSMaxY(frame) + kGeoHostPadding +
368 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:title];
369 }
370 if (!i->hosts.empty())
371 frame.origin.y += kGeoPadding - kGeoHostPadding;
372
373 // Add the domain list's title.
374 NSTextField* title =
375 LabelWithFrame(base::SysUTF8ToNSString(i->title), frame);
376 SetControlSize(title, NSSmallControlSize);
377 [contentsContainer_ addSubview:title];
378
379 frame.origin.y = NSMaxY(frame) + kGeoPadding +
380 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:title];
381 }
382
383 CGFloat containerHeight = frame.origin.y;
384 // Undo last padding.
385 if (!content.domain_lists.empty())
386 containerHeight -= kGeoPadding;
387
388 // Resize container to fit its subviews, and window to fit the container.
389 NSRect windowFrame = [[self window] frame];
390 windowFrame.size.height += containerHeight - NSHeight(containerFrame);
391 [[self window] setFrame:windowFrame display:NO];
392 containerFrame.size.height = containerHeight;
393 [contentsContainer_ setFrame:containerFrame];
394 }
395
396 - (void)sizeToFitLoadPluginsButton {
397 const ContentSettingBubbleModel::BubbleContent& content =
398 contentSettingBubbleModel_->bubble_content();
399 [loadAllPluginsButton_ setEnabled:content.custom_link_enabled];
400
401 // Resize horizontally to fit button if necessary.
402 NSRect windowFrame = [[self window] frame];
403 int widthNeeded = NSWidth([loadAllPluginsButton_ frame]) +
404 2 * NSMinX([loadAllPluginsButton_ frame]);
405 if (NSWidth(windowFrame) < widthNeeded) {
406 windowFrame.size.width = widthNeeded;
407 [[self window] setFrame:windowFrame display:NO];
408 }
409 }
410
411 - (void)sizeToFitManageDoneButtons {
412 CGFloat actualWidth = NSWidth([[[self window] contentView] frame]);
413 CGFloat requiredWidth = NSMaxX([manageButton_ frame]) + kManageDonePadding +
414 NSWidth([[doneButton_ superview] frame]) - NSMinX([doneButton_ frame]);
415 if (requiredWidth <= actualWidth || !doneButton_ || !manageButton_)
416 return;
417
418 // Resize window, autoresizing takes care of the rest.
419 NSSize size = NSMakeSize(requiredWidth - actualWidth, 0);
420 size = [[[self window] contentView] convertSize:size toView:nil];
421 NSRect frame = [[self window] frame];
422 frame.origin.x -= size.width;
423 frame.size.width += size.width;
424 [[self window] setFrame:frame display:NO];
425 }
426
427 - (void)awakeFromNib {
428 [super awakeFromNib];
429
430 [[self bubble] setArrowLocation:info_bubble::kTopRight];
431
432 // Adapt window size to bottom buttons. Do this before all other layouting.
433 [self sizeToFitManageDoneButtons];
434
435 [self initializeTitle];
436
437 ContentSettingsType type = contentSettingBubbleModel_->content_type();
438 if (type == CONTENT_SETTINGS_TYPE_PLUGINS) {
439 [self sizeToFitLoadPluginsButton];
440 [self initializeBlockedPluginsList];
441 }
442 if (allowBlockRadioGroup_) // not bound in cookie bubble xib
443 [self initializeRadioGroup];
444
445 if (type == CONTENT_SETTINGS_TYPE_POPUPS)
446 [self initializePopupList];
447 if (type == CONTENT_SETTINGS_TYPE_GEOLOCATION)
448 [self initializeGeoLists];
449 }
450
451 ///////////////////////////////////////////////////////////////////////////////
452 // Actual application logic
453
454 - (IBAction)allowBlockToggled:(id)sender {
455 NSButtonCell *selectedCell = [sender selectedCell];
456 contentSettingBubbleModel_->OnRadioClicked(
457 [selectedCell tag] == kAllowTag ? 0 : 1);
458 }
459
460 - (void)popupLinkClicked:(id)sender {
461 content_setting_bubble::PopupLinks::iterator i(popupLinks_.find(sender));
462 DCHECK(i != popupLinks_.end());
463 contentSettingBubbleModel_->OnPopupClicked(i->second);
464 }
465
466 - (void)clearGeolocationForCurrentHost:(id)sender {
467 contentSettingBubbleModel_->OnCustomLinkClicked();
468 [self close];
469 }
470
471 - (IBAction)showMoreInfo:(id)sender {
472 contentSettingBubbleModel_->OnCustomLinkClicked();
473 [self close];
474 }
475
476 - (IBAction)loadAllPlugins:(id)sender {
477 contentSettingBubbleModel_->OnCustomLinkClicked();
478 [self close];
479 }
480
481 - (IBAction)manageBlocking:(id)sender {
482 contentSettingBubbleModel_->OnManageLinkClicked();
483 }
484
485 - (IBAction)closeBubble:(id)sender {
486 [self close];
487 }
488
489 @end // ContentSettingBubbleController
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698