Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / content_settings / collected_cookies_mac.mm
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.
4
5 #import "chrome/browser/ui/cocoa/content_settings/collected_cookies_mac.h"
6
7 #include <vector>
8
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 "chrome/grit/generated_resources.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/web_contents.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"
38
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;
46
47 enum TabViewItemIndices {
48   kAllowedCookiesTabIndex = 0,
49   kBlockedCookiesTabIndex
50 };
51
52 } // namespace
53
54 namespace chrome {
55
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);
60 }
61
62 }  // namespace chrome
63
64 #pragma mark Constrained window delegate
65
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));
71
72   sheet_controller_.reset([[CollectedCookiesWindowController alloc]
73       initWithWebContents:web_contents
74       collectedCookiesMac:this]);
75
76   base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
77       [[CustomConstrainedWindowSheet alloc]
78           initWithCustomWindow:[sheet_controller_ window]]);
79   window_.reset(new ConstrainedWindowMac(
80       this, web_contents, sheet));
81 }
82
83 CollectedCookiesMac::~CollectedCookiesMac() {
84 }
85
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();
91 }
92
93 void CollectedCookiesMac::PerformClose() {
94   window_->CloseWebContentsModalDialog();
95 }
96
97 void CollectedCookiesMac::OnConstrainedWindowClosed(
98     ConstrainedWindowMac* window) {
99   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
100 }
101
102 #pragma mark Window Controller
103
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
110
111 @implementation CollectedCookiesWindowController
112
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_;
124
125 @synthesize allowedCookiesButtonsEnabled =
126     allowedCookiesButtonsEnabled_;
127 @synthesize blockedCookiesButtonsEnabled =
128     blockedCookiesButtonsEnabled_;
129 @synthesize deleteCookiesButtonEnabled = deleteCookiesButtonEnabled_;
130
131 - (id)initWithWebContents:(content::WebContents*)webContents
132       collectedCookiesMac:(CollectedCookiesMac*)collectedCookiesMac {
133   DCHECK(webContents);
134
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];
142
143     animation_.reset([[NSViewAnimation alloc] init]);
144     [animation_ setAnimationBlockingMode:NSAnimationNonblocking];
145   }
146   return self;
147 }
148
149 - (void)awakeFromNib {
150   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
151   NSImage* infoIcon = rb.GetNativeImageNamed(IDR_INFO).ToNSImage();
152   [infoBarIcon_ setImage:infoIcon];
153
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];
169
170   NSColor* bannerStrokeColor =
171       [NSColor colorWithCalibratedWhite:kBannerStrokeColor
172                                   alpha:1.0];
173   [infoBar_ setStrokeColor:bannerStrokeColor];
174
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_];
183
184     // Shrink the blocked cookies outline view.
185     NSRect frame = [blockedScrollView_ frame];
186     frame.size.height -= textDeltaY;
187     [blockedScrollView_ setFrame:frame];
188
189     // Move the label down so it actually fits.
190     frame = [blockedCookiesText_ frame];
191     frame.origin.y -= textDeltaY;
192     [blockedCookiesText_ setFrame:frame];
193   }
194
195   detailsViewController_.reset([[CookieDetailsViewController alloc] init]);
196
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];
203
204   [self tabView:tabView_ didSelectTabViewItem:[tabView_ selectedTabViewItem]];
205 }
206
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];
215 }
216
217 - (IBAction)closeSheet:(id)sender {
218   collectedCookiesMac_->PerformClose();
219 }
220
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;
248 }
249
250 - (IBAction)allowOrigin:(id)sender {
251   [self addException:CONTENT_SETTING_ALLOW
252       forTreeController:blockedTreeController_];
253 }
254
255 - (IBAction)allowForSessionFromOrigin:(id)sender {
256   [self addException:CONTENT_SETTING_SESSION_ONLY
257       forTreeController:blockedTreeController_];
258 }
259
260 - (IBAction)blockOrigin:(id)sender {
261   [self addException:CONTENT_SETTING_BLOCK
262       forTreeController:allowedTreeController_];
263 }
264
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   }
273 }
274
275 - (CocoaCookieTreeNode*)cocoaAllowedTreeModel {
276   return allowedControllerBridge_->cocoa_model();
277 }
278
279 - (CookiesTreeModel*)allowedTreeModel {
280   return allowedTreeModel_.get();
281 }
282
283 - (CocoaCookieTreeNode*)cocoaBlockedTreeModel {
284   return blockedControllerBridge_->cocoa_model();
285 }
286
287 - (CookiesTreeModel*)blockedTreeModel {
288   return blockedTreeModel_.get();
289 }
290
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];
308 }
309
310 - (void)outlineViewSelectionDidChange:(NSNotification*)notif {
311   BOOL isAllowedOutlineView;
312   if ([notif object] == allowedOutlineView_) {
313     isAllowedOutlineView = YES;
314   } else if ([notif object] == blockedOutlineView_) {
315     isAllowedOutlineView = NO;
316   } else {
317     NOTREACHED();
318     return;
319   }
320
321   NSTreeController* controller =
322       isAllowedOutlineView ? allowedTreeController_ : blockedTreeController_;
323   NSArray* nodes = [controller selectedNodes];
324
325   if (isAllowedOutlineView)
326     [self setDeleteCookiesButtonEnabled:([nodes count] > 0)];
327   else
328     [self setDeleteCookiesButtonEnabled:NO];
329
330   for (NSTreeNode* treeNode in nodes) {
331     CocoaCookieTreeNode* node = [treeNode representedObject];
332     CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
333     if (cookie->GetDetailedInfo().node_type !=
334         CookieTreeNode::DetailedInfo::TYPE_HOST) {
335       continue;
336     }
337    CookieTreeHostNode* host_node =
338        static_cast<CookieTreeHostNode*>(cookie);
339    if (host_node->CanCreateContentException()) {
340       if (isAllowedOutlineView) {
341         [self setAllowedCookiesButtonsEnabled:YES];
342       } else {
343         [self setBlockedCookiesButtonsEnabled:YES];
344       }
345       return;
346     }
347   }
348   if (isAllowedOutlineView) {
349     [self setAllowedCookiesButtonsEnabled:NO];
350   } else {
351     [self setBlockedCookiesButtonsEnabled:NO];
352   }
353 }
354
355 // Initializes the |allowedTreeModel_| and |blockedTreeModel_|, and builds
356 // the |cocoaAllowedTreeModel_| and |cocoaBlockedTreeModel_|.
357 - (void)loadTreeModelFromWebContents {
358   TabSpecificContentSettings* content_settings =
359       TabSpecificContentSettings::FromWebContents(webContents_);
360
361   const LocalSharedObjectsContainer& allowed_data =
362       content_settings->allowed_local_shared_objects();
363   allowedTreeModel_ = allowed_data.CreateCookiesTreeModel();
364
365   const LocalSharedObjectsContainer& blocked_data =
366       content_settings->blocked_local_shared_objects();
367   blockedTreeModel_ = blocked_data.CreateCookiesTreeModel();
368
369   // Convert the model's icons from Skia to Cocoa.
370   std::vector<gfx::ImageSkia> skiaIcons;
371   allowedTreeModel_->GetIcons(&skiaIcons);
372   icons_.reset([[NSMutableArray alloc] init]);
373   for (std::vector<gfx::ImageSkia>::iterator it = skiaIcons.begin();
374        it != skiaIcons.end(); ++it) {
375     [icons_ addObject:gfx::NSImageFromImageSkia(*it)];
376   }
377
378   // Default icon will be the last item in the array.
379   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
380   // TODO(rsesek): Rename this resource now that it's in multiple places.
381   [icons_ addObject:
382       rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).ToNSImage()];
383
384   // Create the Cocoa model.
385   allowedControllerBridge_.reset(
386       new CookiesTreeControllerBridge(allowedTreeModel_.get()));
387   blockedControllerBridge_.reset(
388       new CookiesTreeControllerBridge(blockedTreeModel_.get()));
389 }
390
391 - (void)showInfoBarForMultipleDomainsAndSetting:(ContentSetting)setting {
392   NSString* label;
393   switch (setting) {
394     case CONTENT_SETTING_BLOCK:
395       label = l10n_util::GetNSString(
396           IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED);
397       break;
398
399     case CONTENT_SETTING_ALLOW:
400       label = l10n_util::GetNSString(
401           IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED);
402       break;
403
404     case CONTENT_SETTING_SESSION_ONLY:
405       label = l10n_util::GetNSString(
406           IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED);
407       break;
408
409     default:
410       NOTREACHED();
411       label = [[[NSString alloc] init] autorelease];
412   }
413   [infoBarText_ setStringValue:label];
414   [self animateInfoBar];
415 }
416
417 + (NSArray*)normalizeNodeSelection:(NSArray*)selection {
418   NSMutableArray* normalized = [NSMutableArray arrayWithArray:selection];
419   for (NSTreeNode* node in selection) {
420     NSTreeNode* parent = node;
421     while ((parent = [parent parentNode])) {
422       if ([normalized containsObject:parent]) {
423         [normalized removeObject:node];
424         break;
425       }
426     }
427   }
428   return normalized;
429 }
430
431 - (void)showInfoBarForDomain:(const base::string16&)domain
432                      setting:(ContentSetting)setting {
433   NSString* label;
434   switch (setting) {
435     case CONTENT_SETTING_BLOCK:
436       label = l10n_util::GetNSStringF(
437           IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED,
438           domain);
439       break;
440
441     case CONTENT_SETTING_ALLOW:
442       label = l10n_util::GetNSStringF(
443           IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED,
444           domain);
445       break;
446
447     case CONTENT_SETTING_SESSION_ONLY:
448       label = l10n_util::GetNSStringF(
449           IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED,
450           domain);
451       break;
452
453     default:
454       NOTREACHED();
455       label = [[[NSString alloc] init] autorelease];
456   }
457   [infoBarText_ setStringValue:label];
458   [self animateInfoBar];
459 }
460
461 - (void)animateInfoBar {
462   if (infoBarVisible_)
463     return;
464
465   infoBarVisible_ = YES;
466
467   NSWindow* sheet = [self window];
468   NSRect sheetFrame = [sheet frame];
469   NSRect infoBarFrame = [infoBar_ frame];
470   NSRect tabViewFrame = [tabView_ frame];
471
472   // Calculate the end position of the info bar and set it to its start
473   // position.
474   infoBarFrame.origin.y = NSHeight(sheetFrame);
475   infoBarFrame.size.width = NSWidth(sheetFrame);
476   [infoBar_ setFrame:infoBarFrame];
477   [[[self window] contentView] addSubview:infoBar_];
478
479   // Calculate the new position of the sheet.
480   sheetFrame.origin.y -= NSHeight(infoBarFrame);
481   sheetFrame.size.height += NSHeight(infoBarFrame);
482
483   NSArray* animations = @[
484     // Slide the infobar in.
485     @{
486       NSViewAnimationTargetKey : infoBar_,
487       NSViewAnimationEndFrameKey : [NSValue valueWithRect:infoBarFrame]
488     },
489     // Make sure the tab view ends up in the right position.
490     @{
491       NSViewAnimationTargetKey : tabView_,
492       NSViewAnimationEndFrameKey : [NSValue valueWithRect:tabViewFrame]
493     },
494     // Grow the sheet.
495     @{
496       NSViewAnimationTargetKey : sheet,
497       NSViewAnimationEndFrameKey : [NSValue valueWithRect:sheetFrame]
498     }
499   ];
500
501   [animation_ setViewAnimations:animations];
502   // The default duration is 0.5s, which actually feels slow in here, so speed
503   // it up a bit.
504   [animation_ gtm_setDuration:0.2 eventMask:NSLeftMouseUpMask];
505   [animation_ startAnimation];
506 }
507
508 - (void)tabView:(NSTabView*)tabView
509     didSelectTabViewItem:(NSTabViewItem*)tabViewItem {
510   NSTreeController* treeController = nil;
511   switch ([tabView indexOfTabViewItem:tabViewItem]) {
512     case kAllowedCookiesTabIndex:
513       treeController = allowedTreeController_;
514       break;
515     case kBlockedCookiesTabIndex:
516       treeController = blockedTreeController_;
517       break;
518     default:
519       NOTREACHED();
520       return;
521   }
522   [detailsViewController_ configureBindingsForTreeController:treeController];
523 }
524
525 @end