1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/content_settings/collected_cookies_mac.h"
9 #include "base/mac/bundle_locations.h"
10 #import "base/mac/mac_util.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
15 #include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
16 #include "chrome/browser/browsing_data/browsing_data_database_helper.h"
17 #include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
18 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
19 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
20 #include "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/content_settings/cookie_settings.h"
23 #include "chrome/browser/content_settings/local_shared_objects_container.h"
24 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
25 #include "chrome/browser/infobars/infobar_service.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/ui/browser_dialogs.h"
28 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
29 #import "chrome/browser/ui/cocoa/content_settings/cookie_details_view_controller.h"
30 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h"
31 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
32 #include "chrome/common/pref_names.h"
33 #include "content/public/browser/notification_details.h"
34 #include "content/public/browser/notification_source.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_view.h"
37 #include "grit/generated_resources.h"
38 #include "grit/theme_resources.h"
39 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
40 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
41 #include "third_party/apple_sample_code/ImageAndTextCell.h"
42 #include "ui/base/l10n/l10n_util_mac.h"
43 #include "ui/base/resource/resource_bundle.h"
44 #include "ui/gfx/image/image.h"
45 #include "ui/gfx/image/image_skia.h"
46 #include "ui/gfx/image/image_skia_util_mac.h"
49 // Colors for the infobar.
50 const double kBannerGradientColorTop[3] =
51 {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0};
52 const double kBannerGradientColorBottom[3] =
53 {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0};
54 const double kBannerStrokeColor = 135.0 / 255.0;
56 enum TabViewItemIndices {
57 kAllowedCookiesTabIndex = 0,
58 kBlockedCookiesTabIndex
65 // Declared in browser_dialogs.h so others don't have to depend on our header.
66 void ShowCollectedCookiesDialog(content::WebContents* web_contents) {
67 // Deletes itself on close.
68 new CollectedCookiesMac(web_contents);
73 #pragma mark Constrained window delegate
75 CollectedCookiesMac::CollectedCookiesMac(content::WebContents* web_contents) {
76 TabSpecificContentSettings* content_settings =
77 TabSpecificContentSettings::FromWebContents(web_contents);
78 registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
79 content::Source<TabSpecificContentSettings>(content_settings));
81 sheet_controller_.reset([[CollectedCookiesWindowController alloc]
82 initWithWebContents:web_contents
83 collectedCookiesMac:this]);
85 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
86 [[CustomConstrainedWindowSheet alloc]
87 initWithCustomWindow:[sheet_controller_ window]]);
88 window_.reset(new ConstrainedWindowMac(
89 this, web_contents, sheet));
92 CollectedCookiesMac::~CollectedCookiesMac() {
95 void CollectedCookiesMac::Observe(int type,
96 const content::NotificationSource& source,
97 const content::NotificationDetails& details) {
98 DCHECK(type == chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN);
99 window_->CloseWebContentsModalDialog();
102 void CollectedCookiesMac::PerformClose() {
103 window_->CloseWebContentsModalDialog();
106 void CollectedCookiesMac::OnConstrainedWindowClosed(
107 ConstrainedWindowMac* window) {
108 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
111 #pragma mark Window Controller
113 @interface CollectedCookiesWindowController(Private)
114 -(void)showInfoBarForDomain:(const string16&)domain
115 setting:(ContentSetting)setting;
116 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting;
117 -(void)animateInfoBar;
120 @implementation CollectedCookiesWindowController
122 @synthesize allowedCookiesButtonsEnabled =
123 allowedCookiesButtonsEnabled_;
124 @synthesize blockedCookiesButtonsEnabled =
125 blockedCookiesButtonsEnabled_;
127 @synthesize allowedTreeController = allowedTreeController_;
128 @synthesize blockedTreeController = blockedTreeController_;
129 @synthesize allowedOutlineView = allowedOutlineView_;
130 @synthesize blockedOutlineView = blockedOutlineView_;
131 @synthesize infoBar = infoBar_;
132 @synthesize infoBarIcon = infoBarIcon_;
133 @synthesize infoBarText = infoBarText_;
134 @synthesize tabView = tabView_;
135 @synthesize blockedScrollView = blockedScrollView_;
136 @synthesize blockedCookiesText = blockedCookiesText_;
137 @synthesize cookieDetailsViewPlaceholder = cookieDetailsViewPlaceholder_;
139 - (id)initWithWebContents:(content::WebContents*)webContents
140 collectedCookiesMac:(CollectedCookiesMac*)collectedCookiesMac {
144 [base::mac::FrameworkBundle() pathForResource:@"CollectedCookies"
146 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
147 webContents_ = webContents;
148 collectedCookiesMac_ = collectedCookiesMac;
149 [self loadTreeModelFromWebContents];
151 animation_.reset([[NSViewAnimation alloc] init]);
152 [animation_ setAnimationBlockingMode:NSAnimationNonblocking];
157 - (void)awakeFromNib {
158 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
159 NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO).ToNSImage();
160 [infoBarIcon_ setImage:infoIcon];
162 // Initialize the banner gradient and stroke color.
163 NSColor* bannerStartingColor =
164 [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0]
165 green:kBannerGradientColorTop[1]
166 blue:kBannerGradientColorTop[2]
168 NSColor* bannerEndingColor =
169 [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0]
170 green:kBannerGradientColorBottom[1]
171 blue:kBannerGradientColorBottom[2]
173 base::scoped_nsobject<NSGradient> bannerGradient(
174 [[NSGradient alloc] initWithStartingColor:bannerStartingColor
175 endingColor:bannerEndingColor]);
176 [infoBar_ setGradient:bannerGradient];
178 NSColor* bannerStrokeColor =
179 [NSColor colorWithCalibratedWhite:kBannerStrokeColor
181 [infoBar_ setStrokeColor:bannerStrokeColor];
183 // Change the label of the blocked cookies part if necessary.
185 Profile::FromBrowserContext(webContents_->GetBrowserContext());
186 if (profile->GetPrefs()->GetBoolean(prefs::kBlockThirdPartyCookies)) {
187 [blockedCookiesText_ setStringValue:l10n_util::GetNSString(
188 IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED)];
189 CGFloat textDeltaY = [GTMUILocalizerAndLayoutTweaker
190 sizeToFitFixedWidthTextField:blockedCookiesText_];
192 // Shrink the blocked cookies outline view.
193 NSRect frame = [blockedScrollView_ frame];
194 frame.size.height -= textDeltaY;
195 [blockedScrollView_ setFrame:frame];
197 // Move the label down so it actually fits.
198 frame = [blockedCookiesText_ frame];
199 frame.origin.y -= textDeltaY;
200 [blockedCookiesText_ setFrame:frame];
203 detailsViewController_.reset([[CookieDetailsViewController alloc] init]);
205 NSView* detailView = [detailsViewController_.get() view];
206 NSRect viewFrameRect = [cookieDetailsViewPlaceholder_ frame];
207 [[detailsViewController_.get() view] setFrame:viewFrameRect];
208 [[cookieDetailsViewPlaceholder_ superview]
209 replaceSubview:cookieDetailsViewPlaceholder_
212 [self tabView:tabView_ didSelectTabViewItem:[tabView_ selectedTabViewItem]];
215 - (void)windowWillClose:(NSNotification*)notif {
216 if (contentSettingsChanged_) {
217 CollectedCookiesInfoBarDelegate::Create(
218 InfoBarService::FromWebContents(webContents_));
220 [allowedOutlineView_ setDelegate:nil];
221 [blockedOutlineView_ setDelegate:nil];
222 [animation_ stopAnimation];
225 - (IBAction)closeSheet:(id)sender {
226 collectedCookiesMac_->PerformClose();
229 - (void)addException:(ContentSetting)setting
230 forTreeController:(NSTreeController*)controller {
231 NSArray* nodes = [controller selectedNodes];
232 BOOL multipleDomainsChanged = NO;
234 for (NSTreeNode* treeNode in nodes) {
235 CocoaCookieTreeNode* node = [treeNode representedObject];
236 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
237 if (cookie->GetDetailedInfo().node_type !=
238 CookieTreeNode::DetailedInfo::TYPE_HOST) {
242 Profile::FromBrowserContext(webContents_->GetBrowserContext());
243 CookieTreeHostNode* host_node =
244 static_cast<CookieTreeHostNode*>(cookie);
245 host_node->CreateContentException(
246 CookieSettings::Factory::GetForProfile(profile).get(), setting);
247 if (!lastDomain.empty())
248 multipleDomainsChanged = YES;
249 lastDomain = host_node->GetTitle();
251 if (multipleDomainsChanged)
252 [self showInfoBarForMultipleDomainsAndSetting:setting];
254 [self showInfoBarForDomain:lastDomain setting:setting];
255 contentSettingsChanged_ = YES;
258 - (IBAction)allowOrigin:(id)sender {
259 [self addException:CONTENT_SETTING_ALLOW
260 forTreeController:blockedTreeController_];
263 - (IBAction)allowForSessionFromOrigin:(id)sender {
264 [self addException:CONTENT_SETTING_SESSION_ONLY
265 forTreeController:blockedTreeController_];
268 - (IBAction)blockOrigin:(id)sender {
269 [self addException:CONTENT_SETTING_BLOCK
270 forTreeController:allowedTreeController_];
273 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel {
274 return cocoaAllowedTreeModel_.get();
276 - (void)setCocoaAllowedTreeModel:(CocoaCookieTreeNode*)model {
277 cocoaAllowedTreeModel_.reset([model retain]);
280 - (CookiesTreeModel*)allowedTreeModel {
281 return allowedTreeModel_.get();
284 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel {
285 return cocoaBlockedTreeModel_.get();
287 - (void)setCocoaBlockedTreeModel:(CocoaCookieTreeNode*)model {
288 cocoaBlockedTreeModel_.reset([model retain]);
291 - (CookiesTreeModel*)blockedTreeModel {
292 return blockedTreeModel_.get();
295 - (void)outlineView:(NSOutlineView*)outlineView
296 willDisplayCell:(id)cell
297 forTableColumn:(NSTableColumn*)tableColumn
299 CocoaCookieTreeNode* node = [item representedObject];
301 if (outlineView == allowedOutlineView_)
302 index = allowedTreeModel_->GetIconIndex([node treeNode]);
304 index = blockedTreeModel_->GetIconIndex([node treeNode]);
307 icon = [icons_ objectAtIndex:index];
309 icon = [icons_ lastObject];
310 DCHECK([cell isKindOfClass:[ImageAndTextCell class]]);
311 [static_cast<ImageAndTextCell*>(cell) setImage:icon];
314 - (void)outlineViewSelectionDidChange:(NSNotification*)notif {
315 BOOL isAllowedOutlineView;
316 if ([notif object] == allowedOutlineView_) {
317 isAllowedOutlineView = YES;
318 } else if ([notif object] == blockedOutlineView_) {
319 isAllowedOutlineView = NO;
324 NSTreeController* controller =
325 isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_;
327 NSArray* nodes = [controller selectedNodes];
328 for (NSTreeNode* treeNode in nodes) {
329 CocoaCookieTreeNode* node = [treeNode representedObject];
330 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
331 if (cookie->GetDetailedInfo().node_type !=
332 CookieTreeNode::DetailedInfo::TYPE_HOST) {
335 CookieTreeHostNode* host_node =
336 static_cast<CookieTreeHostNode*>(cookie);
337 if (host_node->CanCreateContentException()) {
338 if (isAllowedOutlineView) {
339 [self setAllowedCookiesButtonsEnabled:YES];
341 [self setBlockedCookiesButtonsEnabled:YES];
346 if (isAllowedOutlineView) {
347 [self setAllowedCookiesButtonsEnabled:NO];
349 [self setBlockedCookiesButtonsEnabled:NO];
353 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds
354 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|.
355 - (void)loadTreeModelFromWebContents {
356 TabSpecificContentSettings* content_settings =
357 TabSpecificContentSettings::FromWebContents(webContents_);
359 const LocalSharedObjectsContainer& allowed_data =
360 content_settings->allowed_local_shared_objects();
361 allowedTreeModel_ = allowed_data.CreateCookiesTreeModel();
363 const LocalSharedObjectsContainer& blocked_data =
364 content_settings->blocked_local_shared_objects();
365 blockedTreeModel_ = blocked_data.CreateCookiesTreeModel();
367 // Convert the model's icons from Skia to Cocoa.
368 std::vector<gfx::ImageSkia> skiaIcons;
369 allowedTreeModel_->GetIcons(&skiaIcons);
370 icons_.reset([[NSMutableArray alloc] init]);
371 for (std::vector<gfx::ImageSkia>::iterator it = skiaIcons.begin();
372 it != skiaIcons.end(); ++it) {
373 [icons_ addObject:gfx::NSImageFromImageSkia(*it)];
376 // Default icon will be the last item in the array.
377 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
378 // TODO(rsesek): Rename this resource now that it's in multiple places.
380 rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).ToNSImage()];
382 // Create the Cocoa model.
383 CookieTreeNode* root =
384 static_cast<CookieTreeNode*>(allowedTreeModel_->GetRoot());
385 base::scoped_nsobject<CocoaCookieTreeNode> model(
386 [[CocoaCookieTreeNode alloc] initWithNode:root]);
387 [self setCocoaAllowedTreeModel:model.get()]; // Takes ownership.
388 root = static_cast<CookieTreeNode*>(blockedTreeModel_->GetRoot());
390 [[CocoaCookieTreeNode alloc] initWithNode:root]);
391 [self setCocoaBlockedTreeModel:model.get()]; // Takes ownership.
394 -(void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting {
397 case CONTENT_SETTING_BLOCK:
398 label = l10n_util::GetNSString(
399 IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED);
402 case CONTENT_SETTING_ALLOW:
403 label = l10n_util::GetNSString(
404 IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED);
407 case CONTENT_SETTING_SESSION_ONLY:
408 label = l10n_util::GetNSString(
409 IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED);
414 label = [[[NSString alloc] init] autorelease];
416 [infoBarText_ setStringValue:label];
417 [self animateInfoBar];
420 -(void)showInfoBarForDomain:(const string16&)domain
421 setting:(ContentSetting)setting {
424 case CONTENT_SETTING_BLOCK:
425 label = l10n_util::GetNSStringF(
426 IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED,
430 case CONTENT_SETTING_ALLOW:
431 label = l10n_util::GetNSStringF(
432 IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED,
436 case CONTENT_SETTING_SESSION_ONLY:
437 label = l10n_util::GetNSStringF(
438 IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED,
444 label = [[[NSString alloc] init] autorelease];
446 [infoBarText_ setStringValue:label];
447 [self animateInfoBar];
450 -(void)animateInfoBar {
454 infoBarVisible_ = YES;
456 NSMutableArray* animations = [NSMutableArray arrayWithCapacity:3];
458 NSWindow* sheet = [self window];
459 NSRect sheetFrame = [sheet frame];
460 NSRect infoBarFrame = [infoBar_ frame];
461 NSRect tabViewFrame = [tabView_ frame];
463 // Calculate the end position of the info bar and set it to its start
465 infoBarFrame.origin.y = NSHeight(sheetFrame);
466 infoBarFrame.size.width = NSWidth(sheetFrame);
467 [infoBar_ setFrame:infoBarFrame];
468 [[[self window] contentView] addSubview:infoBar_];
470 // Calculate the new position of the sheet.
471 sheetFrame.origin.y -= NSHeight(infoBarFrame);
472 sheetFrame.size.height += NSHeight(infoBarFrame);
474 // Slide the infobar in.
475 [animations addObject:
476 [NSDictionary dictionaryWithObjectsAndKeys:
477 infoBar_, NSViewAnimationTargetKey,
478 [NSValue valueWithRect:infoBarFrame],
479 NSViewAnimationEndFrameKey,
481 // Make sure the tab view ends up in the right position.
482 [animations addObject:
483 [NSDictionary dictionaryWithObjectsAndKeys:
484 tabView_, NSViewAnimationTargetKey,
485 [NSValue valueWithRect:tabViewFrame],
486 NSViewAnimationEndFrameKey,
490 [animations addObject:
491 [NSDictionary dictionaryWithObjectsAndKeys:
492 sheet, NSViewAnimationTargetKey,
493 [NSValue valueWithRect:sheetFrame],
494 NSViewAnimationEndFrameKey,
496 [animation_ setViewAnimations:animations];
497 // The default duration is 0.5s, which actually feels slow in here, so speed
499 [animation_ gtm_setDuration:0.2
500 eventMask:NSLeftMouseUpMask];
501 [animation_ startAnimation];
504 - (void) tabView:(NSTabView*)tabView
505 didSelectTabViewItem:(NSTabViewItem*)tabViewItem {
506 NSTreeController* treeController = nil;
507 switch ([tabView indexOfTabViewItem:tabViewItem]) {
508 case kAllowedCookiesTabIndex:
509 treeController = allowedTreeController_;
511 case kBlockedCookiesTabIndex:
512 treeController = blockedTreeController_;
518 [detailsViewController_ configureBindingsForTreeController:treeController];