Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / content_settings / collected_cookies_mac.mm
blobbd76c519f854565853fd3084fce295f0286686c4
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/tab_specific_content_settings.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
19 #import "chrome/browser/ui/cocoa/content_settings/cookie_details_view_controller.h"
20 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h"
21 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "grit/theme_resources.h"
28 #include "third_party/apple_sample_code/ImageAndTextCell.h"
29 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
30 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
31 #include "ui/base/l10n/l10n_util_mac.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_skia.h"
35 #include "ui/gfx/image/image_skia_util_mac.h"
37 namespace {
38 // Colors for the infobar.
39 const double kBannerGradientColorTop[3] =
40     {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0};
41 const double kBannerGradientColorBottom[3] =
42     {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0};
43 const double kBannerStrokeColor = 135.0 / 255.0;
45 enum TabViewItemIndices {
46   kAllowedCookiesTabIndex = 0,
47   kBlockedCookiesTabIndex
50 } // namespace
52 #pragma mark Constrained window delegate
54 CollectedCookiesMac::CollectedCookiesMac(content::WebContents* web_contents) {
55   TabSpecificContentSettings* content_settings =
56       TabSpecificContentSettings::FromWebContents(web_contents);
57   registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
58                  content::Source<TabSpecificContentSettings>(content_settings));
60   sheet_controller_.reset([[CollectedCookiesWindowController alloc]
61       initWithWebContents:web_contents
62       collectedCookiesMac:this]);
64   base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
65       [[CustomConstrainedWindowSheet alloc]
66           initWithCustomWindow:[sheet_controller_ window]]);
67   window_.reset(new ConstrainedWindowMac(
68       this, web_contents, sheet));
71 CollectedCookiesMac::~CollectedCookiesMac() {
74 void CollectedCookiesMac::Observe(int type,
75                                   const content::NotificationSource& source,
76                                   const content::NotificationDetails& details) {
77   DCHECK(type == chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN);
78   window_->CloseWebContentsModalDialog();
81 void CollectedCookiesMac::PerformClose() {
82   window_->CloseWebContentsModalDialog();
85 void CollectedCookiesMac::OnConstrainedWindowClosed(
86     ConstrainedWindowMac* window) {
87   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
90 #pragma mark Window Controller
92 @interface CollectedCookiesWindowController (Private)
93 - (void)showInfoBarForDomain:(const base::string16&)domain
94                      setting:(ContentSetting)setting;
95 - (void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting;
96 - (void)animateInfoBar;
97 @end
99 @implementation CollectedCookiesWindowController
101 @synthesize allowedTreeController = allowedTreeController_;
102 @synthesize blockedTreeController = blockedTreeController_;
103 @synthesize allowedOutlineView = allowedOutlineView_;
104 @synthesize blockedOutlineView = blockedOutlineView_;
105 @synthesize infoBar = infoBar_;
106 @synthesize infoBarIcon = infoBarIcon_;
107 @synthesize infoBarText = infoBarText_;
108 @synthesize tabView = tabView_;
109 @synthesize blockedScrollView = blockedScrollView_;
110 @synthesize blockedCookiesText = blockedCookiesText_;
111 @synthesize cookieDetailsViewPlaceholder = cookieDetailsViewPlaceholder_;
113 @synthesize allowedCookiesButtonsEnabled =
114     allowedCookiesButtonsEnabled_;
115 @synthesize blockedCookiesButtonsEnabled =
116     blockedCookiesButtonsEnabled_;
117 @synthesize deleteCookiesButtonEnabled = deleteCookiesButtonEnabled_;
119 - (id)initWithWebContents:(content::WebContents*)webContents
120       collectedCookiesMac:(CollectedCookiesMac*)collectedCookiesMac {
121   DCHECK(webContents);
123   NSString* nibpath =
124       [base::mac::FrameworkBundle() pathForResource:@"CollectedCookies"
125                                              ofType:@"nib"];
126   if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
127     webContents_ = webContents;
128     collectedCookiesMac_ = collectedCookiesMac;
129     [self loadTreeModelFromWebContents];
131     animation_.reset([[NSViewAnimation alloc] init]);
132     [animation_ setAnimationBlockingMode:NSAnimationNonblocking];
133   }
134   return self;
137 - (void)awakeFromNib {
138   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
139   NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO).ToNSImage();
140   [infoBarIcon_ setImage:infoIcon];
142   // Initialize the banner gradient and stroke color.
143   NSColor* bannerStartingColor =
144       [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0]
145                                 green:kBannerGradientColorTop[1]
146                                  blue:kBannerGradientColorTop[2]
147                                 alpha:1.0];
148   NSColor* bannerEndingColor =
149       [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0]
150                                 green:kBannerGradientColorBottom[1]
151                                  blue:kBannerGradientColorBottom[2]
152                                 alpha:1.0];
153   base::scoped_nsobject<NSGradient> bannerGradient(
154       [[NSGradient alloc] initWithStartingColor:bannerStartingColor
155                                     endingColor:bannerEndingColor]);
156   [infoBar_ setGradient:bannerGradient];
158   NSColor* bannerStrokeColor =
159       [NSColor colorWithCalibratedWhite:kBannerStrokeColor
160                                   alpha:1.0];
161   [infoBar_ setStrokeColor:bannerStrokeColor];
163   // Change the label of the blocked cookies part if necessary.
164   Profile* profile =
165       Profile::FromBrowserContext(webContents_->GetBrowserContext());
166   if (profile->GetPrefs()->GetBoolean(prefs::kBlockThirdPartyCookies)) {
167     [blockedCookiesText_ setStringValue:l10n_util::GetNSString(
168         IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED)];
169     CGFloat textDeltaY = [GTMUILocalizerAndLayoutTweaker
170         sizeToFitFixedWidthTextField:blockedCookiesText_];
172     // Shrink the blocked cookies outline view.
173     NSRect frame = [blockedScrollView_ frame];
174     frame.size.height -= textDeltaY;
175     [blockedScrollView_ setFrame:frame];
177     // Move the label down so it actually fits.
178     frame = [blockedCookiesText_ frame];
179     frame.origin.y -= textDeltaY;
180     [blockedCookiesText_ setFrame:frame];
181   }
183   detailsViewController_.reset([[CookieDetailsViewController alloc] init]);
185   NSView* detailView = [detailsViewController_.get() view];
186   NSRect viewFrameRect = [cookieDetailsViewPlaceholder_ frame];
187   [[detailsViewController_.get() view] setFrame:viewFrameRect];
188   [[cookieDetailsViewPlaceholder_ superview]
189       replaceSubview:cookieDetailsViewPlaceholder_
190                 with:detailView];
192   [self tabView:tabView_ didSelectTabViewItem:[tabView_ selectedTabViewItem]];
195 - (void)windowWillClose:(NSNotification*)notif {
196   // If the user closes our parent tab while we're still open, this method will
197   // (eventually) be called in response to a WebContentsDestroyed() call from
198   // the WebContentsImpl to its observers.  But since the InfoBarService is also
199   // torn down in response to WebContentsDestroyed(), it may already be null.
200   // Since the tab is going away anyway, we can just omit showing an infobar,
201   // which prevents any attempt to access a null InfoBarService.
202   if (contentSettingsChanged_ && !webContents_->IsBeingDestroyed()) {
203     CollectedCookiesInfoBarDelegate::Create(
204         InfoBarService::FromWebContents(webContents_));
205   }
206   [allowedOutlineView_ setDelegate:nil];
207   [blockedOutlineView_ setDelegate:nil];
208   [animation_ stopAnimation];
211 - (IBAction)closeSheet:(id)sender {
212   collectedCookiesMac_->PerformClose();
215 - (void)addException:(ContentSetting)setting
216    forTreeController:(NSTreeController*)controller {
217   NSArray* nodes = [controller selectedNodes];
218   BOOL multipleDomainsChanged = NO;
219   base::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_HOST) {
225       continue;
226     }
227     Profile* profile =
228         Profile::FromBrowserContext(webContents_->GetBrowserContext());
229     CookieTreeHostNode* host_node =
230         static_cast<CookieTreeHostNode*>(cookie);
231     host_node->CreateContentException(
232         CookieSettings::Factory::GetForProfile(profile).get(), setting);
233     if (!lastDomain.empty())
234       multipleDomainsChanged = YES;
235     lastDomain = host_node->GetTitle();
236   }
237   if (multipleDomainsChanged)
238     [self showInfoBarForMultipleDomainsAndSetting:setting];
239   else
240     [self showInfoBarForDomain:lastDomain setting:setting];
241   contentSettingsChanged_ = YES;
244 - (IBAction)allowOrigin:(id)sender {
245   [self addException:CONTENT_SETTING_ALLOW
246       forTreeController:blockedTreeController_];
249 - (IBAction)allowForSessionFromOrigin:(id)sender {
250   [self addException:CONTENT_SETTING_SESSION_ONLY
251       forTreeController:blockedTreeController_];
254 - (IBAction)blockOrigin:(id)sender {
255   [self addException:CONTENT_SETTING_BLOCK
256       forTreeController:allowedTreeController_];
259 - (IBAction)deleteSelected:(id)sender {
260   NSArray* nodes = [[self class] normalizeNodeSelection:
261       [allowedTreeController_ selectedNodes]];
262   for (NSTreeNode* cocoaTreeNode in nodes) {
263     CookieTreeNode* cookieNode = static_cast<CookieTreeNode*>(
264         [[cocoaTreeNode representedObject] treeNode]);
265     allowedTreeModel_->DeleteCookieNode(cookieNode);
266   }
269 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel {
270   return allowedControllerBridge_->cocoa_model();
273 - (CookiesTreeModel*)allowedTreeModel {
274   return allowedTreeModel_.get();
277 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel {
278   return blockedControllerBridge_->cocoa_model();
281 - (CookiesTreeModel*)blockedTreeModel {
282   return blockedTreeModel_.get();
285 - (void)outlineView:(NSOutlineView*)outlineView
286     willDisplayCell:(id)cell
287      forTableColumn:(NSTableColumn*)tableColumn
288                item:(id)item {
289   CocoaCookieTreeNode* node = [item representedObject];
290   int index;
291   if (outlineView == allowedOutlineView_)
292     index = allowedTreeModel_->GetIconIndex([node treeNode]);
293   else
294     index = blockedTreeModel_->GetIconIndex([node treeNode]);
295   NSImage* icon = nil;
296   if (index >= 0)
297     icon = [icons_ objectAtIndex:index];
298   else
299     icon = [icons_ lastObject];
300   DCHECK([cell isKindOfClass:[ImageAndTextCell class]]);
301   [static_cast<ImageAndTextCell*>(cell) setImage:icon];
304 - (void)outlineViewSelectionDidChange:(NSNotification*)notif {
305   BOOL isAllowedOutlineView;
306   if ([notif object] == allowedOutlineView_) {
307     isAllowedOutlineView = YES;
308   } else if ([notif object] == blockedOutlineView_) {
309     isAllowedOutlineView = NO;
310   } else {
311     NOTREACHED();
312     return;
313   }
315   NSTreeController* controller =
316       isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_;
317   NSArray* nodes = [controller selectedNodes];
319   if (isAllowedOutlineView)
320     [self setDeleteCookiesButtonEnabled:([nodes count] > 0)];
321   else
322     [self setDeleteCookiesButtonEnabled:NO];
324   for (NSTreeNode* treeNode in nodes) {
325     CocoaCookieTreeNode* node = [treeNode representedObject];
326     CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
327     if (cookie->GetDetailedInfo().node_type !=
328         CookieTreeNode::DetailedInfo::TYPE_HOST) {
329       continue;
330     }
331    CookieTreeHostNode* host_node =
332        static_cast<CookieTreeHostNode*>(cookie);
333    if (host_node->CanCreateContentException()) {
334       if (isAllowedOutlineView) {
335         [self setAllowedCookiesButtonsEnabled:YES];
336       } else {
337         [self setBlockedCookiesButtonsEnabled:YES];
338       }
339       return;
340     }
341   }
342   if (isAllowedOutlineView) {
343     [self setAllowedCookiesButtonsEnabled:NO];
344   } else {
345     [self setBlockedCookiesButtonsEnabled:NO];
346   }
349 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds
350 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|.
351 - (void)loadTreeModelFromWebContents {
352   TabSpecificContentSettings* content_settings =
353       TabSpecificContentSettings::FromWebContents(webContents_);
355   allowedTreeModel_ = content_settings->CreateAllowedCookiesTreeModel();
356   blockedTreeModel_ = content_settings->CreateBlockedCookiesTreeModel();
358   // Convert the model's icons from Skia to Cocoa.
359   std::vector<gfx::ImageSkia> skiaIcons;
360   allowedTreeModel_->GetIcons(&skiaIcons);
361   icons_.reset([[NSMutableArray alloc] init]);
362   for (std::vector<gfx::ImageSkia>::iterator it = skiaIcons.begin();
363        it != skiaIcons.end(); ++it) {
364     [icons_ addObject:gfx::NSImageFromImageSkia(*it)];
365   }
367   // Default icon will be the last item in the array.
368   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
369   // TODO(rsesek): Rename this resource now that it's in multiple places.
370   [icons_ addObject:
371       rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).ToNSImage()];
373   // Create the Cocoa model.
374   allowedControllerBridge_.reset(
375       new CookiesTreeControllerBridge(allowedTreeModel_.get()));
376   blockedControllerBridge_.reset(
377       new CookiesTreeControllerBridge(blockedTreeModel_.get()));
380 - (void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting {
381   NSString* label;
382   switch (setting) {
383     case CONTENT_SETTING_BLOCK:
384       label = l10n_util::GetNSString(
385           IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED);
386       break;
388     case CONTENT_SETTING_ALLOW:
389       label = l10n_util::GetNSString(
390           IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED);
391       break;
393     case CONTENT_SETTING_SESSION_ONLY:
394       label = l10n_util::GetNSString(
395           IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED);
396       break;
398     default:
399       NOTREACHED();
400       label = [[[NSString alloc] init] autorelease];
401   }
402   [infoBarText_ setStringValue:label];
403   [self animateInfoBar];
406 + (NSArray*)normalizeNodeSelection:(NSArray*)selection {
407   NSMutableArray* normalized = [NSMutableArray arrayWithArray:selection];
408   for (NSTreeNode* node in selection) {
409     NSTreeNode* parent = node;
410     while ((parent = [parent parentNode])) {
411       if ([normalized containsObject:parent]) {
412         [normalized removeObject:node];
413         break;
414       }
415     }
416   }
417   return normalized;
420 - (void)showInfoBarForDomain:(const base::string16&)domain
421                      setting:(ContentSetting)setting {
422   NSString* label;
423   switch (setting) {
424     case CONTENT_SETTING_BLOCK:
425       label = l10n_util::GetNSStringF(
426           IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED,
427           domain);
428       break;
430     case CONTENT_SETTING_ALLOW:
431       label = l10n_util::GetNSStringF(
432           IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED,
433           domain);
434       break;
436     case CONTENT_SETTING_SESSION_ONLY:
437       label = l10n_util::GetNSStringF(
438           IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED,
439           domain);
440       break;
442     default:
443       NOTREACHED();
444       label = [[[NSString alloc] init] autorelease];
445   }
446   [infoBarText_ setStringValue:label];
447   [self animateInfoBar];
450 - (void)animateInfoBar {
451   if (infoBarVisible_)
452     return;
454   infoBarVisible_ = YES;
456   NSWindow* sheet = [self window];
457   NSRect sheetFrame = [sheet frame];
458   NSRect infoBarFrame = [infoBar_ frame];
459   NSRect tabViewFrame = [tabView_ frame];
461   // Calculate the end position of the info bar and set it to its start
462   // position.
463   infoBarFrame.origin.y = NSHeight(sheetFrame);
464   infoBarFrame.size.width = NSWidth(sheetFrame);
465   [infoBar_ setFrame:infoBarFrame];
466   [[[self window] contentView] addSubview:infoBar_];
468   // Calculate the new position of the sheet.
469   sheetFrame.origin.y -= NSHeight(infoBarFrame);
470   sheetFrame.size.height += NSHeight(infoBarFrame);
472   NSArray* animations = @[
473     // Slide the infobar in.
474     @{
475       NSViewAnimationTargetKey : infoBar_,
476       NSViewAnimationEndFrameKey : [NSValue valueWithRect:infoBarFrame]
477     },
478     // Make sure the tab view ends up in the right position.
479     @{
480       NSViewAnimationTargetKey : tabView_,
481       NSViewAnimationEndFrameKey : [NSValue valueWithRect:tabViewFrame]
482     },
483     // Grow the sheet.
484     @{
485       NSViewAnimationTargetKey : sheet,
486       NSViewAnimationEndFrameKey : [NSValue valueWithRect:sheetFrame]
487     }
488   ];
490   [animation_ setViewAnimations:animations];
491   // The default duration is 0.5s, which actually feels slow in here, so speed
492   // it up a bit.
493   [animation_ gtm_setDuration:0.2 eventMask:NSLeftMouseUpMask];
494   [animation_ startAnimation];
497 - (void)tabView:(NSTabView*)tabView
498     didSelectTabViewItem:(NSTabViewItem*)tabViewItem {
499   NSTreeController* treeController = nil;
500   switch ([tabView indexOfTabViewItem:tabViewItem]) {
501     case kAllowedCookiesTabIndex:
502       treeController = allowedTreeController_;
503       break;
504     case kBlockedCookiesTabIndex:
505       treeController = blockedTreeController_;
506       break;
507     default:
508       NOTREACHED();
509       return;
510   }
511   [detailsViewController_ configureBindingsForTreeController:treeController];
514 @end