Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / content_settings / collected_cookies_mac.mm
blob487804363ee4d2b08f62fa719390750e9b425824
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"
7 #include <vector>
9 #include "base/mac/bundle_locations.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/content_settings/cookie_settings.h"
15 #include "chrome/browser/content_settings/local_shared_objects_container.h"
16 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
17 #include "chrome/browser/infobars/infobar_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser_dialogs.h"
20 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
21 #import "chrome/browser/ui/cocoa/content_settings/cookie_details_view_controller.h"
22 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h"
23 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/web_contents.h"
28 #include "grit/generated_resources.h"
29 #include "grit/theme_resources.h"
30 #include "third_party/apple_sample_code/ImageAndTextCell.h"
31 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
32 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
33 #include "ui/base/l10n/l10n_util_mac.h"
34 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/gfx/image/image.h"
36 #include "ui/gfx/image/image_skia.h"
37 #include "ui/gfx/image/image_skia_util_mac.h"
39 namespace {
40 // Colors for the infobar.
41 const double kBannerGradientColorTop[3] =
42     {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0};
43 const double kBannerGradientColorBottom[3] =
44     {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0};
45 const double kBannerStrokeColor = 135.0 / 255.0;
47 enum TabViewItemIndices {
48   kAllowedCookiesTabIndex = 0,
49   kBlockedCookiesTabIndex
52 } // namespace
54 namespace chrome {
56 // Declared in browser_dialogs.h so others don't have to depend on our header.
57 void ShowCollectedCookiesDialog(content::WebContents* web_contents) {
58   // Deletes itself on close.
59   new CollectedCookiesMac(web_contents);
62 }  // namespace chrome
64 #pragma mark Constrained window delegate
66 CollectedCookiesMac::CollectedCookiesMac(content::WebContents* web_contents) {
67   TabSpecificContentSettings* content_settings =
68       TabSpecificContentSettings::FromWebContents(web_contents);
69   registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
70                  content::Source<TabSpecificContentSettings>(content_settings));
72   sheet_controller_.reset([[CollectedCookiesWindowController alloc]
73       initWithWebContents:web_contents
74       collectedCookiesMac:this]);
76   base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
77       [[CustomConstrainedWindowSheet alloc]
78           initWithCustomWindow:[sheet_controller_ window]]);
79   window_.reset(new ConstrainedWindowMac(
80       this, web_contents, sheet));
83 CollectedCookiesMac::~CollectedCookiesMac() {
86 void CollectedCookiesMac::Observe(int type,
87                                   const content::NotificationSource& source,
88                                   const content::NotificationDetails& details) {
89   DCHECK(type == chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN);
90   window_->CloseWebContentsModalDialog();
93 void CollectedCookiesMac::PerformClose() {
94   window_->CloseWebContentsModalDialog();
97 void CollectedCookiesMac::OnConstrainedWindowClosed(
98     ConstrainedWindowMac* window) {
99   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
102 #pragma mark Window Controller
104 @interface CollectedCookiesWindowController (Private)
105 - (void)showInfoBarForDomain:(const base::string16&)domain
106                      setting:(ContentSetting)setting;
107 - (void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting;
108 - (void)animateInfoBar;
109 @end
111 @implementation CollectedCookiesWindowController
113 @synthesize allowedTreeController = allowedTreeController_;
114 @synthesize blockedTreeController = blockedTreeController_;
115 @synthesize allowedOutlineView = allowedOutlineView_;
116 @synthesize blockedOutlineView = blockedOutlineView_;
117 @synthesize infoBar = infoBar_;
118 @synthesize infoBarIcon = infoBarIcon_;
119 @synthesize infoBarText = infoBarText_;
120 @synthesize tabView = tabView_;
121 @synthesize blockedScrollView = blockedScrollView_;
122 @synthesize blockedCookiesText = blockedCookiesText_;
123 @synthesize cookieDetailsViewPlaceholder = cookieDetailsViewPlaceholder_;
125 @synthesize allowedCookiesButtonsEnabled =
126     allowedCookiesButtonsEnabled_;
127 @synthesize blockedCookiesButtonsEnabled =
128     blockedCookiesButtonsEnabled_;
129 @synthesize deleteCookiesButtonEnabled = deleteCookiesButtonEnabled_;
131 - (id)initWithWebContents:(content::WebContents*)webContents
132       collectedCookiesMac:(CollectedCookiesMac*)collectedCookiesMac {
133   DCHECK(webContents);
135   NSString* nibpath =
136       [base::mac::FrameworkBundle() pathForResource:@"CollectedCookies"
137                                              ofType:@"nib"];
138   if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
139     webContents_ = webContents;
140     collectedCookiesMac_ = collectedCookiesMac;
141     [self loadTreeModelFromWebContents];
143     animation_.reset([[NSViewAnimation alloc] init]);
144     [animation_ setAnimationBlockingMode:NSAnimationNonblocking];
145   }
146   return self;
149 - (void)awakeFromNib {
150   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
151   NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO).ToNSImage();
152   [infoBarIcon_ setImage:infoIcon];
154   // Initialize the banner gradient and stroke color.
155   NSColor* bannerStartingColor =
156       [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0]
157                                 green:kBannerGradientColorTop[1]
158                                  blue:kBannerGradientColorTop[2]
159                                 alpha:1.0];
160   NSColor* bannerEndingColor =
161       [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0]
162                                 green:kBannerGradientColorBottom[1]
163                                  blue:kBannerGradientColorBottom[2]
164                                 alpha:1.0];
165   base::scoped_nsobject<NSGradient> bannerGradient(
166       [[NSGradient alloc] initWithStartingColor:bannerStartingColor
167                                     endingColor:bannerEndingColor]);
168   [infoBar_ setGradient:bannerGradient];
170   NSColor* bannerStrokeColor =
171       [NSColor colorWithCalibratedWhite:kBannerStrokeColor
172                                   alpha:1.0];
173   [infoBar_ setStrokeColor:bannerStrokeColor];
175   // Change the label of the blocked cookies part if necessary.
176   Profile* profile =
177       Profile::FromBrowserContext(webContents_->GetBrowserContext());
178   if (profile->GetPrefs()->GetBoolean(prefs::kBlockThirdPartyCookies)) {
179     [blockedCookiesText_ setStringValue:l10n_util::GetNSString(
180         IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED)];
181     CGFloat textDeltaY = [GTMUILocalizerAndLayoutTweaker
182         sizeToFitFixedWidthTextField:blockedCookiesText_];
184     // Shrink the blocked cookies outline view.
185     NSRect frame = [blockedScrollView_ frame];
186     frame.size.height -= textDeltaY;
187     [blockedScrollView_ setFrame:frame];
189     // Move the label down so it actually fits.
190     frame = [blockedCookiesText_ frame];
191     frame.origin.y -= textDeltaY;
192     [blockedCookiesText_ setFrame:frame];
193   }
195   detailsViewController_.reset([[CookieDetailsViewController alloc] init]);
197   NSView* detailView = [detailsViewController_.get() view];
198   NSRect viewFrameRect = [cookieDetailsViewPlaceholder_ frame];
199   [[detailsViewController_.get() view] setFrame:viewFrameRect];
200   [[cookieDetailsViewPlaceholder_ superview]
201       replaceSubview:cookieDetailsViewPlaceholder_
202                 with:detailView];
204   [self tabView:tabView_ didSelectTabViewItem:[tabView_ selectedTabViewItem]];
207 - (void)windowWillClose:(NSNotification*)notif {
208   if (contentSettingsChanged_) {
209     CollectedCookiesInfoBarDelegate::Create(
210         InfoBarService::FromWebContents(webContents_));
211   }
212   [allowedOutlineView_ setDelegate:nil];
213   [blockedOutlineView_ setDelegate:nil];
214   [animation_ stopAnimation];
217 - (IBAction)closeSheet:(id)sender {
218   collectedCookiesMac_->PerformClose();
221 - (void)addException:(ContentSetting)setting
222    forTreeController:(NSTreeController*)controller {
223   NSArray* nodes = [controller selectedNodes];
224   BOOL multipleDomainsChanged = NO;
225   base::string16 lastDomain;
226   for (NSTreeNode* treeNode in nodes) {
227     CocoaCookieTreeNode* node = [treeNode representedObject];
228     CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
229     if (cookie->GetDetailedInfo().node_type !=
230         CookieTreeNode::DetailedInfo::TYPE_HOST) {
231       continue;
232     }
233     Profile* profile =
234         Profile::FromBrowserContext(webContents_->GetBrowserContext());
235     CookieTreeHostNode* host_node =
236         static_cast<CookieTreeHostNode*>(cookie);
237     host_node->CreateContentException(
238         CookieSettings::Factory::GetForProfile(profile).get(), setting);
239     if (!lastDomain.empty())
240       multipleDomainsChanged = YES;
241     lastDomain = host_node->GetTitle();
242   }
243   if (multipleDomainsChanged)
244     [self showInfoBarForMultipleDomainsAndSetting:setting];
245   else
246     [self showInfoBarForDomain:lastDomain setting:setting];
247   contentSettingsChanged_ = YES;
250 - (IBAction)allowOrigin:(id)sender {
251   [self addException:CONTENT_SETTING_ALLOW
252       forTreeController:blockedTreeController_];
255 - (IBAction)allowForSessionFromOrigin:(id)sender {
256   [self addException:CONTENT_SETTING_SESSION_ONLY
257       forTreeController:blockedTreeController_];
260 - (IBAction)blockOrigin:(id)sender {
261   [self addException:CONTENT_SETTING_BLOCK
262       forTreeController:allowedTreeController_];
265 - (IBAction)deleteSelected:(id)sender {
266   NSArray* nodes = [[self class] normalizeNodeSelection:
267       [allowedTreeController_ selectedNodes]];
268   for (NSTreeNode* cocoaTreeNode in nodes) {
269     CookieTreeNode* cookieNode = static_cast<CookieTreeNode*>(
270         [[cocoaTreeNode representedObject] treeNode]);
271     allowedTreeModel_->DeleteCookieNode(cookieNode);
272   }
275 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel {
276   return allowedControllerBridge_->cocoa_model();
279 - (CookiesTreeModel*)allowedTreeModel {
280   return allowedTreeModel_.get();
283 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel {
284   return blockedControllerBridge_->cocoa_model();
287 - (CookiesTreeModel*)blockedTreeModel {
288   return blockedTreeModel_.get();
291 - (void)outlineView:(NSOutlineView*)outlineView
292     willDisplayCell:(id)cell
293      forTableColumn:(NSTableColumn*)tableColumn
294                item:(id)item {
295   CocoaCookieTreeNode* node = [item representedObject];
296   int index;
297   if (outlineView == allowedOutlineView_)
298     index = allowedTreeModel_->GetIconIndex([node treeNode]);
299   else
300     index = blockedTreeModel_->GetIconIndex([node treeNode]);
301   NSImage* icon = nil;
302   if (index >= 0)
303     icon = [icons_ objectAtIndex:index];
304   else
305     icon = [icons_ lastObject];
306   DCHECK([cell isKindOfClass:[ImageAndTextCell class]]);
307   [static_cast<ImageAndTextCell*>(cell) setImage:icon];
310 - (void)outlineViewSelectionDidChange:(NSNotification*)notif {
311   BOOL isAllowedOutlineView;
312   if ([notif object] == allowedOutlineView_) {
313     isAllowedOutlineView = YES;
314     [self setDeleteCookiesButtonEnabled:YES];
315   } else if ([notif object] == blockedOutlineView_) {
316     isAllowedOutlineView = NO;
317     [self setDeleteCookiesButtonEnabled:NO];
318   } else {
319     NOTREACHED();
320     return;
321   }
322   NSTreeController* controller =
323       isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_;
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_HOST) {
331       continue;
332     }
333    CookieTreeHostNode* host_node =
334        static_cast<CookieTreeHostNode*>(cookie);
335    if (host_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   }
351 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds
352 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|.
353 - (void)loadTreeModelFromWebContents {
354   TabSpecificContentSettings* content_settings =
355       TabSpecificContentSettings::FromWebContents(webContents_);
357   const LocalSharedObjectsContainer& allowed_data =
358       content_settings->allowed_local_shared_objects();
359   allowedTreeModel_ = allowed_data.CreateCookiesTreeModel();
361   const LocalSharedObjectsContainer& blocked_data =
362       content_settings->blocked_local_shared_objects();
363   blockedTreeModel_ = blocked_data.CreateCookiesTreeModel();
365   // Convert the model's icons from Skia to Cocoa.
366   std::vector<gfx::ImageSkia> skiaIcons;
367   allowedTreeModel_->GetIcons(&skiaIcons);
368   icons_.reset([[NSMutableArray alloc] init]);
369   for (std::vector<gfx::ImageSkia>::iterator it = skiaIcons.begin();
370        it != skiaIcons.end(); ++it) {
371     [icons_ addObject:gfx::NSImageFromImageSkia(*it)];
372   }
374   // Default icon will be the last item in the array.
375   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
376   // TODO(rsesek): Rename this resource now that it's in multiple places.
377   [icons_ addObject:
378       rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).ToNSImage()];
380   // Create the Cocoa model.
381   allowedControllerBridge_.reset(
382       new CookiesTreeControllerBridge(allowedTreeModel_.get()));
383   blockedControllerBridge_.reset(
384       new CookiesTreeControllerBridge(blockedTreeModel_.get()));
387 - (void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting {
388   NSString* label;
389   switch (setting) {
390     case CONTENT_SETTING_BLOCK:
391       label = l10n_util::GetNSString(
392           IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED);
393       break;
395     case CONTENT_SETTING_ALLOW:
396       label = l10n_util::GetNSString(
397           IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED);
398       break;
400     case CONTENT_SETTING_SESSION_ONLY:
401       label = l10n_util::GetNSString(
402           IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED);
403       break;
405     default:
406       NOTREACHED();
407       label = [[[NSString alloc] init] autorelease];
408   }
409   [infoBarText_ setStringValue:label];
410   [self animateInfoBar];
413 + (NSArray*)normalizeNodeSelection:(NSArray*)selection {
414   NSMutableArray* normalized = [NSMutableArray arrayWithArray:selection];
415   for (NSTreeNode* node in selection) {
416     NSTreeNode* parent = node;
417     while ((parent = [parent parentNode])) {
418       if ([normalized containsObject:parent]) {
419         [normalized removeObject:node];
420         break;
421       }
422     }
423   }
424   return normalized;
427 - (void)showInfoBarForDomain:(const base::string16&)domain
428                      setting:(ContentSetting)setting {
429   NSString* label;
430   switch (setting) {
431     case CONTENT_SETTING_BLOCK:
432       label = l10n_util::GetNSStringF(
433           IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED,
434           domain);
435       break;
437     case CONTENT_SETTING_ALLOW:
438       label = l10n_util::GetNSStringF(
439           IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED,
440           domain);
441       break;
443     case CONTENT_SETTING_SESSION_ONLY:
444       label = l10n_util::GetNSStringF(
445           IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED,
446           domain);
447       break;
449     default:
450       NOTREACHED();
451       label = [[[NSString alloc] init] autorelease];
452   }
453   [infoBarText_ setStringValue:label];
454   [self animateInfoBar];
457 - (void)animateInfoBar {
458   if (infoBarVisible_)
459     return;
461   infoBarVisible_ = YES;
463   NSWindow* sheet = [self window];
464   NSRect sheetFrame = [sheet frame];
465   NSRect infoBarFrame = [infoBar_ frame];
466   NSRect tabViewFrame = [tabView_ frame];
468   // Calculate the end position of the info bar and set it to its start
469   // position.
470   infoBarFrame.origin.y = NSHeight(sheetFrame);
471   infoBarFrame.size.width = NSWidth(sheetFrame);
472   [infoBar_ setFrame:infoBarFrame];
473   [[[self window] contentView] addSubview:infoBar_];
475   // Calculate the new position of the sheet.
476   sheetFrame.origin.y -= NSHeight(infoBarFrame);
477   sheetFrame.size.height += NSHeight(infoBarFrame);
479   NSArray* animations = @[
480     // Slide the infobar in.
481     @{
482       NSViewAnimationTargetKey : infoBar_,
483       NSViewAnimationEndFrameKey : [NSValue valueWithRect:infoBarFrame]
484     },
485     // Make sure the tab view ends up in the right position.
486     @{
487       NSViewAnimationTargetKey : tabView_,
488       NSViewAnimationEndFrameKey : [NSValue valueWithRect:tabViewFrame]
489     },
490     // Grow the sheet.
491     @{
492       NSViewAnimationTargetKey : sheet,
493       NSViewAnimationEndFrameKey : [NSValue valueWithRect:sheetFrame]
494     }
495   ];
497   [animation_ setViewAnimations:animations];
498   // The default duration is 0.5s, which actually feels slow in here, so speed
499   // it up a bit.
500   [animation_ gtm_setDuration:0.2 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_;
510       break;
511     case kBlockedCookiesTabIndex:
512       treeController = blockedTreeController_;
513       break;
514     default:
515       NOTREACHED();
516       return;
517   }
518   [detailsViewController_ configureBindingsForTreeController:treeController];
521 @end