OLD | NEW |
| (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/collected_cookies_mac.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "app/l10n_util_mac.h" | |
10 #include "app/resource_bundle.h" | |
11 #import "base/mac/mac_util.h" | |
12 #include "base/sys_string_conversions.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/browser/tab_contents/tab_contents.h" | |
15 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h" | |
16 #include "chrome/common/notification_details.h" | |
17 #include "chrome/common/notification_source.h" | |
18 #include "grit/generated_resources.h" | |
19 #include "grit/theme_resources.h" | |
20 #include "skia/ext/skia_utils_mac.h" | |
21 #include "third_party/apple/ImageAndTextCell.h" | |
22 #include "third_party/skia/include/core/SkBitmap.h" | |
23 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | |
24 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | |
25 | |
26 namespace { | |
27 // Colors for the infobar. | |
28 const double kBannerGradientColorTop[3] = | |
29 {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0}; | |
30 const double kBannerGradientColorBottom[3] = | |
31 {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0}; | |
32 const double kBannerStrokeColor = 135.0 / 255.0; | |
33 | |
34 // Minimal height for the collected cookies dialog. | |
35 const CGFloat kMinCollectedCookiesViewHeight = 116; | |
36 } // namespace | |
37 | |
38 #pragma mark Bridge between the constrained window delegate and the sheet | |
39 | |
40 // The delegate used to forward the events from the sheet to the constrained | |
41 // window delegate. | |
42 @interface CollectedCookiesSheetBridge : NSObject { | |
43 CollectedCookiesMac* collectedCookies_; // weak | |
44 } | |
45 - (id)initWithCollectedCookiesMac:(CollectedCookiesMac*)collectedCookies; | |
46 - (void)sheetDidEnd:(NSWindow*)sheet | |
47 returnCode:(int)returnCode | |
48 contextInfo:(void*)contextInfo; | |
49 @end | |
50 | |
51 @implementation CollectedCookiesSheetBridge | |
52 - (id)initWithCollectedCookiesMac:(CollectedCookiesMac*)collectedCookies { | |
53 if ((self = [super init])) { | |
54 collectedCookies_ = collectedCookies; | |
55 } | |
56 return self; | |
57 } | |
58 | |
59 - (void)sheetDidEnd:(NSWindow*)sheet | |
60 returnCode:(int)returnCode | |
61 contextInfo:(void*)contextInfo { | |
62 collectedCookies_->OnSheetDidEnd(sheet); | |
63 } | |
64 @end | |
65 | |
66 #pragma mark Constrained window delegate | |
67 | |
68 CollectedCookiesMac::CollectedCookiesMac(NSWindow* parent, | |
69 TabContents* tab_contents) | |
70 : ConstrainedWindowMacDelegateCustomSheet( | |
71 [[[CollectedCookiesSheetBridge alloc] | |
72 initWithCollectedCookiesMac:this] autorelease], | |
73 @selector(sheetDidEnd:returnCode:contextInfo:)), | |
74 tab_contents_(tab_contents) { | |
75 TabSpecificContentSettings* content_settings = | |
76 tab_contents->GetTabSpecificContentSettings(); | |
77 registrar_.Add(this, NotificationType::COLLECTED_COOKIES_SHOWN, | |
78 Source<TabSpecificContentSettings>(content_settings)); | |
79 | |
80 sheet_controller_ = [[CollectedCookiesWindowController alloc] | |
81 initWithTabContents:tab_contents]; | |
82 | |
83 set_sheet([sheet_controller_ window]); | |
84 | |
85 window_ = tab_contents->CreateConstrainedDialog(this); | |
86 } | |
87 | |
88 CollectedCookiesMac::~CollectedCookiesMac() { | |
89 NSWindow* window = [sheet_controller_ window]; | |
90 if (window_ && window && is_sheet_open()) { | |
91 window_ = NULL; | |
92 [NSApp endSheet:window]; | |
93 } | |
94 } | |
95 | |
96 void CollectedCookiesMac::DeleteDelegate() { | |
97 delete this; | |
98 } | |
99 | |
100 void CollectedCookiesMac::Observe(NotificationType type, | |
101 const NotificationSource& source, | |
102 const NotificationDetails& details) { | |
103 DCHECK(type == NotificationType::COLLECTED_COOKIES_SHOWN); | |
104 DCHECK_EQ(Source<TabSpecificContentSettings>(source).ptr(), | |
105 tab_contents_->GetTabSpecificContentSettings()); | |
106 window_->CloseConstrainedWindow(); | |
107 } | |
108 | |
109 void CollectedCookiesMac::OnSheetDidEnd(NSWindow* sheet) { | |
110 [sheet orderOut:sheet_controller_]; | |
111 if (window_) | |
112 window_->CloseConstrainedWindow(); | |
113 } | |
114 | |
115 #pragma mark Window Controller | |
116 | |
117 @interface CollectedCookiesWindowController(Private) | |
118 -(void)showInfoBarForDomain:(const string16&)domain | |
119 setting:(ContentSetting)setting; | |
120 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting; | |
121 -(void)animateInfoBar; | |
122 @end | |
123 | |
124 @implementation CollectedCookiesWindowController | |
125 | |
126 @synthesize allowedCookiesButtonsEnabled = | |
127 allowedCookiesButtonsEnabled_; | |
128 @synthesize blockedCookiesButtonsEnabled = | |
129 blockedCookiesButtonsEnabled_; | |
130 | |
131 @synthesize allowedTreeController = allowedTreeController_; | |
132 @synthesize blockedTreeController = blockedTreeController_; | |
133 | |
134 - (id)initWithTabContents:(TabContents*)tabContents { | |
135 DCHECK(tabContents); | |
136 infoBarVisible_ = NO; | |
137 tabContents_ = tabContents; | |
138 | |
139 NSString* nibpath = | |
140 [base::mac::MainAppBundle() pathForResource:@"CollectedCookies" | |
141 ofType:@"nib"]; | |
142 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { | |
143 [self loadTreeModelFromTabContents]; | |
144 | |
145 animation_.reset([[NSViewAnimation alloc] init]); | |
146 [animation_ setAnimationBlockingMode:NSAnimationNonblocking]; | |
147 } | |
148 return self; | |
149 } | |
150 | |
151 - (void)awakeFromNib { | |
152 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
153 NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO); | |
154 DCHECK(infoIcon); | |
155 [infoBarIcon_ setImage:infoIcon]; | |
156 | |
157 // Initialize the banner gradient and stroke color. | |
158 NSColor* bannerStartingColor = | |
159 [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0] | |
160 green:kBannerGradientColorTop[1] | |
161 blue:kBannerGradientColorTop[2] | |
162 alpha:1.0]; | |
163 NSColor* bannerEndingColor = | |
164 [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0] | |
165 green:kBannerGradientColorBottom[1] | |
166 blue:kBannerGradientColorBottom[2] | |
167 alpha:1.0]; | |
168 scoped_nsobject<NSGradient> bannerGradient( | |
169 [[NSGradient alloc] initWithStartingColor:bannerStartingColor | |
170 endingColor:bannerEndingColor]); | |
171 [infoBar_ setGradient:bannerGradient]; | |
172 | |
173 NSColor* bannerStrokeColor = | |
174 [NSColor colorWithCalibratedWhite:kBannerStrokeColor | |
175 alpha:1.0]; | |
176 [infoBar_ setStrokeColor:bannerStrokeColor]; | |
177 | |
178 // Change the label of the blocked cookies part if necessary. | |
179 if (tabContents_->profile()->GetHostContentSettingsMap()-> | |
180 BlockThirdPartyCookies()) { | |
181 [blockedCookiesText_ setStringValue:l10n_util::GetNSString( | |
182 IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED)]; | |
183 CGFloat textDeltaY = [GTMUILocalizerAndLayoutTweaker | |
184 sizeToFitFixedWidthTextField:blockedCookiesText_]; | |
185 | |
186 // Shrink the upper custom view. | |
187 NSView* upperContentView = [[splitView_ subviews] objectAtIndex:0]; | |
188 NSRect frame = [upperContentView frame]; | |
189 [splitView_ setPosition:(frame.size.height - textDeltaY/2.0) | |
190 ofDividerAtIndex:0]; | |
191 | |
192 // Shrink the lower outline view. | |
193 frame = [lowerScrollView_ frame]; | |
194 frame.size.height -= textDeltaY; | |
195 [lowerScrollView_ setFrame:frame]; | |
196 | |
197 // Move the label down so it actually fits. | |
198 frame = [blockedCookiesText_ frame]; | |
199 frame.origin.y -= textDeltaY; | |
200 [blockedCookiesText_ setFrame:frame]; | |
201 } | |
202 } | |
203 | |
204 - (void)windowWillClose:(NSNotification*)notif { | |
205 [allowedOutlineView_ setDelegate:nil]; | |
206 [blockedOutlineView_ setDelegate:nil]; | |
207 [animation_ stopAnimation]; | |
208 [self autorelease]; | |
209 } | |
210 | |
211 - (IBAction)closeSheet:(id)sender { | |
212 [NSApp endSheet:[self window]]; | |
213 } | |
214 | |
215 - (void)addException:(ContentSetting)setting | |
216 forTreeController:(NSTreeController*)controller { | |
217 NSArray* nodes = [controller selectedNodes]; | |
218 BOOL multipleDomainsChanged = NO; | |
219 string16 lastDomain; | |
220 for (NSTreeNode* treeNode in nodes) { | |
221 CocoaCookieTreeNode* node = [treeNode representedObject]; | |
222 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]); | |
223 if (cookie->GetDetailedInfo().node_type != | |
224 CookieTreeNode::DetailedInfo::TYPE_ORIGIN) { | |
225 continue; | |
226 } | |
227 CookieTreeOriginNode* origin_node = | |
228 static_cast<CookieTreeOriginNode*>(cookie); | |
229 origin_node->CreateContentException( | |
230 tabContents_->profile()->GetHostContentSettingsMap(), | |
231 setting); | |
232 if (!lastDomain.empty()) | |
233 multipleDomainsChanged = YES; | |
234 lastDomain = origin_node->GetTitle(); | |
235 } | |
236 if (multipleDomainsChanged) | |
237 [self showInfoBarForMultipleDomainsAndSetting:setting]; | |
238 else | |
239 [self showInfoBarForDomain:lastDomain setting:setting]; | |
240 } | |
241 | |
242 - (IBAction)allowOrigin:(id)sender { | |
243 [self addException:CONTENT_SETTING_ALLOW | |
244 forTreeController:blockedTreeController_]; | |
245 } | |
246 | |
247 - (IBAction)allowForSessionFromOrigin:(id)sender { | |
248 [self addException:CONTENT_SETTING_SESSION_ONLY | |
249 forTreeController:blockedTreeController_]; | |
250 } | |
251 | |
252 - (IBAction)blockOrigin:(id)sender { | |
253 [self addException:CONTENT_SETTING_BLOCK | |
254 forTreeController:allowedTreeController_]; | |
255 } | |
256 | |
257 - (CGFloat) splitView:(NSSplitView *)sender | |
258 constrainMinCoordinate:(CGFloat)proposedMin | |
259 ofSubviewAt:(NSInteger)offset { | |
260 return proposedMin + kMinCollectedCookiesViewHeight; | |
261 } | |
262 - (CGFloat) splitView:(NSSplitView *)sender | |
263 constrainMaxCoordinate:(CGFloat)proposedMax | |
264 ofSubviewAt:(NSInteger)offset { | |
265 return proposedMax - kMinCollectedCookiesViewHeight; | |
266 } | |
267 - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { | |
268 return YES; | |
269 } | |
270 | |
271 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel { | |
272 return cocoaAllowedTreeModel_.get(); | |
273 } | |
274 - (void)setCocoaAllowedTreeModel:(CocoaCookieTreeNode*)model { | |
275 cocoaAllowedTreeModel_.reset([model retain]); | |
276 } | |
277 | |
278 - (CookiesTreeModel*)allowedTreeModel { | |
279 return allowedTreeModel_.get(); | |
280 } | |
281 | |
282 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel { | |
283 return cocoaBlockedTreeModel_.get(); | |
284 } | |
285 - (void)setCocoaBlockedTreeModel:(CocoaCookieTreeNode*)model { | |
286 cocoaBlockedTreeModel_.reset([model retain]); | |
287 } | |
288 | |
289 - (CookiesTreeModel*)blockedTreeModel { | |
290 return blockedTreeModel_.get(); | |
291 } | |
292 | |
293 - (void)outlineView:(NSOutlineView*)outlineView | |
294 willDisplayCell:(id)cell | |
295 forTableColumn:(NSTableColumn*)tableColumn | |
296 item:(id)item { | |
297 CocoaCookieTreeNode* node = [item representedObject]; | |
298 int index; | |
299 if (outlineView == allowedOutlineView_) | |
300 index = allowedTreeModel_->GetIconIndex([node treeNode]); | |
301 else | |
302 index = blockedTreeModel_->GetIconIndex([node treeNode]); | |
303 NSImage* icon = nil; | |
304 if (index >= 0) | |
305 icon = [icons_ objectAtIndex:index]; | |
306 else | |
307 icon = [icons_ lastObject]; | |
308 DCHECK([cell isKindOfClass:[ImageAndTextCell class]]); | |
309 [static_cast<ImageAndTextCell*>(cell) setImage:icon]; | |
310 } | |
311 | |
312 - (void)outlineViewSelectionDidChange:(NSNotification*)notif { | |
313 BOOL isAllowedOutlineView; | |
314 if ([notif object] == allowedOutlineView_) { | |
315 isAllowedOutlineView = YES; | |
316 } else if ([notif object] == blockedOutlineView_) { | |
317 isAllowedOutlineView = NO; | |
318 } else { | |
319 NOTREACHED(); | |
320 return; | |
321 } | |
322 NSTreeController* controller = | |
323 isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_; | |
324 | |
325 NSArray* nodes = [controller selectedNodes]; | |
326 for (NSTreeNode* treeNode in nodes) { | |
327 CocoaCookieTreeNode* node = [treeNode representedObject]; | |
328 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]); | |
329 if (cookie->GetDetailedInfo().node_type != | |
330 CookieTreeNode::DetailedInfo::TYPE_ORIGIN) { | |
331 continue; | |
332 } | |
333 CookieTreeOriginNode* origin_node = | |
334 static_cast<CookieTreeOriginNode*>(cookie); | |
335 if (origin_node->CanCreateContentException()) { | |
336 if (isAllowedOutlineView) { | |
337 [self setAllowedCookiesButtonsEnabled:YES]; | |
338 } else { | |
339 [self setBlockedCookiesButtonsEnabled:YES]; | |
340 } | |
341 return; | |
342 } | |
343 } | |
344 if (isAllowedOutlineView) { | |
345 [self setAllowedCookiesButtonsEnabled:NO]; | |
346 } else { | |
347 [self setBlockedCookiesButtonsEnabled:NO]; | |
348 } | |
349 } | |
350 | |
351 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds | |
352 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|. | |
353 - (void)loadTreeModelFromTabContents { | |
354 TabSpecificContentSettings* content_settings = | |
355 tabContents_->GetTabSpecificContentSettings(); | |
356 allowedTreeModel_.reset(content_settings->GetAllowedCookiesTreeModel()); | |
357 blockedTreeModel_.reset(content_settings->GetBlockedCookiesTreeModel()); | |
358 | |
359 // Convert the model's icons from Skia to Cocoa. | |
360 std::vector<SkBitmap> skiaIcons; | |
361 allowedTreeModel_->GetIcons(&skiaIcons); | |
362 icons_.reset([[NSMutableArray alloc] init]); | |
363 for (std::vector<SkBitmap>::iterator it = skiaIcons.begin(); | |
364 it != skiaIcons.end(); ++it) { | |
365 [icons_ addObject:gfx::SkBitmapToNSImage(*it)]; | |
366 } | |
367 | |
368 // Default icon will be the last item in the array. | |
369 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
370 // TODO(rsesek): Rename this resource now that it's in multiple places. | |
371 [icons_ addObject:rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER)]; | |
372 | |
373 // Create the Cocoa model. | |
374 CookieTreeNode* root = | |
375 static_cast<CookieTreeNode*>(allowedTreeModel_->GetRoot()); | |
376 scoped_nsobject<CocoaCookieTreeNode> model( | |
377 [[CocoaCookieTreeNode alloc] initWithNode:root]); | |
378 [self setCocoaAllowedTreeModel:model.get()]; // Takes ownership. | |
379 root = static_cast<CookieTreeNode*>(blockedTreeModel_->GetRoot()); | |
380 model.reset( | |
381 [[CocoaCookieTreeNode alloc] initWithNode:root]); | |
382 [self setCocoaBlockedTreeModel:model.get()]; // Takes ownership. | |
383 } | |
384 | |
385 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting { | |
386 NSString* label; | |
387 switch (setting) { | |
388 case CONTENT_SETTING_BLOCK: | |
389 label = l10n_util::GetNSString( | |
390 IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED); | |
391 break; | |
392 | |
393 case CONTENT_SETTING_ALLOW: | |
394 label = l10n_util::GetNSString( | |
395 IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED); | |
396 break; | |
397 | |
398 case CONTENT_SETTING_SESSION_ONLY: | |
399 label = l10n_util::GetNSString( | |
400 IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED); | |
401 break; | |
402 | |
403 default: | |
404 NOTREACHED(); | |
405 label = [[[NSString alloc] init] autorelease]; | |
406 } | |
407 [infoBarText_ setStringValue:label]; | |
408 [self animateInfoBar]; | |
409 } | |
410 | |
411 -(void)showInfoBarForDomain:(const string16&)domain | |
412 setting:(ContentSetting)setting { | |
413 NSString* label; | |
414 switch (setting) { | |
415 case CONTENT_SETTING_BLOCK: | |
416 label = l10n_util::GetNSStringF( | |
417 IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED, | |
418 domain); | |
419 break; | |
420 | |
421 case CONTENT_SETTING_ALLOW: | |
422 label = l10n_util::GetNSStringF( | |
423 IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED, | |
424 domain); | |
425 break; | |
426 | |
427 case CONTENT_SETTING_SESSION_ONLY: | |
428 label = l10n_util::GetNSStringF( | |
429 IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED, | |
430 domain); | |
431 break; | |
432 | |
433 default: | |
434 NOTREACHED(); | |
435 label = [[[NSString alloc] init] autorelease]; | |
436 } | |
437 [infoBarText_ setStringValue:label]; | |
438 [self animateInfoBar]; | |
439 } | |
440 | |
441 -(void)animateInfoBar { | |
442 if (infoBarVisible_) | |
443 return; | |
444 | |
445 infoBarVisible_ = YES; | |
446 | |
447 NSMutableArray* animations = [NSMutableArray arrayWithCapacity:3]; | |
448 | |
449 NSWindow* sheet = [self window]; | |
450 NSRect sheetFrame = [sheet frame]; | |
451 NSRect infoBarFrame = [infoBar_ frame]; | |
452 NSRect splitViewFrame = [splitView_ frame]; | |
453 | |
454 // Calculate the end position of the info bar and set it to its start | |
455 // position. | |
456 infoBarFrame.origin.y = NSHeight(sheetFrame); | |
457 infoBarFrame.size.width = NSWidth(sheetFrame); | |
458 NSRect infoBarStartFrame = infoBarFrame; | |
459 infoBarStartFrame.origin.y += NSHeight(infoBarFrame); | |
460 infoBarStartFrame.size.height = 0.0; | |
461 [infoBar_ setFrame:infoBarStartFrame]; | |
462 [[[self window] contentView] addSubview:infoBar_]; | |
463 | |
464 // Calculate the new position of the sheet. | |
465 sheetFrame.origin.y -= NSHeight(infoBarFrame); | |
466 sheetFrame.size.height += NSHeight(infoBarFrame); | |
467 | |
468 // Slide the infobar in. | |
469 [animations addObject: | |
470 [NSDictionary dictionaryWithObjectsAndKeys: | |
471 infoBar_, NSViewAnimationTargetKey, | |
472 [NSValue valueWithRect:infoBarFrame], | |
473 NSViewAnimationEndFrameKey, | |
474 [NSValue valueWithRect:infoBarStartFrame], | |
475 NSViewAnimationStartFrameKey, | |
476 nil]]; | |
477 // Make sure the split view ends up in the right position. | |
478 [animations addObject: | |
479 [NSDictionary dictionaryWithObjectsAndKeys: | |
480 splitView_, NSViewAnimationTargetKey, | |
481 [NSValue valueWithRect:splitViewFrame], | |
482 NSViewAnimationEndFrameKey, | |
483 nil]]; | |
484 | |
485 // Grow the sheet. | |
486 [animations addObject: | |
487 [NSDictionary dictionaryWithObjectsAndKeys: | |
488 sheet, NSViewAnimationTargetKey, | |
489 [NSValue valueWithRect:sheetFrame], | |
490 NSViewAnimationEndFrameKey, | |
491 nil]]; | |
492 [animation_ setViewAnimations:animations]; | |
493 // The default duration is 0.5s, which actually feels slow in here, so speed | |
494 // it up a bit. | |
495 [animation_ gtm_setDuration:0.2 | |
496 eventMask:NSLeftMouseUpMask]; | |
497 [animation_ startAnimation]; | |
498 } | |
499 | |
500 @end | |
OLD | NEW |