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